diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 00000000..374370d6 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,2 @@ +# Ignore spotlessApply reformat +9d48150e57e46c8df16822acee58f6f36dcdc91a diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..fd2792b6 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,44 @@ +* text eol=lf + +*.[jJ][aA][rR] binary + +*.[pP][nN][gG] binary +*.[jJ][pP][gG] binary +*.[jJ][pP][eE][gG] binary +*.[gG][iI][fF] binary +*.[tT][iI][fF] binary +*.[tT][iI][fF][fF] binary +*.[iI][cC][oO] binary +*.[sS][vV][gG] text +*.[eE][pP][sS] binary +*.[xX][cC][fF] binary + +*.[kK][aA][rR] binary +*.[mM]4[aA] binary +*.[mM][iI][dD] binary +*.[mM][iI][dD][iI] binary +*.[mM][pP]3 binary +*.[oO][gG][gG] binary +*.[rR][aA] binary + +*.7[zZ] binary +*.[gG][zZ] binary +*.[tT][aA][rR] binary +*.[tT][gG][zZ] binary +*.[zZ][iI][pP] binary + +*.[tT][cC][nN] binary +*.[sS][oO] binary +*.[dD][lL][lL] binary +*.[dD][yY][lL][iI][bB] binary +*.[pP][sS][dD] binary +*.[tT][tT][fF] binary +*.[oO][tT][fF] binary + +*.[pP][aA][tT][cC][hH] -text + +*.[bB][aA][tT] text eol=crlf +*.[cC][mM][dD] text eol=crlf +*.[pP][sS]1 text eol=crlf + +*[aA][uU][tT][oO][gG][eE][nN][eE][rR][aA][tT][eE][dD]* binary diff --git a/.github/scripts/test-no-crash-reports.sh b/.github/scripts/test-no-crash-reports.sh deleted file mode 100644 index c67e342c..00000000 --- a/.github/scripts/test-no-crash-reports.sh +++ /dev/null @@ -1,9 +0,0 @@ -directory="run/crash-reports" -if [ -d $directory ]; then - echo "Crash reports detected:" - cat $directory/* - exit 1 -else - echo "No crash reports detected" - exit 0 -fi diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index a60a2b64..3ee2f686 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -1,5 +1,3 @@ -# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time -# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle name: Build and test @@ -11,35 +9,5 @@ on: jobs: build-and-test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - - name: Set up JDK 8 - uses: actions/setup-java@v2 - with: - java-version: '8' - distribution: 'adopt' - cache: gradle - - - name: Grant execute permission for gradlew - run: chmod +x gradlew - - - name: Setup the workspace - run: ./gradlew setupCIWorkspace - - - name: Build the mod - run: ./gradlew build - - - name: Run server for 1 minute - run: | - mkdir run - echo "eula=true" > run/eula.txt - timeout 60 ./gradlew runServer || true - - - name: Test no crashes happend - run: | - chmod +x .github/scripts/test-no-crash-reports.sh - .github/scripts/test-no-crash-reports.sh + uses: GTNewHorizons/GTNH-Actions-Workflows/.github/workflows/build-and-test.yml@master + secrets: inherit diff --git a/.github/workflows/optimize-images.yml b/.github/workflows/optimize-images.yml new file mode 100644 index 00000000..16d85ce1 --- /dev/null +++ b/.github/workflows/optimize-images.yml @@ -0,0 +1,17 @@ +name: Optimize images + +on: + pull_request: + branches: [ master, main, release/** ] + paths: + - "**/*.png" + + push: + branches: [ master, main, release/** ] + paths: + - "**/*.png" + +jobs: + optimize-images: + uses: GTNewHorizons/GTNH-Actions-Workflows/.github/workflows/optimize-images.yml@master + secrets: inherit diff --git a/.github/workflows/release-tags.yml b/.github/workflows/release-tags.yml index 25c354b2..e4c0be6b 100644 --- a/.github/workflows/release-tags.yml +++ b/.github/workflows/release-tags.yml @@ -1,45 +1,14 @@ -# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time -# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle name: Release tagged build on: push: - tags: - - '*' + tags: [ '*' ] -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - - name: Set release version - run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV - - - name: Set up JDK 8 - uses: actions/setup-java@v2 - with: - java-version: '8' - distribution: 'adopt' - cache: gradle - - - name: Grant execute permission for gradlew - run: chmod +x gradlew +permissions: + contents: write - - name: Setup the workspace - run: ./gradlew setupCIWorkspace - - - name: Build the mod - run: ./gradlew build - - - name: Release under current tag - uses: "marvinpinto/action-automatic-releases@latest" - with: - repo_token: "${{ secrets.GITHUB_TOKEN }}" - automatic_release_tag: "${{ env.RELEASE_VERSION }}" - prerelease: false - title: "${{ env.RELEASE_VERSION }}" - files: build/libs/*.jar +jobs: + release-tags: + uses: GTNewHorizons/GTNH-Actions-Workflows/.github/workflows/release-tags.yml@master + secrets: inherit diff --git a/.gitignore b/.gitignore index fe92f881..38d39a46 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ whitelist.json *.ipr *.iws src/main/resources/mixins.*.json -dependencies/voxelmap*.jar -/data/ *.bat +*.DS_Store +!gradlew.bat +layout.json diff --git a/BON2-2.5.0.CUSTOM-all.jar b/BON2-2.5.0.CUSTOM-all.jar deleted file mode 100644 index 0445b5ed..00000000 Binary files a/BON2-2.5.0.CUSTOM-all.jar and /dev/null differ diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 00000000..a6b5f68c --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,3 @@ +# Any Github changes require admin approval +/.github/** @GTNewHorizons/admin + diff --git a/README.md b/README.md index f012a011..0edbfde1 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ This mod is intended for player convenience, but may also be used as API, since it provides the location of all GT ore veins in a cache. VisualProspecting tracks all GT Ore Veins a player has found and visualizes them in JourneyMap and/or XaeroWorldMap (optional, if installed). It also visualizes tracked Thaumcraft aura nodes if TCNodeTracker if installed. VoxelMap will add waypoints for prospected ore veins and fluids. VisualProspecting tracks all ores that a player interacted with, by right or by left click. It also integrates prospecting data from GTs _Advanced Seismic Prospector_, although only books that are created after this mod was added will provide integration. -You may share your findings with other players by crafting a _Prospector's Log_. +You may share your findings with other players by crafting a _Prospector's Log_, or automatically with your GTNHLib team (see [Team Sharing](#team-sharing)). This mod is tailored to _GregTech: New Horizons 2_, but feel free to use it however you like. Even though this mod is build against the custom GT5U from GT:NH, it should still work fine with other GT5U versions. @@ -44,6 +44,17 @@ _Thaumcraft aura nodes in JourneyMap overlay. You may double-click an aura node _Thaumcraft aura nodes in XaeroWorldMap overlay_ +### Team Sharing + +If you belong to a [GTNHLib](https://github.com/GTNewHorizons/GTNHLib) team, all prospection findings (ore veins, underground fluids, vein-depletion toggles) are automatically shared with online team members, and replayed to teammates when they next log in. This replaces the standalone _SharedProspecting_ mod. + +Disable via `B:enableTeamSharing=false` in `config/visualprospecting.cfg`. + +Admin commands: + - `/vp_team_info [player] [detailed]`: inspect a team's prospection record. + - `/vp_team_clear `: wipe a team's server-side prospection record (op/console). + - `/vp_team_upload `: Push a player's local cache into their team's record (op/console). + ### Reset Progress You may use JourneyMap's Actions Menu to achieve this or type `/visualprospectingresetprogress` in chat. Beware, there are no backups! Please use at your own risk. @@ -54,13 +65,27 @@ Does VisualProspecting run with other maps? - I runs just fine, but it has no vi - [TheLastKumquat](https://github.com/kumquat-ir) integrated XaeroWorldMap and XaeroMiniMap - [glowredman](https://github.com/glowredman) integrated VoxelMap +### Developer overlays + +VisualProspecting comes with developer overlays for debugging chunk saving issues, to activate it change the following config setting in `config/visualprospecting.cfg` to true: + +``` +B:enableDeveloperOverlays=true +``` + +The dirty chunk overlay works only in singleplayer mode in JourneyMap, and displays which chunks are marked as "dirty" in the game engine. +This can be used to debug mods that don't set this flag properly and therefore lose data or duplicate items on world load/unload. + +![Dirty chunks in JourneyMap developer overlay](https://i.ibb.co/XX1hqS5/2022-03-30-18-06-56.png) \ +_Dirty chunks in JourneyMap developer overlay_ + ### Dependencies #### Required Mods: - Minecraft Forge - Injected class: [_ItemEditableBook_](https://github.com/SinTh0r4s/VisualProspecting/blob/master/src/main/java/com/sinthoras/visualprospecting/mixins/minecraft/ItemEditableBookMixin.java) - [GregTech5-Unofficial](https://github.com/GTNewHorizons/GT5-Unofficial) - - Injected classes: [_GT_Block_Ores_Abstract_](https://github.com/SinTh0r4s/VisualProspecting/blob/master/src/main/java/com/sinthoras/visualprospecting/mixins/gregtech/GT_Block_Ores_AbstractMixin.java), [_GT_MetaTileEntity_AdvSeismicProspector_](https://github.com/SinTh0r4s/VisualProspecting/blob/master/src/main/java/com/sinthoras/visualprospecting/mixins/gregtech/GT_MetaTileEntity_AdvSeismicProspectorMixin.java), [_GT_MetaTileEntity_Scanner_](https://github.com/SinTh0r4s/VisualProspecting/blob/master/src/main/java/com/sinthoras/visualprospecting/mixins/gregtech/GT_MetaTileEntity_ScannerMixin.java), [_GT_Worldgenerator.WorldGenContainer_](https://github.com/SinTh0r4s/VisualProspecting/blob/master/src/main/java/com/sinthoras/visualprospecting/mixins/gregtech/WorldGenContainerMixin.java) + - Injected classes: [_GT_MetaTileEntity_AdvSeismicProspector_](https://github.com/SinTh0r4s/VisualProspecting/blob/master/src/main/java/com/sinthoras/visualprospecting/mixins/gregtech/MTEAdvSeismicProspectorMixin.java), [_GT_MetaTileEntity_Scanner_](https://github.com/SinTh0r4s/VisualProspecting/blob/master/src/main/java/com/sinthoras/visualprospecting/mixins/gregtech/MTEScannerMixin.java) - [SpongeMixins](https://github.com/GTNewHorizons/SpongeMixins) - [Enklumne](https://github.com/Hugobros3/Enklume) _by Hugobros3_ - Automatically shipped. No manual handling is required. @@ -161,120 +186,5 @@ if(world.isRemote == false) { } ``` -#### JourneyMap Custom Layer - -VisualProspecting provides a light-weight API for custom and interactive layers. This API will keep all maps as optional mod at runtime and not crash you game if it is missing. You may instantiate [`ButtonManager`](https://github.com/SinTh0r4s/VisualProspecting/blob/master/src/main/java/com/sinthoras/visualprospecting/gui/model/buttons/ButtonManager.java) to create your own logical button. Follow it up with an instance of [`LayerButton`](https://github.com/SinTh0r4s/VisualProspecting/blob/master/src/main/java/com/sinthoras/visualprospecting/gui/journeymap/buttons/LayerButton.java) and register both in the [`VisualProspecting_API`](https://github.com/SinTh0r4s/VisualProspecting/blob/master/src/main/java/com/sinthoras/visualprospecting/VisualProspecting_API.java): - -``` -ButtonManager buttonManager = new ButtonManager("translation.key", "iconName"); -LayerButton layerButton = new LayerButton(buttonManager); - -VisualProspecting_API.LogicalClient.registerCustomButtonManager(buttonManager); -VisualProspecting_API.LogicalClient.registerJourneyMapButton(layerButton); -``` - -If you start the game now, you will see a new button in the menu! - -First, you will implement [`ILocationProvider`](https://github.com/SinTh0r4s/VisualProspecting/blob/master/src/main/java/com/sinthoras/visualprospecting/gui/model/locations/ILocationProvider.java). This class is a container and will provide all information required to display your item on screen. It won't do any rendering. - -``` -class MyLocation implements ILocationProvider { - - public int getDimensionId() { - return 0; // overworld - } - - public int getBlockX() { - return 0; - } - - public int getBlockY() { - return 65; - } - - public int getBlockZ() { - return 0; - } - - public String getLabel() { - return "Hello Minecraft"; - } -} -``` - -Next up, you'll extend [`LayerManager`](https://github.com/SinTh0r4s/VisualProspecting/blob/master/src/main/java/com/sinthoras/visualprospecting/gui/model/layers/LayerManager.java) and implement the abstract function to generate cached List of your [`ILocationProvider`](https://github.com/SinTh0r4s/VisualProspecting/blob/master/src/main/java/com/sinthoras/visualprospecting/gui/model/locations/ILocationProvider.java) implementation. You should only add whatever items are visible to this list. There are more methods to override, that will assist you with that. Take a look! - -``` -class MyLayerManager extends LayerManager { - - public static final MyLayerManager instance = new MyLayerManager(); - - public MyLayerManager() { - super(buttonManager); - } - - protected List generateVisibleElements(int minBlockX, int minBlockZ, int maxBlockX, int maxBlockZ) { - return Collections.singletonList(new MyLocation); - } -} -``` - -You have finished the logical implementation of your custom map layer. Congratulations! Now it is time for the visual integration into the map. This example is provided for JourneyMap, but you might as well take a look at the other possibilities. Since you already implemented the button as a first step, you will need to follow it up with an implementation of `DrawStep`, an interface from JourneyMap. This class will receive an instance of `MyLocation` and perform the actual rendering. - -``` -class MyDrawStep implements DrawStep { - - private final MyLocation myLocation; - - public MyDrawStep(MyLocation myLocation) { - this.myLocation = myLocation; - } - - @Override - public void draw(double draggedPixelX, double draggedPixelY, GridRenderer gridRenderer, float drawScale, double fontScale, double rotation) { - final double blockSize = Math.pow(2, gridRenderer.getZoom()); - final Point2D.Double blockAsPixel = gridRenderer.getBlockPixelInGrid(myLocation.getBlockX(), myLocation.getBlockZ()); - final Point2D.Double pixel = new Point2D.Double(blockAsPixel.getX() + draggedPixelX, blockAsPixel.getY() + draggedPixelY); - - DrawUtil.drawLabel(myLocation.getText(), pixel.getX(), pixel.getY(), DrawUtil.HAlign.Center, DrawUtil.VAlign.Middle, 0, 180, 0x00FFFFFF, 255, fontScale, false, rotation); - } -} -``` - -Continue with your own implementation of [`LayerRenderer`](https://github.com/SinTh0r4s/VisualProspecting/blob/master/src/main/java/com/sinthoras/visualprospecting/gui/journeymap/render/LayerRenderer.java). This class will cache all `DrawStep`s and provide it to JourneyMap whenever it is time to render. - -``` -class MyLayerRenderer extends LayerRenderer { - - public MyLayerRenderer() { - // You may skip MyLayerManager and use an existing ButtonManager like "OreVeinLayerManager.instance" - // Your custom layer will toggle with whatever button you specify here - super(MyLayerManager.instance); - } - - @Override - public List mapLocationProviderToDrawStep(List visibleElements) { - final List drawSteps = new ArrayList<>(); - visibleElements.stream() - .map(element -> (MyLocation) element) - .forEach(location -> drawSteps.add(new MyDrawStep(location))); - return drawSteps; - } - -} -``` - -That's already it! Now you need to register some of these classes. Forge's postInit is a good place for it: - -``` -VisualProspecting_API.LogicalClient.registerCustomLayer(MyLayerManager.instance); -VisualProspecting_API.LogicalClient.registerJourneyMapRenderer(new MyLayerRenderer()); -``` - -Now you need to launch Minecraft, teleport to the right sport (`/tp 0 80 0`) and open JourneyMap. - -If you want to extend support for your layer in other maps as well you already have half the work done. You just need to add implementations for the layer and element renderer. \ -For interactive layers you may take a look at extensions/implementations of [`WaypointProviderManager`](https://github.com/SinTh0r4s/VisualProspecting/blob/master/src/main/java/com/sinthoras/visualprospecting/gui/model/layers/WaypointProviderManager.java) and [`ClickableDrawStep`](https://github.com/SinTh0r4s/VisualProspecting/blob/master/src/main/java/com/sinthoras/visualprospecting/gui/journeymap/drawsteps/ClickableDrawStep.java) to get a head start. - Thank you and happy coding,\ SinTh0r4s diff --git a/addon.gradle b/addon.gradle deleted file mode 100644 index ed7fac06..00000000 --- a/addon.gradle +++ /dev/null @@ -1,37 +0,0 @@ -buildscript { - repositories { - mavenCentral() - } - dependencies { - classpath 'de.undercouch:gradle-download-task:4.1.2' - } -} - -import de.undercouch.gradle.tasks.download.Download - -def voxelMapFile = file("data/voxelmap_1.7.0b.jar") -def voxelMapFileDeobf = file("dependencies/voxelmap_1.7.0b-deobf.jar") - -task getVoxelMap(type: Download) { - onlyIf { - !voxelMapFile.exists() - } - src "https://media.forgecdn.net/files/2462/146/mod_voxelMap_1.7.0b_for_1.7.10.litemod" //direct link to VoxelMap on CurseForge - dest voxelMapFile - mustRunAfter "deobfBinJar" - mustRunAfter "repackMinecraft" -} - -task deobfVoxelMap(dependsOn: getVoxelMap, type: Exec) { - onlyIf { - !voxelMapFileDeobf.exists() - } - // VoxelMap uese Notch names, so we have to deobfuscate them - // Source for the BON2 version used: https://github.com/glowredman/BON2/tree/Official - commandLine 'java', '-jar', 'BON2-2.5.0.CUSTOM-all.jar', '--inputJar', voxelMapFile, '--outputJar', voxelMapFileDeobf, '--mcVer', '1.7.10', '--mappingsVer', 'stable_12-1.7.10', '--notch' -} - -tasks.setupCIWorkspace.dependsOn deobfVoxelMap -tasks.setupDevWorkspace.dependsOn deobfVoxelMap -tasks.setupDecompWorkspace.dependsOn deobfVoxelMap -tasks.compileJava.dependsOn deobfVoxelMap diff --git a/build.gradle b/build.gradle index e466de0f..e57a16f9 100644 --- a/build.gradle +++ b/build.gradle @@ -1,588 +1,5 @@ -//version: 1641429628 -/* -DO NOT CHANGE THIS FILE! - -Also, you may replace this file at any time if there is an update available. -Please check https://github.com/SinTh0r4s/ExampleMod1.7.10/blob/main/build.gradle for updates. - */ - - -import com.github.jengelman.gradle.plugins.shadow.tasks.ConfigureShadowRelocation -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar - -import java.util.concurrent.TimeUnit - -buildscript { - repositories { - maven { - name = "forge" - url = "https://maven.minecraftforge.net" - } - maven { - name = "sonatype" - url = "https://oss.sonatype.org/content/repositories/snapshots/" - } - maven { - name = "Scala CI dependencies" - url = "https://repo1.maven.org/maven2/" - } - maven { - name = "jitpack" - url = "https://jitpack.io" - } - } - dependencies { - classpath 'com.github.GTNewHorizons:ForgeGradle:1.2.5' - } -} +//version: 1707058017 plugins { - id 'idea' - id 'scala' - id("org.ajoberstar.grgit") version("3.1.1") - id("com.github.johnrengelman.shadow") version("4.0.4") - id("com.palantir.git-version") version("0.12.3") - id("maven-publish") -} - -apply plugin: 'forge' - -def projectJavaVersion = JavaLanguageVersion.of(8) - -java { - toolchain { - languageVersion.set(projectJavaVersion) - } -} - -idea { - module { - inheritOutputDirs = true - downloadJavadoc = true - downloadSources = true - } -} - -if(JavaVersion.current() != JavaVersion.VERSION_1_8) { - throw new GradleException("This project requires Java 8, but it's running on " + JavaVersion.current()) -} - -checkPropertyExists("modName") -checkPropertyExists("modId") -checkPropertyExists("modGroup") -checkPropertyExists("autoUpdateBuildScript") -checkPropertyExists("minecraftVersion") -checkPropertyExists("forgeVersion") -checkPropertyExists("replaceGradleTokenInFile") -checkPropertyExists("gradleTokenModId") -checkPropertyExists("gradleTokenModName") -checkPropertyExists("gradleTokenVersion") -checkPropertyExists("gradleTokenGroupName") -checkPropertyExists("apiPackage") -checkPropertyExists("accessTransformersFile") -checkPropertyExists("usesMixins") -checkPropertyExists("mixinPlugin") -checkPropertyExists("mixinsPackage") -checkPropertyExists("coreModClass") -checkPropertyExists("containsMixinsAndOrCoreModOnly") -checkPropertyExists("usesShadowedDependencies") -checkPropertyExists("developmentEnvironmentUserName") - - -String javaSourceDir = "src/main/java/" -String scalaSourceDir = "src/main/scala/" - -String targetPackageJava = javaSourceDir + modGroup.toString().replaceAll("\\.", "/") -String targetPackageScala = scalaSourceDir + modGroup.toString().replaceAll("\\.", "/") -if((getFile(targetPackageJava).exists() || getFile(targetPackageScala).exists()) == false) { - throw new GradleException("Could not resolve \"modGroup\"! Could not find " + targetPackageJava + " or " + targetPackageScala) -} - -if(apiPackage) { - targetPackageJava = javaSourceDir + modGroup.toString().replaceAll("\\.", "/") + "/" + apiPackage.toString().replaceAll("\\.", "/") - targetPackageScala = scalaSourceDir + modGroup.toString().replaceAll("\\.", "/") + "/" + apiPackage.toString().replaceAll("\\.", "/") - if((getFile(targetPackageJava).exists() || getFile(targetPackageScala).exists()) == false) { - throw new GradleException("Could not resolve \"apiPackage\"! Could not find " + targetPackageJava + " or " + targetPackageScala) - } -} - -if(accessTransformersFile) { - String targetFile = "src/main/resources/META-INF/" + accessTransformersFile - if(getFile(targetFile).exists() == false) { - throw new GradleException("Could not resolve \"accessTransformersFile\"! Could not find " + targetFile) - } -} - -if(usesMixins.toBoolean()) { - if(mixinsPackage.isEmpty() || mixinPlugin.isEmpty()) { - throw new GradleException("\"mixinPlugin\" requires \"mixinsPackage\" and \"mixinPlugin\" to be set!") - } - - targetPackageJava = javaSourceDir + modGroup.toString().replaceAll("\\.", "/") + "/" + mixinsPackage.toString().replaceAll("\\.", "/") - targetPackageScala = scalaSourceDir + modGroup.toString().replaceAll("\\.", "/") + "/" + mixinsPackage.toString().replaceAll("\\.", "/") - if((getFile(targetPackageJava).exists() || getFile(targetPackageScala).exists()) == false) { - throw new GradleException("Could not resolve \"mixinsPackage\"! Could not find " + targetPackageJava + " or " + targetPackageScala) - } - - String targetFileJava = javaSourceDir + modGroup.toString().replaceAll("\\.", "/") + "/" + mixinPlugin.toString().replaceAll("\\.", "/") + ".java" - String targetFileScala = scalaSourceDir + modGroup.toString().replaceAll("\\.", "/") + "/" + mixinPlugin.toString().replaceAll("\\.", "/") + ".scala" - String targetFileScalaJava = scalaSourceDir + modGroup.toString().replaceAll("\\.", "/") + "/" + mixinPlugin.toString().replaceAll("\\.", "/") + ".java" - if((getFile(targetFileJava).exists() || getFile(targetFileScala).exists() || getFile(targetFileScalaJava).exists()) == false) { - throw new GradleException("Could not resolve \"mixinPlugin\"! Could not find " + targetFileJava + " or " + targetFileScala + " or " + targetFileScalaJava) - } -} - -if(coreModClass) { - String targetFileJava = javaSourceDir + modGroup.toString().replaceAll("\\.", "/") + "/" + coreModClass.toString().replaceAll("\\.", "/") + ".java" - String targetFileScala = scalaSourceDir + modGroup.toString().replaceAll("\\.", "/") + "/" + coreModClass.toString().replaceAll("\\.", "/") + ".scala" - String targetFileScalaJava = scalaSourceDir + modGroup.toString().replaceAll("\\.", "/") + "/" + coreModClass.toString().replaceAll("\\.", "/") + ".java" - if((getFile(targetFileJava).exists() || getFile(targetFileScala).exists() || getFile(targetFileScalaJava).exists()) == false) { - throw new GradleException("Could not resolve \"coreModClass\"! Could not find " + targetFileJava + " or " + targetFileScala + " or " + targetFileScalaJava) - } -} - -configurations.all { - resolutionStrategy.cacheChangingModulesFor(0, TimeUnit.SECONDS) - - // Make sure GregTech build won't time out - System.setProperty("org.gradle.internal.http.connectionTimeout", 120000 as String) - System.setProperty("org.gradle.internal.http.socketTimeout", 120000 as String) -} - -// Fix Jenkins' Git: chmod a file should not be detected as a change and append a '.dirty' to the version -'git config core.fileMode false'.execute() -// Pulls version from git tag -try { - version = minecraftVersion + "-" + gitVersion() -} -catch (Exception e) { - throw new IllegalStateException("This mod must be version controlled by Git AND the repository must provide at least one tag!"); -} - -group = modGroup -if(project.hasProperty("customArchiveBaseName") && customArchiveBaseName) { - archivesBaseName = customArchiveBaseName -} -else { - archivesBaseName = modId -} - -minecraft { - version = minecraftVersion + "-" + forgeVersion + "-" + minecraftVersion - runDir = "run" - - if (replaceGradleTokenInFile) { - replaceIn replaceGradleTokenInFile - if(gradleTokenModId) { - replace gradleTokenModId, modId - } - if(gradleTokenModName) { - replace gradleTokenModName, modName - } - if(gradleTokenVersion) { - replace gradleTokenVersion, versionDetails().lastTag - } - if(gradleTokenGroupName) { - replace gradleTokenGroupName, modGroup - } - } -} - -if(file("addon.gradle").exists()) { - apply from: "addon.gradle" -} - -apply from: 'repositories.gradle' - -configurations { - implementation.extendsFrom(shadowImplementation) // TODO: remove after all uses are refactored - implementation.extendsFrom(shadowCompile) - implementation.extendsFrom(shadeCompile) -} - -repositories { - maven { - name = "Overmind forge repo mirror" - url = "https://gregtech.overminddl1.com/" - } - if(usesMixins.toBoolean()) { - maven { - name = "sponge" - url = "https://repo.spongepowered.org/repository/maven-public" - } - maven { - url = "https://jitpack.io" - } - } -} - -dependencies { - if(usesMixins.toBoolean()) { - annotationProcessor("org.ow2.asm:asm-debug-all:5.0.3") - annotationProcessor("com.google.guava:guava:24.1.1-jre") - annotationProcessor("com.google.code.gson:gson:2.8.6") - annotationProcessor("org.spongepowered:mixin:0.8-SNAPSHOT") - // using 0.8 to workaround a issue in 0.7 which fails mixin application - compile("org.spongepowered:mixin:0.7.11-SNAPSHOT") { - // Mixin includes a lot of dependencies that are too up-to-date - exclude module: "launchwrapper" - exclude module: "guava" - exclude module: "gson" - exclude module: "commons-io" - exclude module: "log4j-core" - } - compile("com.github.GTNewHorizons:SpongeMixins:1.3.3:dev") - } -} - -apply from: 'dependencies.gradle' - -def mixingConfigRefMap = "mixins." + modId + ".refmap.json" -def refMap = "${tasks.compileJava.temporaryDir}" + File.separator + mixingConfigRefMap -def mixinSrg = "${tasks.reobf.temporaryDir}" + File.separator + "mixins.srg" - -task generateAssets { - if(usesMixins.toBoolean()) { - getFile("/src/main/resources/mixins." + modId + ".json").text = """{ - "required": true, - "minVersion": "0.7.11", - "package": "${modGroup}.${mixinsPackage}", - "plugin": "${modGroup}.${mixinPlugin}", - "refmap": "${mixingConfigRefMap}", - "target": "@env(DEFAULT)", - "compatibilityLevel": "JAVA_8" -} - -""" - } -} - -task relocateShadowJar(type: ConfigureShadowRelocation) { - target = tasks.shadowJar - prefix = modGroup + ".shadow" -} - -shadowJar { - project.configurations.shadeCompile.each { dep -> - from(project.zipTree(dep)) { - exclude 'META-INF', 'META-INF/**' - } - } - - manifest { - attributes(getManifestAttributes()) - } - - minimize() // This will only allow shading for actually used classes - configurations = [project.configurations.shadowImplementation, project.configurations.shadowCompile] - dependsOn(relocateShadowJar) -} - -jar { - project.configurations.shadeCompile.each { dep -> - from(project.zipTree(dep)) { - exclude 'META-INF', 'META-INF/**' - } - } - - manifest { - attributes(getManifestAttributes()) - } - - if(usesShadowedDependencies.toBoolean()) { - dependsOn(shadowJar) - enabled = false - } -} - -reobf { - if(usesMixins.toBoolean()) { - addExtraSrgFile mixinSrg - } -} - -afterEvaluate { - if(usesMixins.toBoolean()) { - tasks.compileJava { - options.compilerArgs += [ - "-AreobfSrgFile=${tasks.reobf.srg}", - "-AoutSrgFile=${mixinSrg}", - "-AoutRefMapFile=${refMap}", - // Elan: from what I understand they are just some linter configs so you get some warning on how to properly code - "-XDenableSunApiLintControl", - "-XDignore.symbol.file" - ] - } - } -} - -runClient { - def arguments = [] - - if(usesMixins.toBoolean()) { - arguments += [ - "--mods=../build/libs/$modId-${version}.jar", - "--tweakClass org.spongepowered.asm.launch.MixinTweaker" - ] - } - - if(developmentEnvironmentUserName) { - arguments += [ - "--username", - developmentEnvironmentUserName - ] - } - - args(arguments) -} - -runServer { - def arguments = [] - - if (usesMixins.toBoolean()) { - arguments += [ - "--mods=../build/libs/$modId-${version}.jar", - "--tweakClass org.spongepowered.asm.launch.MixinTweaker" - ] - } - - args(arguments) -} - -tasks.withType(JavaExec).configureEach { - javaLauncher.set( - javaToolchains.launcherFor { - languageVersion = projectJavaVersion - } - ) -} - -processResources -{ - // this will ensure that this task is redone when the versions change. - inputs.property "version", project.version - inputs.property "mcversion", project.minecraft.version - - // replace stuff in mcmod.info, nothing else - from(sourceSets.main.resources.srcDirs) { - include 'mcmod.info' - - // replace version and mcversion - expand "minecraftVersion": project.minecraft.version, - "modVersion": versionDetails().lastTag, - "modId": modId, - "modName": modName - } - - if(usesMixins.toBoolean()) { - from refMap - } - - // copy everything else, thats not the mcmod.info - from(sourceSets.main.resources.srcDirs) { - exclude 'mcmod.info' - } -} - -def getManifestAttributes() { - def manifestAttributes = [:] - if(containsMixinsAndOrCoreModOnly.toBoolean() == false && (usesMixins.toBoolean() || coreModClass)) { - manifestAttributes += ["FMLCorePluginContainsFMLMod": true] - } - - if(accessTransformersFile) { - manifestAttributes += ["FMLAT" : accessTransformersFile.toString()] - } - - if(coreModClass) { - manifestAttributes += ["FMLCorePlugin": modGroup + "." + coreModClass] - } - - if(usesMixins.toBoolean()) { - manifestAttributes += [ - "TweakClass" : "org.spongepowered.asm.launch.MixinTweaker", - "MixinConfigs" : "mixins." + modId + ".json", - "ForceLoadAsMod" : containsMixinsAndOrCoreModOnly.toBoolean() == false - ] - } - return manifestAttributes -} - -task sourcesJar(type: Jar) { - from (sourceSets.main.allJava) - from (file("$projectDir/LICENSE")) - getArchiveClassifier().set('sources') -} - -task shadowDevJar(type: ShadowJar) { - project.configurations.shadeCompile.each { dep -> - from(project.zipTree(dep)) { - exclude 'META-INF', 'META-INF/**' - } - } - - from sourceSets.main.output - getArchiveClassifier().set("dev") - - manifest { - attributes(getManifestAttributes()) - } - - minimize() // This will only allow shading for actually used classes - configurations = [project.configurations.shadowImplementation, project.configurations.shadowCompile] -} - -task relocateShadowDevJar(type: ConfigureShadowRelocation) { - target = tasks.shadowDevJar - prefix = modGroup + ".shadow" -} - -task circularResolverJar(type: Jar) { - dependsOn(relocateShadowDevJar) - dependsOn(shadowDevJar) - enabled = false -} - -task devJar(type: Jar) { - project.configurations.shadeCompile.each { dep -> - from(project.zipTree(dep)) { - exclude 'META-INF', 'META-INF/**' - } - } - - from sourceSets.main.output - getArchiveClassifier().set("dev") - - manifest { - attributes(getManifestAttributes()) - } - - if(usesShadowedDependencies.toBoolean()) { - dependsOn(circularResolverJar) - enabled = false - } -} - -task apiJar(type: Jar) { - from (sourceSets.main.allJava) { - include modGroup.toString().replaceAll("\\.", "/") + "/" + apiPackage.toString().replaceAll("\\.", "/") + '/**' - } - - from (sourceSets.main.output) { - include modGroup.toString().replaceAll("\\.", "/") + "/" + apiPackage.toString().replaceAll("\\.", "/") + '/**' - } - - from (sourceSets.main.resources.srcDirs) { - include("LICENSE") - } - - getArchiveClassifier().set('api') -} - -artifacts { - archives sourcesJar - archives devJar - if(apiPackage) { - archives apiJar - } -} - -// publishing -publishing { - publications { - maven(MavenPublication) { - artifact source: jar - artifact source: sourcesJar, classifier: "src" - artifact source: devJar, classifier: "dev" - if (apiPackage) { - artifact source: apiJar, classifier: "api" - } - - groupId = System.getenv("ARTIFACT_GROUP_ID") ?: group - artifactId = System.getenv("ARTIFACT_ID") ?: project.name - version = System.getenv("ARTIFACT_VERSION") ?: project.version - } - } - - repositories { - maven { - String owner = System.getenv("REPOSITORY_OWNER") ?: "Unknown" - String repositoryName = System.getenv("REPOSITORY_NAME") ?: "Unknown" - String githubRepositoryUrl = "https://maven.pkg.github.com/$owner/$repositoryName" - name = "GitHubPackages" - url = githubRepositoryUrl - credentials { - username = System.getenv("GITHUB_ACTOR") ?: "NONE" - password = System.getenv("GITHUB_TOKEN") ?: "NONE" - } - } - } -} - -// Updating -task updateBuildScript { - doLast { - if (performBuildScriptUpdate(projectDir.toString())) return - - print("Build script already up-to-date!") - } -} - -if (isNewBuildScriptVersionAvailable(projectDir.toString())) { - if (autoUpdateBuildScript.toBoolean()) { - performBuildScriptUpdate(projectDir.toString()) - } else { - println("Build script update available! Run 'gradle updateBuildScript'") - } -} - -static URL availableBuildScriptUrl() { - new URL("https://raw.githubusercontent.com/SinTh0r4s/ExampleMod1.7.10/main/build.gradle") -} - -boolean performBuildScriptUpdate(String projectDir) { - if (isNewBuildScriptVersionAvailable(projectDir)) { - def buildscriptFile = getFile("build.gradle") - availableBuildScriptUrl().withInputStream { i -> buildscriptFile.withOutputStream { it << i } } - print("Build script updated. Please REIMPORT the project or RESTART your IDE!") - return true - } - return false -} - -boolean isNewBuildScriptVersionAvailable(String projectDir) { - Map parameters = ["connectTimeout": 2000, "readTimeout": 2000] - - String currentBuildScript = getFile("build.gradle").getText() - String currentBuildScriptHash = getVersionHash(currentBuildScript) - String availableBuildScript = availableBuildScriptUrl().newInputStream(parameters).getText() - String availableBuildScriptHash = getVersionHash(availableBuildScript) - - boolean isUpToDate = currentBuildScriptHash.empty || availableBuildScriptHash.empty || currentBuildScriptHash == availableBuildScriptHash - return !isUpToDate -} - -static String getVersionHash(String buildScriptContent) { - String versionLine = buildScriptContent.find("^//version: [a-z0-9]*") - if(versionLine != null) { - return versionLine.split(": ").last() - } - return "" -} - -configure(updateBuildScript) { - group = 'forgegradle' - description = 'Updates the build script to the latest version' -} - -// Helper methods - -def checkPropertyExists(String propertyName) { - if (project.hasProperty(propertyName) == false) { - throw new GradleException("This project requires a property \"" + propertyName + "\"! Please add it your \"gradle.properties\". You can find all properties and their description here: https://github.com/SinTh0r4s/ExampleMod1.7.10/blob/main/gradle.properties") - } -} - -def getFile(String relativePath) { - return new File(projectDir, relativePath) + id 'com.gtnewhorizons.gtnhconvention' } diff --git a/dependencies.gradle b/dependencies.gradle index b4033245..8623f1c5 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -1,22 +1,11 @@ dependencies { - shadowImplementation("com.github.SinTh0r4s:Enklume:master-SNAPSHOT") + api('com.github.GTNewHorizons:Navigator:1.1.8:dev') + api('com.github.GTNewHorizons:GTNHLib:0.11.23:dev') + api('com.github.GTNewHorizons:GT5-Unofficial:5.09.54.29:dev') + api('com.github.GTNewHorizons:NotEnoughItems:2.8.110-GTNH:dev') + compileOnly('info.journeymap:journeymap-api-forge:1.7.10-2.0.0') + compileOnlyApi('com.github.GTNewHorizons:Galacticraft:3.4.31-GTNH:dev') { transitive = false } - compile("com.github.GTNewHorizons:GT5-Unofficial:master-SNAPSHOT:dev") - compile("com.github.GTNewHorizons:TCNodeTracker:master-SNAPSHOT:dev") - - compile("curse.maven:journeymap-32274:2367915") - compile files("dependencies/Xaeros_Minimap_21.10.14_Forge_1.7.10-deobf.jar") - compile files("dependencies/XaerosWorldMap_1.14.1.11_Forge_1.7.10-deobf.jar") - compileOnly files("dependencies/voxelmap_1.7.0b-deobf.jar") - - compileOnly("com.github.GTNewHorizons:IFU:master-SNAPSHOT:dev") { - transitive = false - } - compileOnly("com.github.GTNewHorizons:GalacticGregGT5:master-SNAPSHOT:dev") { - transitive = false - } - compileOnly("com.github.GTNewHorizons:bartworks:master-SNAPSHOT:dev") { - transitive = false - } + runtimeOnlyNonPublishable(rfg.deobf('maven.modrinth:journeymap:5.2.20')) } diff --git a/dependencies/XaerosWorldMap_1.14.1.11_Forge_1.7.10-deobf.jar b/dependencies/XaerosWorldMap_1.14.1.11_Forge_1.7.10-deobf.jar deleted file mode 100644 index e2a0822d..00000000 Binary files a/dependencies/XaerosWorldMap_1.14.1.11_Forge_1.7.10-deobf.jar and /dev/null differ diff --git a/dependencies/Xaeros_Minimap_21.10.14_Forge_1.7.10-deobf.jar b/dependencies/Xaeros_Minimap_21.10.14_Forge_1.7.10-deobf.jar deleted file mode 100644 index c31265a8..00000000 Binary files a/dependencies/Xaeros_Minimap_21.10.14_Forge_1.7.10-deobf.jar and /dev/null differ diff --git a/gradle.properties b/gradle.properties index cbf81ffc..e941a401 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,64 +1,238 @@ +# ExampleMod tag to use as Blowdryer (Spotless, etc.) settings version, leave empty to disable. +# LOCAL to test local config updates. +gtnh.settings.blowdryerTag = 0.2.0 + +# Human-readable mod name, available for mcmod.info population. modName = VisualProspecting -# This is a case-sensitive string to identify your mod. Convention is to use lower case. +# Case-sensitive identifier string, available for mcmod.info population and used for automatic mixin JSON generation. +# Conventionally lowercase. modId = visualprospecting +# Root package of the mod, used to find various classes in other properties, +# mcmod.info substitution, enabling assertions in run tasks, etc. modGroup = com.sinthoras.visualprospecting -# WHY is there no version field? -# The build script relies on git to provide a version via tags. It is super easy and will enable you to always know the -# code base or your binary. Check out this tutorial: https://blog.mattclemente.com/2017/10/13/versioning-with-git-tags/ +# Whether to use modGroup as the maven publishing group. +# When false, com.github.GTNewHorizons is used. +useModGroupForPublishing = false -# Will update your build.gradle automatically whenever an update is available +# Updates your build.gradle and settings.gradle automatically whenever an update is available. autoUpdateBuildScript = false +# Version of Minecraft to target minecraftVersion = 1.7.10 + +# Version of Minecraft Forge to target forgeVersion = 10.13.4.1614 -# Select a username for testing your mod with breakpoints. You may leave this empty for a random username each time you -# restart Minecraft in development. Choose this dependent on your mod: -# Do you need consistent player progressing (for example Thaumcraft)? -> Select a name -# Do you need to test how your custom blocks interacts with a player that is not the owner? -> leave name empty +# Specify an MCP channel for dependency deobfuscation and the deobfParams task. +channel = stable + +# Specify an MCP mappings version for dependency deobfuscation and the deobfParams task. +mappingsVersion = 12 + +# Defines other MCP mappings for dependency deobfuscation. +remoteMappings = https\://raw.githubusercontent.com/MinecraftForge/FML/1.7.10/conf/ + +# Select a default username for testing your mod. You can always override this per-run by running +# `./gradlew runClient --username=AnotherPlayer`, or configuring this command in your IDE. developmentEnvironmentUserName = Developer -# Define a source file of your project with: +# Enables modern Java syntax support. Valid values: +# - false: No modern syntax, Java 8 only +# - jabel: Jabel syntax-only support, compiles to J8 bytecode +# - jvmDowngrader: Full modern Java via JVM Downgrader (syntax + stdlib APIs) +# - modern: Native modern Java bytecode, no downgrading +enableModernJavaSyntax = jabel + +# If set, ignores the above setting and compiles with the given toolchain. This may cause unexpected issues, +# and should *not* be used in most situations. -1 disables this. +# forceToolchainVersion = -1 + +# Target JVM version for JVM Downgrader bytecode downgrading. +# Only used when enableModernJavaSyntax = jvmDowngrader +# downgradeTargetVersion = 8 + +# Comma-separated list of Java versions for multi-release jar support (JVM Downgrader only). +# Classes will be available in META-INF/versions/N/ for each version N in this list. +# Default: "21,25" (J25+ gets native classes, J21-24 gets partial downgrade, J8-20 gets full downgrade). +# jvmDowngraderMultiReleaseVersions = 21,25 + +# Specifies how JVM Downgrader API stubs are provided. Options: +# - shade: Shade minimized stubs into the jar +# - gtnhlib: GTNHLib provides stubs at runtime (adds version constraint) +# - external: Another dependency provides stubs (no constraint, no warning) +# - (empty): Warning reminding you to configure stubs +# Note: 'shade' option requires you to verify license compliance, see: https://github.com/unimined/JvmDowngrader/blob/main/LICENSE.md +# jvmDowngraderStubsProvider = + +# Enables injecting missing generics into the decompiled source code for a better coding experience. +# Turns most publicly visible List, Map, etc. into proper List, Map types. +enableGenericInjection = true + +# Generate a class with a String field for the mod version named as defined below. +# If generateGradleTokenClass is empty or not missing, no such class will be generated. +# If gradleTokenVersion is empty or missing, the field will not be present in the class. +generateGradleTokenClass = com.sinthoras.visualprospecting.Version + +# Name of the token containing the project's current version to generate/replace. +gradleTokenVersion = VERSION + +# [DEPRECATED] Mod ID replacement token. +gradleTokenModId = + +# [DEPRECATED] Mod name replacement token. +gradleTokenModName = + +# [DEPRECATED] Mod Group replacement token. +gradleTokenGroupName = + +# [DEPRECATED] +# Multiple source files can be defined here by providing a comma-separated list: Class1.java,Class2.java,Class3.java # public static final String VERSION = "GRADLETOKEN_VERSION"; # The string's content will be replaced with your mod's version when compiled. You should use this to specify your mod's -# version in @Mod([...], version = VERSION, [...]) -# Leave these properties empty to skip individual token replacements -replaceGradleTokenInFile = Tags.java -gradleTokenModId = GRADLETOKEN_MODID -gradleTokenModName = GRADLETOKEN_MODNAME -gradleTokenVersion = GRADLETOKEN_VERSION -gradleTokenGroupName = GRADLETOKEN_GROUPNAME +# version in @Mod([...], version = VERSION, [...]). +# Leave these properties empty to skip individual token replacements. +replaceGradleTokenInFile = # In case your mod provides an API for other mods to implement you may declare its package here. Otherwise, you can # leave this property empty. -# Example value: apiPackage = api + modGroup = com.myname.mymodid -> com.myname.mymodid.api +# Example value: (apiPackage = api) + (modGroup = com.myname.mymodid) -> com.myname.mymodid.api apiPackage = -# Specify the configuration file for Forge's access transformers here. I must be placed into /src/main/resources/META-INF/ -# Example value: mymodid_at.cfg +# Specify the configuration file for Forge's access transformers here. It must be placed into /src/main/resources/META-INF/ +# There can be multiple files in a space-separated list. +# Example value: mymodid_at.cfg nei_at.cfg accessTransformersFile = visualprospecting_at.cfg # Provides setup for Mixins if enabled. If you don't know what mixins are: Keep it disabled! usesMixins = true + +# Set to a non-empty string to configure mixins in a separate source set under src/VALUE, instead of src/main. +# This can speed up compile times thanks to not running the mixin annotation processor on all input sources. +# Mixin classes will have access to "main" classes, but not the other way around. +separateMixinSourceSet = + +# Adds some debug arguments like verbose output and class export. +usesMixinDebug = true + # Specify the location of your implementation of IMixinConfigPlugin. Leave it empty otherwise. -mixinPlugin = mixinplugin.MixinPlugin -# Specify the package that contains all of your Mixins. You may only place Mixins in this package or the build will fail! +mixinPlugin = + +# Specify the package that contains all of your Mixins. The package must exist or +# the build will fail. If you have a package property defined in your mixins..json, +# it must match with this or the build will fail. mixinsPackage = mixins + # Specify the core mod entry class if you use a core mod. This class must implement IFMLLoadingPlugin! # This parameter is for legacy compatibility only -# Example value: coreModClass = asm.FMLPlugin + modGroup = com.myname.mymodid -> com.myname.mymodid.asm.FMLPlugin -coreModClass = +# Example value: (coreModClass = asm.FMLPlugin) + (modGroup = com.myname.mymodid) -> com.myname.mymodid.asm.FMLPlugin +coreModClass = mixinplugin.VisualProspectingEarlyMixinLoader + # If your project is only a consolidation of mixins or a core mod and does NOT contain a 'normal' mod ( = some class # that is annotated with @Mod) you want this to be true. When in doubt: leave it on false! containsMixinsAndOrCoreModOnly = false -# If enabled, you may use 'shadowImplementation' for dependencies. They will be integrated in your jar. It is your -# responsibility check the licence and request permission for distribution, if required. +# Enables Mixins even if this mod doesn't use them, useful if one of the dependencies uses mixins. +forceEnableMixins = false + +# If enabled, you may use 'shadowCompile' for dependencies. They will be integrated into your jar. It is your +# responsibility to check the license and request permission for distribution if required. usesShadowedDependencies = true -# Optional parameter to customize the produced artifacts. Use this to preserver artifact naming when migrating older +# If disabled, won't remove unused classes from shadowed dependencies. Some libraries use reflection to access +# their own classes, making the minimization unreliable. +minimizeShadowedDependencies = true + +# If disabled, won't rename the shadowed classes. +relocateShadowedDependencies = true + +# Adds CurseMaven, Modrinth, and some more well-known 1.7.10 repositories. +includeWellKnownRepositories = true + +# A list of repositories to exclude from the includeWellKnownRepositories setting. Should be a space separated +# list of strings, with the acceptable keys being(case does not matter): +# cursemaven +# modrinth +excludeWellKnownRepositories = + +# Change these to your Maven coordinates if you want to publish to a custom Maven repository instead of the default GTNH Maven. +# Authenticate with the MAVEN_USER and MAVEN_PASSWORD environment variables. +# If you need a more complex setup disable maven publishing here and add a publishing repository to addon.gradle. +usesMavenPublishing = true + +# Maven repository to publish the mod to. +# mavenPublishUrl = https\://nexus.gtnewhorizons.com/repository/releases/ + +# Publishing to Modrinth requires you to set the MODRINTH_TOKEN environment variable to your current Modrinth API token. +# +# The project's ID on Modrinth. Can be either the slug or the ID. +# Leave this empty if you don't want to publish to Modrinth. +modrinthProjectId = + +# The project's relations on Modrinth. You can use this to refer to other projects on Modrinth. +# Syntax: scope1-type1:name1;scope2-type2:name2;... +# Where scope can be one of [required, optional, incompatible, embedded], +# type can be one of [project, version], +# and the name is the Modrinth project or version slug/id of the other mod. +# Example: required-project:fplib;optional-project:gasstation;incompatible-project:gregtech +# Note: UniMixins is automatically set as a required dependency if usesMixins = true. +modrinthRelations = + +# Publishing to CurseForge requires you to set the CURSEFORGE_TOKEN environment variable to one of your CurseForge API tokens. +# +# The project's numeric ID on CurseForge. You can find this in the About Project box. +# Leave this empty if you don't want to publish on CurseForge. +curseForgeProjectId = + +# The project's relations on CurseForge. You can use this to refer to other projects on CurseForge. +# Syntax: type1:name1;type2:name2;... +# Where type can be one of [requiredDependency, embeddedLibrary, optionalDependency, tool, incompatible], +# and the name is the CurseForge project slug of the other mod. +# Example: requiredDependency:railcraft;embeddedLibrary:cofhlib;incompatible:buildcraft +# Note: UniMixins is automatically set as a required dependency if usesMixins = true. +curseForgeRelations = + +# Optional parameter to customize the produced artifacts. Use this to preserve artifact naming when migrating older # projects. New projects should not use this parameter. -#customArchiveBaseName = +# customArchiveBaseName = + +# Optional parameter to customize the default working directory used by the runClient* tasks. Relative to the project directory. +# runClientWorkingDirectory = run/client + +# Optional parameter to customize the default working directory used by the runServer* tasks. Relative to the project directory. +# runServerWorkingDirectory = run/server + +# Optional parameter to have the build automatically fail if an illegal version is used. +# This can be useful if you e.g. only want to allow versions in the form of '1.1.xxx'. +# The check is ONLY performed if the version is a git tag. +# Note: the specified string must be escaped, so e.g. 1\\.1\\.\\d+ instead of 1\.1\.\d+ +# versionPattern = + +# Uncomment to prevent the source code from being published. +# noPublishedSources = true + +# Uncomment this to disable Spotless checks. +# This should only be uncommented to keep it easier to sync with upstream/other forks. +# That is, if there is no other active fork/upstream, NEVER change this. +# disableSpotless = true + +# Uncomment this to disable Checkstyle checks (currently wildcard import check). +# disableCheckstyle = true + +# Override the IDEA build type. Valid values are: "" (leave blank, do not override), "idea" (force use native IDEA build), "gradle" +# (force use delegated build). +# This is meant to be set in $HOME/.gradle/gradle.properties. +# e.g. add "systemProp.org.gradle.project.ideaOverrideBuildType=idea" will override the build type to be native build. +# WARNING: If you do use this option, it will overwrite whatever you have in your existing projects. This might not be what you want! +# Usually there is no need to uncomment this here as other developers do not necessarily use the same build type as you. +# ideaOverrideBuildType = idea + +# Whether IDEA should run spotless checks when pressing the Build button. +# This is meant to be set in $HOME/.gradle/gradle.properties. +# ideaCheckSpotlessOnBuild = true + +# Non-GTNH properties +org.gradle.configuration-cache = true +org.gradle.parallel = true diff --git a/gradle/gradle-daemon-jvm.properties b/gradle/gradle-daemon-jvm.properties new file mode 100644 index 00000000..fa4ed510 --- /dev/null +++ b/gradle/gradle-daemon-jvm.properties @@ -0,0 +1,12 @@ +#This file is generated by updateDaemonJvm +toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/cf726b4a1c84b50457225f9bba6d7650/redirect +toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/fa1e318c287360478e3c83a9a3ef1007/redirect +toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/cf726b4a1c84b50457225f9bba6d7650/redirect +toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/fa1e318c287360478e3c83a9a3ef1007/redirect +toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/c2dd35c9d0aaf0ba6ad0791320f99dfc/redirect +toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/e5810bd7fd1f8a586644409d395a7e55/redirect +toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/cf726b4a1c84b50457225f9bba6d7650/redirect +toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/fa1e318c287360478e3c83a9a3ef1007/redirect +toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/7b3c4877c0749019e6805bb61e421497/redirect +toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/d76df094a9cbbabd3b08251f9e61444a/redirect +toolchainVersion=25 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 5c2d1cf0..d997cfc6 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3ab0b725..dbc3ce4a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.9.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 83f2acfd..0262dcbd 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,80 +15,114 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/b631911858264c0b6e4d6603d677ff5218766cee/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -97,92 +131,118 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=$((i+1)) + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=$(save "$@") -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" fi +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 9618d8d9..c4bdd3ab 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,8 +13,10 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,10 +27,14 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @@ -37,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -51,48 +57,35 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/repositories.gradle b/repositories.gradle index d943c8f9..1e870960 100644 --- a/repositories.gradle +++ b/repositories.gradle @@ -1,16 +1,6 @@ repositories { maven { - name = "ic2" - url = "https://maven.ic2.player.to/" - metadataSources { - mavenPom() - artifact() - } - } - maven { - url "https://cursemaven.com" - } - maven { - url = "https://jitpack.io" + name = "JourneyMap" + url = "https://maven.blamejared.com" } } diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 00000000..7371c582 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,23 @@ + +pluginManagement { + repositories { + maven { + // RetroFuturaGradle + name "GTNH Maven" + url "https://nexus.gtnewhorizons.com/repository/public/" + mavenContent { + includeGroup("com.gtnewhorizons") + includeGroupByRegex("com\\.gtnewhorizons\\..+") + } + } + gradlePluginPortal() + mavenCentral() + mavenLocal() + } +} + +plugins { + id 'com.gtnewhorizons.gtnhsettingsconvention' version '2.0.26' +} + + diff --git a/src/main/java/com/sinthoras/visualprospecting/Config.java b/src/main/java/com/sinthoras/visualprospecting/Config.java index 3d76054f..679c0a2e 100644 --- a/src/main/java/com/sinthoras/visualprospecting/Config.java +++ b/src/main/java/com/sinthoras/visualprospecting/Config.java @@ -1,15 +1,17 @@ package com.sinthoras.visualprospecting; +import java.io.File; + import net.minecraftforge.common.config.Configuration; import net.minecraftforge.common.config.Property; -import java.io.File; - public class Config { private static class Defaults { public static final boolean enableProspecting = true; + public static final boolean enableTeamSharing = true; + public static final boolean keepProspectionOnTeamLeave = true; public static final int cacheGenerationLogUpdateMinTime = 5; public static final boolean recacheVeins = false; public static final int minDelayBetweenVeinRequests = 2000; @@ -18,10 +20,12 @@ private static class Defaults { public static final int uploadBandwidthBytes = 2000000; public static final int maxTransferCacheSizeMB = 50; public static final boolean enableVoxelMapWaypointsByDefault = false; - public static final int maxDimensionSizeMBForFastScanning = 10000; + public static final boolean showOreLabelsOnJourneyMap6Minimap = true; + public static final int maxRegionRowFileMBForInMemoryScan = 10000; } private static class Categories { + public static final String general = "general"; public static final String network = "network"; public static final String integration = "integration"; @@ -29,6 +33,8 @@ private static class Categories { } public static boolean enableProspecting = Defaults.enableProspecting; + public static boolean enableTeamSharing = Defaults.enableTeamSharing; + public static boolean keepProspectionOnTeamLeave = Defaults.keepProspectionOnTeamLeave; public static int cacheGenerationLogUpdateMinTime = Defaults.cacheGenerationLogUpdateMinTime; public static boolean recacheVeins = Defaults.recacheVeins; public static int minDelayBetweenVeinRequests = Defaults.minDelayBetweenVeinRequests; @@ -38,65 +44,114 @@ private static class Categories { public static int uploadPacketsPerSecond = uploadBandwidthBytes / VP.uploadSizePerPacketInBytes; public static int maxTransferCacheSizeMB = Defaults.maxTransferCacheSizeMB; public static boolean enableVoxelMapWaypointsByDefault = Defaults.enableVoxelMapWaypointsByDefault; - public static int maxDimensionSizeMBForFastScanning = Defaults.maxDimensionSizeMBForFastScanning; - + public static boolean showOreLabelsOnJourneyMap6Minimap = Defaults.showOreLabelsOnJourneyMap6Minimap; + public static int maxRegionRowFileMBForInMemoryScan = Defaults.maxRegionRowFileMBForInMemoryScan; public static void syncronizeConfiguration(File configFile) { Configuration configuration = new Configuration(configFile); configuration.load(); - Property enableProspectingProperty = configuration.get(Categories.general, "enableProspecting", - Defaults.enableProspecting, "[CLIENT] You may want to disable prospecting for low-performance clients."); + Property enableProspectingProperty = configuration.get( + Categories.general, + "enableProspecting", + Defaults.enableProspecting, + "[CLIENT] You may want to disable prospecting for low-performance clients."); enableProspecting = enableProspectingProperty.getBoolean(); - Property cacheGenerationLogUpdateMinTimeProperty = configuration.get(Categories.general, "cacheGenerationLogUpdateMinTime", - Defaults.cacheGenerationLogUpdateMinTime, "[SERVER] Minimum between log updates to show progress when " + - "caching save files. This happens only ONCE!"); + Property enableTeamSharingProperty = configuration.get( + Categories.general, + "enableTeamSharing", + Defaults.enableTeamSharing, + "[SERVER] Share prospecting data automatically between members of a GTNHLib team."); + enableTeamSharing = enableTeamSharingProperty.getBoolean(); + + Property keepProspectionOnTeamLeaveProperty = configuration.get( + Categories.general, + "keepProspectionOnTeamLeave", + Defaults.keepProspectionOnTeamLeave, + "[SERVER] Allow the player leaving a team to keep a copy of the team prospection data into their new solo team."); + keepProspectionOnTeamLeave = keepProspectionOnTeamLeaveProperty.getBoolean(); + + Property cacheGenerationLogUpdateMinTimeProperty = configuration.get( + Categories.general, + "cacheGenerationLogUpdateMinTime", + Defaults.cacheGenerationLogUpdateMinTime, + "[SERVER] Minimum between log updates to show progress when " + + "caching save files. This happens only ONCE!"); cacheGenerationLogUpdateMinTime = cacheGenerationLogUpdateMinTimeProperty.getInt(); - Property minDelayBetweenVeinRequestsProperty = configuration.get(Categories.network, "minDelayBetweenVeinRequests", - Defaults.minDelayBetweenVeinRequests, "[CLIENT + SERVER] Anti spam mechanic: What is the minimum delay (in milliseconds)" + - " a player is allowed to request ore vein information."); + Property minDelayBetweenVeinRequestsProperty = configuration.get( + Categories.network, + "minDelayBetweenVeinRequests", + Defaults.minDelayBetweenVeinRequests, + "[CLIENT + SERVER] Anti spam mechanic: What is the minimum delay (in milliseconds)" + + " a player is allowed to request ore vein information."); minDelayBetweenVeinRequests = minDelayBetweenVeinRequestsProperty.getInt(); - Property minZoomLevelProperty = configuration.get(Categories.general, "minZoomLevelForOreLabel", Defaults.minZoomLevelForOreLabel, + Property minZoomLevelProperty = configuration.get( + Categories.general, + "minZoomLevelForOreLabel", + Defaults.minZoomLevelForOreLabel, "[CLIENT] Minimum zoom level at which ore veins labels are displayed. Zoom starts at 0 and increments linearly."); minZoomLevelForOreLabel = minZoomLevelProperty.getInt(); - Property minZoomLevelForUndergroundFluidDetailsProperty = configuration.get(Categories.general, "minZoomLevelForUndergroundFluidDetails", - Defaults.minZoomLevelForUndergroundFluidDetails, "[CLIENT] Minimum zoom level at which underground fluid details are displayed. Zoom starts at 0 and increments linearly."); + Property minZoomLevelForUndergroundFluidDetailsProperty = configuration.get( + Categories.general, + "minZoomLevelForUndergroundFluidDetails", + Defaults.minZoomLevelForUndergroundFluidDetails, + "[CLIENT] Minimum zoom level at which underground fluid details are displayed. Zoom starts at 0 and increments linearly."); minZoomLevelForUndergroundFluidDetails = minZoomLevelForUndergroundFluidDetailsProperty.getInt(); - Property uploadBandwidthProperty = configuration.get(Categories.network, "uploadBandwidth", Defaults.uploadBandwidthBytes, - "[CLIENT + SERVER] Limit the bandwidth (in B/s) a client is allowed to transmit when uploading its prospection data." + - " If exceeded, the client will be kicked!"); + Property uploadBandwidthProperty = configuration.get( + Categories.network, + "uploadBandwidth", + Defaults.uploadBandwidthBytes, + "[CLIENT + SERVER] Limit the bandwidth (in B/s) a client is allowed to transmit when uploading its prospection data." + + " If exceeded, the client will be kicked!"); uploadBandwidthBytes = uploadBandwidthProperty.getInt(); uploadPacketsPerSecond = uploadBandwidthBytes / VP.uploadSizePerPacketInBytes; - Property maxTransferCacheSizeMBProperty = configuration.get(Categories.general, "maxTransferCacheSizeMB", - Defaults.maxTransferCacheSizeMB, "[Server] Limit the RAM size in MB of what can players use to" + - " synchronize their map data."); + Property maxTransferCacheSizeMBProperty = configuration.get( + Categories.general, + "maxTransferCacheSizeMB", + Defaults.maxTransferCacheSizeMB, + "[Server] Limit the RAM size in MB of what can players use to" + " synchronize their map data."); maxTransferCacheSizeMB = maxTransferCacheSizeMBProperty.getInt(); - Property recacheVeinsProperty = configuration.get(Categories.general, "recacheVeins", Defaults.recacheVeins, - "[SERVER] Redo GT ore vein caching if set to True. Will automatically be set back to False the next " + - "time the game is started."); + Property recacheVeinsProperty = configuration.get( + Categories.general, + "recacheVeins", + Defaults.recacheVeins, + "[SERVER] Redo GT ore vein caching if set to True. Will automatically be set back to False the next " + + "time the game is started."); recacheVeins = recacheVeinsProperty.getBoolean(); - if(recacheVeins) { + if (recacheVeins) { recacheVeinsProperty.set(false); } - - Property enableVoxelMapWaypointsByDefaultProperty = configuration.get(Categories.integration, "enableVoxelMapWaypointsByDefault", Defaults.enableVoxelMapWaypointsByDefault, - "[CLIENT / VoxelMap] Enable waypoints added by prospecting GT ore veins or underground fluids by default"); - enableVoxelMapWaypointsByDefault = enableVoxelMapWaypointsByDefaultProperty.getBoolean(); - Property maxDimensionSizeMBForFastScanningProperty = configuration.get(Categories.caching, "maxDimensionSizeMBForFastScanning", - Defaults.maxDimensionSizeMBForFastScanning, "[Client + Server] Define the maximum size of a " + - "dimension in MB that can be processed in a single pass. Reduce this number if you run into " + - "memory issues during the initial world scan (OutOfMemoryException)."); - maxDimensionSizeMBForFastScanning = maxDimensionSizeMBForFastScanningProperty.getInt(); + Property enableVoxelMapWaypointsByDefaultProperty = configuration.get( + Categories.integration, + "enableVoxelMapWaypointsByDefault", + Defaults.enableVoxelMapWaypointsByDefault, + "[CLIENT / VoxelMap] Enable waypoints added by prospecting GT ore veins or underground fluids by default"); + enableVoxelMapWaypointsByDefault = enableVoxelMapWaypointsByDefaultProperty.getBoolean(); - if(configuration.hasChanged()) { + Property showOreLabelsOnJourneyMap6MinimapProperty = configuration.get( + Categories.integration, + "showOreLabelsOnJourneyMap6Minimap", + Defaults.showOreLabelsOnJourneyMap6Minimap, + "[CLIENT / JourneyMap 6] Show ore vein labels on the minimap."); + showOreLabelsOnJourneyMap6Minimap = showOreLabelsOnJourneyMap6MinimapProperty.getBoolean(); + + Property maxRegionRowFileMBForInMemoryScanProperty = configuration.get( + Categories.caching, + "maxRegionRowFileMBForInMemoryScan", + Defaults.maxRegionRowFileMBForInMemoryScan, + "[CLIENT + SERVER] Limit total region file size the recache vein job can keep open at once. " + + "This is about size on disk and not actual RAM usage. Reduce this if you get a OutOfMemoryException during recache."); + maxRegionRowFileMBForInMemoryScan = maxRegionRowFileMBForInMemoryScanProperty.getInt(); + + if (configuration.hasChanged()) { configuration.save(); } } diff --git a/src/main/java/com/sinthoras/visualprospecting/ServerTranslations.java b/src/main/java/com/sinthoras/visualprospecting/ServerTranslations.java deleted file mode 100644 index 32d16737..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/ServerTranslations.java +++ /dev/null @@ -1,286 +0,0 @@ -package com.sinthoras.visualprospecting; - -import com.sinthoras.visualprospecting.database.veintypes.VeinType; -import net.minecraft.client.resources.I18n; -import net.minecraft.server.MinecraftServer; -import net.minecraftforge.fluids.Fluid; - -// Backup translations for server side lookups only -public class ServerTranslations { - - public static String getEnglishLocalization(Fluid fluid) { - if(MinecraftServer.getServer().isSinglePlayer()) { - return fluid.getLocalizedName(); - } - - switch(fluid.getUnlocalizedName()) { - case "fluid.gas_natural_gas": - return "Natural Gas"; - case "fluid.liquid_light_oil": - return "Light Oil"; - case "fluid.liquid_medium_oil": - return "Raw Oil"; - case "fluid.liquid_heavy_oil": - return "Heavy Oil"; - case "fluid.oil": - return "Oil"; - case "fluid.drillingfluid": - return "Drilling Fluid"; - case "fluid.helium-3": - return "Helium-3"; - case "fluid.saltwater": - return "Saltwater"; - case "fluid.molten.iron": - return "Molten Iron"; - case "fluid.molten.lead": - return "Molten Lead"; - case "fluid.sulfuricacid": - return "Sulfuric Acid"; - case "fluid.carbondioxide": - return "Carbondioxide"; - case "fluid.chlorobenzene": - return "Chlorobenzene"; - case "fluid.liquid_extra_heavy_oil": - return "Extra Heavy Oil"; - case "fluid.ic2distilledwater": - return "Distilled Water"; - case "fluid.oxygen": - return "Oxygen"; - case "fluid.liquidair": - return "Liquid Air"; - case "fluid.methane": - return "Methane"; - case "fluid.ethane": - return "Ethane"; - case "fluid.liquid_hydricsulfur": - return "Liquid Hydric Sulfur"; - case "fluid.carbonmonoxide": - return "Carbonmonoxide"; - case "fluid.nitrogen": - return "Nitrogen"; - case "fluid.ethylene": - return "Ethylene"; - case "fluid.deuterium": - return "Deuterium"; - case "fluid.fluorine": - return "Fluorine"; - case "fluid.hydrofluoricacid_gt5u": - return "Hydrofluoric Acid"; - case "fluid.molten.copper": - return "Molten Copper"; - case "fluid.unknowwater": - return "Unknowwater"; - case "fluid.molten.tin": - return "Molten Tin"; - case "fluid.hydrogen": - return "Hydrogen"; - case "fluid.lava": - return "Lava"; - default: - return fluid.getUnlocalizedName(); - } - } - - /* - Copy the relevant content into a file 'data.dat' and run the following python script to generate the switch-case: - - with open("data.dat") as f: - lines = f.readlines() - for line in lines: - if line is not "\n": - key, value = line.split("=") - value = value[:-1] - print("case \"" + key + "\":") - print(" return \"" + value + "\";") - */ - public static String getEnglishLocalization(VeinType veinType) { - if(MinecraftServer.getServer().isSinglePlayer()) { - return I18n.format(veinType.name); - } - - switch (veinType.name) { - case "ore.mix.naquadah": - return "Naquadah"; - case "ore.mix.lignite": - return "Lignite"; - case "ore.mix.coal": - return "Coal"; - case "ore.mix.magnetite": - return "Magnetite"; - case "ore.mix.gold": - return "Gold"; - case "ore.mix.iron": - return "Iron"; - case "ore.mix.cassiterite": - return "Cassiterite"; - case "ore.mix.tetrahedrite": - return "Tetrahedrite"; - case "ore.mix.netherquartz": - return "Nether Quartz"; - case "ore.mix.sulfur": - return "Sulfur"; - case "ore.mix.copper": - return "Copper"; - case "ore.mix.bauxite": - return "Bauxite"; - case "ore.mix.salts": - return "Salts"; - case "ore.mix.redstone": - return "Redstone"; - case "ore.mix.soapstone": - return "Soapstone"; - case "ore.mix.nickel": - return "Nickel"; - case "ore.mix.platinum": - return "Platinum"; - case "ore.mix.pitchblende": - return "Pitchblende"; - case "ore.mix.monazite": - return "Monazite"; - case "ore.mix.molybdenum": - return "Molybdenum"; - case "ore.mix.tungstate": - return "Tungstate"; - case "ore.mix.sapphire": - return "Sapphire"; - case "ore.mix.manganese": - return "Manganese"; - case "ore.mix.quartz": - return "Quartz"; - case "ore.mix.diamond": - return "Diamond"; - case "ore.mix.olivine": - return "Olivine"; - case "ore.mix.apatite": - return "Apatite"; - case "ore.mix.galena": - return "Galena"; - case "ore.mix.lapis": - return "Lapis"; - case "ore.mix.beryllium": - return "Beryllium"; - case "ore.mix.uranium": - return "Uranium"; - case "ore.mix.oilsand": - return "Oilsands"; - case "ore.mix.neutronium": - return "Neutronium"; - case "ore.mix.aquaignis": - return "Aqua and Ignis"; - case "ore.mix.terraaer": - return "Terra and Aer"; - case "ore.mix.perditioordo": - return "Perdito and Ordo"; - case "ore.mix.coppertin": - return "Vermiculite"; - case "ore.mix.titaniumchrome": - return "Ilmenite"; - case "ore.mix.mineralsand": - return "Mineralsand"; - case "ore.mix.garnettin": - return "Garnettin"; - case "ore.mix.kaolinitezeolite": - return "Kaolinite"; - case "ore.mix.mica": - return "Mica"; - case "ore.mix.dolomite": - return "Dolomite"; - case "ore.mix.platinumchrome": - return "Palladium"; - case "ore.mix.iridiummytryl": - return "Iridium"; - case "ore.mix.osmium": - return "Osmium"; - case "ore.mix.saltpeterelectrotine": - return "Electrotine"; - case "ore.mix.desh": - return "Desh"; - case "ore.mix.draconium": - return "Draconium"; - case "ore.mix.quantium": - return "Quantum"; - case "ore.mix.callistoice": - return "Callisto Ice"; - case "ore.mix.mytryl": - return "Mithril"; - case "ore.mix.ledox": - return "Ledox"; - case "ore.mix.oriharukon": - return "Oriharukon"; - case "ore.mix.blackplutonium": - return "Black Plutonium"; - case "ore.mix.infusedgold": - return "Infused Gold"; - case "ore.mix.niobium": - return "Niobium"; - case "ore.mix.tungstenirons": - return "Tungsten"; - case "ore.mix.uraniumgtnh": - return "Thorium"; - case "ore.mix.vanadiumgold": - return "Vanadium"; - case "ore.mix.netherstar": - return "NetherStar"; - case "ore.mix.garnet": - return "Garnet"; - case "ore.mix.rareearth": - return "Rare Earths"; - case "ore.mix.richnuclear": - return "Plutonium"; - case "ore.mix.heavypentele": - return "Arsenic"; - case "ore.mix.europa": - return "Magnesite"; - case "ore.mix.europacore": - return "Chrome"; - case "ore.mix.secondlanthanid": - return "Samarium"; - case "ore.mix.quartzspace": - return "Quartz"; - case "ore.mix.rutile": - return "Rutile"; - case "ore.mix.tfgalena": - return "Cryolite"; - case "ore.mix.luvtantalite": - return "Pyrolusit"; - case "ore.mix.ross128.Thorianit": - return "Thorianit"; - case "ore.mix.ross128.carbon": - return "Graphite"; - case "ore.mix.ross128.bismuth": - return "Bismuth"; - case "ore.mix.ross128.TurmalinAlkali": - return "Olenit"; - case "ore.mix.ross128.Roquesit": - return "Roquesit"; - case "ore.mix.ross128.Tungstate": - return "Scheelite"; - case "ore.mix.ross128.CopperSulfits": - return "Djurleit"; - case "ore.mix.ross128.Forsterit": - return "Forsterit"; - case "ore.mix.ross128.Hedenbergit": - return "Hedenbergit"; - case "ore.mix.ross128.RedZircon": - return "Red Zircon"; - case "ore.mix.ross128ba.tib": - return "Tiberium"; - case "ore.mix.ross128ba.Tungstate": - return "Scheelite"; - case "ore.mix.ross128ba.bart": - return "BArTiMaEuSNeK"; - case "ore.mix.ross128ba.TurmalinAlkali": - return "Olenit"; - case "ore.mix.ross128ba.Amethyst": - return "Amethyst"; - case "ore.mix.ross128ba.CopperSulfits": - return "Djurleit"; - case "ore.mix.ross128ba.RedZircon": - return "Red Zircon"; - case "ore.mix.ross128ba.Fluorspar": - return "Fluorspa"; - default: - return veinType.name; - } - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/Tags.java b/src/main/java/com/sinthoras/visualprospecting/Tags.java index a1525f03..7868acb5 100644 --- a/src/main/java/com/sinthoras/visualprospecting/Tags.java +++ b/src/main/java/com/sinthoras/visualprospecting/Tags.java @@ -5,10 +5,10 @@ public class Tags { // GRADLETOKEN_* will be replaced by your configuration values at build time - public static final String MODID = "GRADLETOKEN_MODID"; - public static final String MODNAME = "GRADLETOKEN_MODNAME"; - public static final String VERSION = "GRADLETOKEN_VERSION"; - public static final String GROUPNAME = "GRADLETOKEN_GROUPNAME"; + public static final String MODID = "visualprospecting"; + public static final String MODNAME = "VisualProspecting"; + public static final String VERSION = Version.VERSION; + public static final String GROUPNAME = "com.sinthoras.visualprospecting"; public static final String VISUALPROSPECTING_DIR = MODID + "/"; public static final String CLIENT_DIR = VISUALPROSPECTING_DIR + "client/"; @@ -29,4 +29,5 @@ public class Tags { public static final String PROSPECTION_NUMBER_OF_UNDERGROUND_FLUID = "prospection_num_fluids"; public static final String PROSPECTORSLOG_AUTHOR = "log_author"; public static final String PROSPECTORSLOG_AUTHOR_ID = "log_author_id"; + public static final int overworldId = 0; } diff --git a/src/main/java/com/sinthoras/visualprospecting/Utils.java b/src/main/java/com/sinthoras/visualprospecting/Utils.java index f5fe456a..1e037e84 100644 --- a/src/main/java/com/sinthoras/visualprospecting/Utils.java +++ b/src/main/java/com/sinthoras/visualprospecting/Utils.java @@ -1,14 +1,12 @@ package com.sinthoras.visualprospecting; -import com.google.gson.Gson; -import com.google.gson.reflect.TypeToken; -import com.sinthoras.visualprospecting.hooks.HooksClient; - -import cpw.mods.fml.common.Loader; -import net.minecraft.client.Minecraft; -import net.minecraft.launchwrapper.Launch; - -import java.io.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.Writer; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; @@ -18,43 +16,38 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; import java.util.stream.Collectors; +import java.util.stream.Stream; -public class Utils { +import javax.annotation.Nullable; - public static boolean isDevelopmentEnvironment() { - return (boolean) Launch.blackboard.get("fml.deobfuscatedEnvironment"); - } +import net.minecraft.client.Minecraft; +import net.minecraft.nbt.CompressedStreamTools; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.IChatComponent; +import net.minecraft.world.storage.ThreadedFileIOBase; - public static boolean isBartworksInstalled() { - return Loader.isModLoaded("bartworks"); - } +import org.jetbrains.annotations.NotNull; - public static boolean isNEIInstalled() { - return Loader.isModLoaded("NotEnoughItems"); - } +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import com.gtnewhorizon.gtnhlib.util.CoordinatePacker; +import com.sinthoras.visualprospecting.hooks.HooksClient; - public static boolean isTCNodeTrackerInstalled() { - return Loader.isModLoaded("tcnodetracker"); - } +import codechicken.nei.NEIClientConfig; +import codechicken.nei.SearchField; +import codechicken.nei.api.ItemFilter; +import cpw.mods.fml.common.Loader; +import gregtech.common.GTWorldgenerator; - public static boolean isJourneyMapInstalled() { - return Loader.isModLoaded("journeymap"); - } +@SuppressWarnings("ResultOfMethodCallIgnored") +public class Utils { - public static boolean isXaerosWorldMapInstalled() { - return Loader.isModLoaded("XaeroWorldMap"); - } - - public static boolean isVoxelMapInstalled() { - try { - // If a LiteLoader mod is present cannot be checked by calling Loader#isModLoaded. - // Instead, we check if the VoxelMap main class is present. - Class.forName("com.thevoxelbox.voxelmap.litemod.LiteModVoxelMap"); - return true; - } catch (Exception e) { - return false; - } + public static boolean isNavigatorInstalled() { + return Loader.isModLoaded("navigator"); } public static int coordBlockToChunk(int blockCoord) { @@ -66,15 +59,21 @@ public static int coordChunkToBlock(int chunkCoord) { } public static long chunkCoordsToKey(int chunkX, int chunkZ) { - return (((long)chunkX) << 32) | (chunkZ & 0xffffffffL); + return CoordinatePacker.pack(chunkX, 0, chunkZ); } public static int mapToCenterOreChunkCoord(final int chunkCoord) { - if(chunkCoord >= 0) { - return chunkCoord - (chunkCoord % 3) + 1; - } - else { - return chunkCoord - (chunkCoord % 3) - 1; + GTWorldgenerator.OregenPattern pattern = GTWorldgenerator.getOregenPattern(); + if (pattern == GTWorldgenerator.OregenPattern.EQUAL_SPACING) { + // new evenly spaced ore pattern + return chunkCoord - Math.floorMod(chunkCoord, 3) + 1; + } else { + // old bugged ore pattern + if (chunkCoord >= 0) { + return chunkCoord - (chunkCoord % 3) + 1; + } else { + return chunkCoord - (chunkCoord % 3) - 1; + } } } @@ -82,16 +81,12 @@ public static int mapToCornerUndergroundFluidChunkCoord(final int chunkCoord) { return chunkCoord & 0xFFFFFFF8; } - public static double journeyMapScaleToLinear(final int jzoom) { - return Math.pow(2, jzoom); - } - public static boolean isSmallOreId(short metaData) { return metaData >= VP.gregTechSmallOreMinimumMeta; } public static short oreIdToMaterialId(short metaData) { - return (short)(metaData % 1000); + return (short) (metaData % 1000); } public static boolean isLogicalClient() { @@ -99,10 +94,9 @@ public static boolean isLogicalClient() { } public static File getMinecraftDirectory() { - if(isLogicalClient()) { + if (isLogicalClient()) { return Minecraft.getMinecraft().mcDataDir; - } - else { + } else { return new File("."); } } @@ -113,94 +107,95 @@ public static File getSubDirectory(final String subdirectory) { public static void deleteDirectoryRecursively(final File targetDirectory) { try { - Files.walk(targetDirectory.toPath()) - .sorted(Comparator.reverseOrder()) - .map(Path::toFile) - .forEach(File::delete); - } - catch (IOException e) { - e.printStackTrace(); - } - } - - public static ByteBuffer readFileToBuffer(File file) { - if(file.exists() == false) { - return null; - } - try - { - final FileInputStream inputStream = new FileInputStream(file); - final FileChannel inputChannel = inputStream.getChannel(); - final ByteBuffer buffer = ByteBuffer.allocate((int) inputChannel.size()); - - inputChannel.read(buffer); - buffer.flip(); - - inputChannel.close(); - inputStream.close(); - - return buffer; - } - catch (IOException e) { - e.printStackTrace(); - return null; + try (Stream files = Files.walk(targetDirectory.toPath())) { + files.sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete); + } + } catch (IOException e) { + VP.LOG.error("Failed to delete directory recursively: {}", targetDirectory, e); } } public static Map readFileToMap(File file) { - if(file.exists() == false) { + if (!file.exists()) { return new HashMap<>(); } try { final Gson gson = new Gson(); final Reader reader = Files.newBufferedReader(file.toPath()); - final Map map = gson.fromJson(reader, new TypeToken>() { }.getType()); + final Map map = gson.fromJson(reader, new TypeToken>() {}.getType()); reader.close(); return map; - } - catch (IOException e) { - e.printStackTrace(); + } catch (IOException e) { + VP.LOG.error("Failed to read map from file: {}", file, e); return new HashMap<>(); } } public static void writeMapToFile(File file, Map map) { try { - if(file.exists()) { + if (file.exists()) { file.delete(); } final Gson gson = new Gson(); final Writer writer = Files.newBufferedWriter(file.toPath(), StandardOpenOption.CREATE_NEW); - gson.toJson(map, new TypeToken>() { }.getType(), writer); + gson.toJson(map, new TypeToken>() {}.getType(), writer); writer.close(); - } - catch (IOException e) { - e.printStackTrace(); + } catch (IOException e) { + VP.LOG.error("Failed to write map to file: {}", file, e); } } - public static void appendToFile(File file, ByteBuffer byteBuffer) { - try { - if(file.exists() == false) { + public static void writeNBT(File file, NBTTagCompound tag) { + ThreadedFileIOBase.threadedIOInstance.queueIO(() -> { + try (FileOutputStream stream = new FileOutputStream(newFile(file))) { + CompressedStreamTools.writeCompressed(tag, stream); + } catch (Exception ex) { + VP.LOG.error("Failed to write NBT to file: {}", file, ex); + } + return false; + }); + } + + public static File newFile(File file) { + if (!file.exists()) { + try { + File parent = file.getParentFile(); + if (!parent.exists()) { + parent.mkdirs(); + } file.createNewFile(); + } catch (Exception e) { + VP.LOG.error("Failed to create new file: {}", file, e); } - final FileOutputStream outputStream = new FileOutputStream(file, true); - final FileChannel outputChannel = outputStream.getChannel(); + } - outputChannel.write(byteBuffer); + return file; + } - outputChannel.close(); - outputStream.close(); + @Nullable + public static NBTTagCompound readNBT(@NotNull File file) { + if (!file.exists() || !file.isFile()) { + return null; } - catch (IOException e) { - e.printStackTrace(); + + try (InputStream stream = new FileInputStream(file)) { + return CompressedStreamTools.readCompressed(stream); + } catch (Exception ex) { + try { + return CompressedStreamTools.read(file); + } catch (Exception ex1) { + return null; + } } } - public static Map getDIMFiles(File directory) { + public static Map getLegacyDimFiles(File directory) { + if (!directory.exists()) { + return new HashMap<>(); + } + try { - final List dimensionIds = Files.walk(directory.toPath(), 1) - .filter(Files::isRegularFile) + final List dimensionIds = Files.walk(directory.toPath(), 1).filter(Files::isRegularFile) .filter(path -> path.getFileName().toString().startsWith("DIM")) .map(dimensionFolder -> Integer.parseInt(dimensionFolder.getFileName().toString().substring(3))) .collect(Collectors.toList()); @@ -213,10 +208,73 @@ public static Map getDIMFiles(File directory) { } return dimensionFiles; - } - catch (IOException e) { - e.printStackTrace(); + } catch (IOException e) { + VP.LOG.error("Failed to scan legacy dim files in: {}", directory, e); return new HashMap<>(); } } + + private static ByteBuffer readFileToBuffer(File file) { + if (!file.exists()) { + return null; + } + try { + final FileInputStream inputStream = new FileInputStream(file); + final FileChannel inputChannel = inputStream.getChannel(); + final ByteBuffer buffer = ByteBuffer.allocate((int) inputChannel.size()); + + inputChannel.read(buffer); + buffer.flip(); + + inputChannel.close(); + inputStream.close(); + + return buffer; + } catch (IOException e) { + VP.LOG.error("Failed to read file to buffer: {}", file, e); + return null; + } + } + + public static void sendChatMessage(IChatComponent msg, boolean italic, EnumChatFormatting color) { + msg.getChatStyle().setItalic(italic).setColor(color); + Minecraft.getMinecraft().thePlayer.addChatMessage(msg); + } + + public static @Nullable Pattern getNEISearchPattern() { + final String searchString = NEIClientConfig.getSearchExpression().toLowerCase(); + if (!SearchField.searchInventories() || searchString.isEmpty()) { + return null; + } + + return getSearchPattern(searchString); + } + + public static @Nullable Pattern getSearchPattern(@NotNull String searchString) { + if (searchString.isEmpty()) return null; + final String unquotedRegex = SearchField.getPattern(searchString).pattern().replace("\\Q", "") + .replace("\\E", ""); + try { + return Pattern.compile(unquotedRegex, Pattern.MULTILINE | Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE); + } catch (PatternSyntaxException e) { + return null; + } + } + + public static @Nullable ItemFilter getNEISearchItemFilter() { + final String searchString = NEIClientConfig.getSearchExpression(); + if (!SearchField.searchInventories() || searchString.isEmpty()) { + return null; + } + return getItemFilter(searchString); + } + + public static @Nullable ItemFilter getItemFilter(@NotNull String searchString) { + if (searchString.isEmpty()) return null; + try { + return SearchField.getFilter(searchString); + } catch (Exception e) { + return null; + } + } } diff --git a/src/main/java/com/sinthoras/visualprospecting/VP.java b/src/main/java/com/sinthoras/visualprospecting/VP.java index 2a43efa2..83d579e8 100644 --- a/src/main/java/com/sinthoras/visualprospecting/VP.java +++ b/src/main/java/com/sinthoras/visualprospecting/VP.java @@ -1,21 +1,18 @@ package com.sinthoras.visualprospecting; -import net.minecraft.client.settings.KeyBinding; +import java.util.Random; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper; -import java.util.Random; - public class VP { public static SimpleNetworkWrapper network; - public static KeyBinding keyAction; - public static final Random randomGeneration = new Random(); - private static Logger LOG = LogManager.getLogger(Tags.MODID); + public static final Logger LOG = LogManager.getLogger(Tags.MODID); public static final int gregTechSmallOreMinimumMeta = 16000; public static final int minecraftWorldHeight = 256; public static final int chunksPerRegionFileX = 32; @@ -28,26 +25,5 @@ public class VP { public static final int chunkHeight = 16; public static final int chunkDepth = 16; public static final int undergroundFluidChunkProspectingBlockRadius = undergroundFluidSizeChunkX * chunkWidth; - public static int uploadSizePerPacketInBytes = 30000; // Larger then 32kB will kick the player! - - - public static void debug(String message) { - VP.LOG.debug(formatMessage(message)); - } - - public static void info(String message) { - VP.LOG.info(formatMessage(message)); - } - - public static void warn(String message) { - VP.LOG.warn(formatMessage(message)); - } - - public static void error(String message) { - VP.LOG.error(formatMessage(message)); - } - - private static String formatMessage(String message) { - return "[" + Tags.MODNAME + "] " + message; - } + public static int uploadSizePerPacketInBytes = 30000; // Larger than 32kB will kick the player! } diff --git a/src/main/java/com/sinthoras/visualprospecting/VPMod.java b/src/main/java/com/sinthoras/visualprospecting/VPMod.java index ac92350c..b2d2ff08 100644 --- a/src/main/java/com/sinthoras/visualprospecting/VPMod.java +++ b/src/main/java/com/sinthoras/visualprospecting/VPMod.java @@ -1,68 +1,84 @@ package com.sinthoras.visualprospecting; import com.sinthoras.visualprospecting.hooks.HooksShared; +import com.sinthoras.visualprospecting.integration.gregtech.GTScannerHandler; +import com.sinthoras.visualprospecting.integration.gregtech.VeinDatabase; + import cpw.mods.fml.common.Mod; import cpw.mods.fml.common.SidedProxy; -import cpw.mods.fml.common.event.*; - +import cpw.mods.fml.common.event.FMLInitializationEvent; +import cpw.mods.fml.common.event.FMLLoadCompleteEvent; +import cpw.mods.fml.common.event.FMLPostInitializationEvent; +import cpw.mods.fml.common.event.FMLPreInitializationEvent; +import cpw.mods.fml.common.event.FMLServerAboutToStartEvent; +import cpw.mods.fml.common.event.FMLServerStartedEvent; +import cpw.mods.fml.common.event.FMLServerStartingEvent; +import cpw.mods.fml.common.event.FMLServerStoppedEvent; +import cpw.mods.fml.common.event.FMLServerStoppingEvent; +import gregtech.crossmod.visualprospecting.VisualProspectingDatabase; -@Mod(modid = Tags.MODID, version = Tags.VERSION, name = Tags.MODNAME, acceptedMinecraftVersions = "[1.7.10]", dependencies = "required-after:spongemixins@[1.1.0,);") +@Mod( + modid = Tags.MODID, + version = Tags.VERSION, + name = Tags.MODNAME, + acceptedMinecraftVersions = "[1.7.10]", + dependencies = "required-after:gregtech;" + "required-after:NotEnoughItems;" + + "required-after:gtnhlib;" + + "after:navigator;") public class VPMod { - @SidedProxy(clientSide= Tags.GROUPNAME + ".hooks.HooksClient", serverSide= Tags.GROUPNAME + ".hooks.HooksShared") + @SidedProxy(clientSide = Tags.GROUPNAME + ".hooks.HooksClient", serverSide = Tags.GROUPNAME + ".hooks.HooksShared") public static HooksShared proxy; @Mod.EventHandler // preInit "Run before anything else. Read your config, create blocks, items, // etc, and register them with the GameRegistry." public void fmlLifeCycleEvent(FMLPreInitializationEvent event) { - VP.debug("Registered sided proxy for: " + (Utils.isLogicalClient() ? "Client" : "Dedicated server")); - VP.debug("preInit()"+event.getModMetadata().name); proxy.fmlLifeCycleEvent(event); } @Mod.EventHandler // load "Do your mod setup. Build whatever data structures you care about. Register recipes." public void fmlLifeCycleEvent(FMLInitializationEvent event) { - VP.debug("init()"); proxy.fmlLifeCycleEvent(event); } @Mod.EventHandler // postInit "Handle interaction with other mods, complete your setup based on this." public void fmlLifeCycle(FMLPostInitializationEvent event) { - VP.debug("postInit()"); + VisualProspectingDatabase.registerDatabase(new VeinDatabase()); + GTScannerHandler.onPostLoad(); + proxy.fmlLifeCycleEvent(event); + } + + @Mod.EventHandler + public void fmlLifeCycle(FMLLoadCompleteEvent event) { proxy.fmlLifeCycleEvent(event); } @Mod.EventHandler public void fmlLifeCycle(FMLServerAboutToStartEvent event) { - VP.debug("Server about to start"); proxy.fmlLifeCycleEvent(event); } @Mod.EventHandler // register server commands in this event handler public void fmlLifeCycle(FMLServerStartingEvent event) { - VP.debug("Server starting"); proxy.fmlLifeCycleEvent(event); } @Mod.EventHandler public void fmlLifeCycle(FMLServerStartedEvent event) { - VP.debug("Server started"); proxy.fmlLifeCycleEvent(event); } @Mod.EventHandler public void fmlLifeCycle(FMLServerStoppingEvent event) { - VP.debug("Server stopping"); proxy.fmlLifeCycleEvent(event); } @Mod.EventHandler public void fmlLifeCycle(FMLServerStoppedEvent event) { - VP.debug("Server stopped"); proxy.fmlLifeCycleEvent(event); } } diff --git a/src/main/java/com/sinthoras/visualprospecting/VisualProspecting_API.java b/src/main/java/com/sinthoras/visualprospecting/VisualProspecting_API.java index ca80959b..2856c135 100644 --- a/src/main/java/com/sinthoras/visualprospecting/VisualProspecting_API.java +++ b/src/main/java/com/sinthoras/visualprospecting/VisualProspecting_API.java @@ -1,30 +1,23 @@ package com.sinthoras.visualprospecting; +import java.util.Collections; +import java.util.List; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.world.World; + import com.sinthoras.visualprospecting.database.ClientCache; import com.sinthoras.visualprospecting.database.OreVeinPosition; import com.sinthoras.visualprospecting.database.ServerCache; import com.sinthoras.visualprospecting.database.UndergroundFluidPosition; -import com.sinthoras.visualprospecting.integration.journeymap.JourneyMapState; -import com.sinthoras.visualprospecting.integration.journeymap.buttons.LayerButton; -import com.sinthoras.visualprospecting.integration.journeymap.render.LayerRenderer; -import com.sinthoras.visualprospecting.integration.model.MapState; -import com.sinthoras.visualprospecting.integration.model.buttons.ButtonManager; -import com.sinthoras.visualprospecting.integration.model.layers.LayerManager; -import com.sinthoras.visualprospecting.integration.model.layers.OreVeinLayerManager; -import com.sinthoras.visualprospecting.integration.model.layers.UndergroundFluidLayerManager; -import com.sinthoras.visualprospecting.integration.xaeroworldmap.XaeroWorldMapState; -import com.sinthoras.visualprospecting.network.ProspectingNotification; +import com.sinthoras.visualprospecting.database.VeinSource; +import com.sinthoras.visualprospecting.database.veintypes.VeinTypeCaching; +import com.sinthoras.visualprospecting.network.VeinDepletionMessage; +import com.sinthoras.visualprospecting.teams.TeamProspectionDispatcher; + import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.world.World; - -import java.util.Collections; -import java.util.List; - -import static com.sinthoras.visualprospecting.Utils.isJourneyMapInstalled; -import static com.sinthoras.visualprospecting.Utils.isXaerosWorldMapInstalled; @SuppressWarnings("unused") public class VisualProspecting_API { @@ -32,100 +25,49 @@ public class VisualProspecting_API { @SideOnly(Side.CLIENT) public static class LogicalClient { - // Register the logical button - public static void registerCustomButtonManager(ButtonManager customManager) { - MapState.instance.buttons.add(customManager); - } - - // Register the logical layer - public static void registerCustomLayer(LayerManager customLayer) { - MapState.instance.layers.add(customLayer); - } - - // Register visualization for logical button in JourneyMap - public static void registerJourneyMapButton(LayerButton customButton) { - if(isJourneyMapInstalled()) { - JourneyMapState.instance.buttons.add(customButton); - } - } - - // Register visualization for logical button in Xaero's World Map - public static void registerXaeroMapButton(com.sinthoras.visualprospecting.integration.xaeroworldmap.buttons.LayerButton customButton) { - if(isXaerosWorldMapInstalled()) { - XaeroWorldMapState.instance.buttons.add(customButton); - } - } - - // Add the JourneyMap renderer for a layer - public static void registerJourneyMapRenderer(LayerRenderer customRenderer) { - if(isJourneyMapInstalled()) { - JourneyMapState.instance.renderers.add(customRenderer); - } - } - - // Add the Xaero's World Map renderer for a layer - public static void registerXaeroMapRenderer(com.sinthoras.visualprospecting.integration.xaeroworldmap.renderers.LayerRenderer customRenderer) { - if(isXaerosWorldMapInstalled()) { - XaeroWorldMapState.instance.renderers.add(customRenderer); - } - } - - public static void openJourneyForOreVeinsMapAt(int blockX, int blockZ) { - if (isJourneyMapInstalled()) { - OreVeinLayerManager.instance.activateLayer(); - JourneyMapState.instance.openJourneyMapAt(blockX, blockZ); - } - } - - public static void openJourneyForOreVeinsMapAt(int blockX, int blockZ, int zoom) { - if (isJourneyMapInstalled()) { - OreVeinLayerManager.instance.activateLayer(); - JourneyMapState.instance.openJourneyMapAt(blockX, blockZ, zoom); - } - } - - public static void openJourneyMapForUndergroundFluidsAt(int blockX, int blockZ) { - if (isJourneyMapInstalled()) { - UndergroundFluidLayerManager.instance.activateLayer(); - JourneyMapState.instance.openJourneyMapAt(blockX, blockZ); - } - } - - public static void openJourneyMapForUndergroundFluidsAt(int blockX, int blockZ, int zoom) { - if (isJourneyMapInstalled()) { - UndergroundFluidLayerManager.instance.activateLayer(); - JourneyMapState.instance.openJourneyMapAt(blockX, blockZ, zoom); - } - } - // This mechanic is limited to blocks the player can touch - public static void triggerProspectingForOreBlock(EntityPlayer player, World world, int blockX, int blockY, int blockZ) { + public static void triggerProspectingForOreBlock(EntityPlayer player, World world, int blockX, int blockY, + int blockZ) { ClientCache.instance.onOreInteracted(world, blockX, blockY, blockZ, player); } public static OreVeinPosition getOreVein(int dimensionId, int blockX, int blockZ) { - return ClientCache.instance.getOreVein(dimensionId, Utils.coordBlockToChunk(blockX), Utils.coordBlockToChunk(blockZ)); + return ClientCache.instance + .getOreVein(dimensionId, Utils.coordBlockToChunk(blockX), Utils.coordBlockToChunk(blockZ)); } public static UndergroundFluidPosition getUndergroundFluid(int dimensionId, int blockX, int blockZ) { - return ClientCache.instance.getUndergroundFluid(dimensionId, Utils.coordBlockToChunk(blockX), Utils.coordBlockToChunk(blockZ)); + return ClientCache.instance + .getUndergroundFluid(dimensionId, Utils.coordBlockToChunk(blockX), Utils.coordBlockToChunk(blockZ)); } public static void setOreVeinDepleted(int dimensionId, int blockX, int blockZ) { - final OreVeinPosition oreVeinPosition = ClientCache.instance.getOreVein(dimensionId, Utils.coordBlockToChunk(blockX), Utils.coordBlockToChunk(blockZ)); - if(oreVeinPosition.isDepleted() == false) { - oreVeinPosition.toggleDepleted(); - } + final OreVeinPosition oreVeinPosition = ClientCache.instance + .getOreVein(dimensionId, Utils.coordBlockToChunk(blockX), Utils.coordBlockToChunk(blockZ)); + if (oreVeinPosition == OreVeinPosition.EMPTY_VEIN) return; + if (oreVeinPosition.isDepleted()) return; + oreVeinPosition.toggleDepleted(); ClientCache.instance.putOreVeins(Collections.singletonList(oreVeinPosition)); + VP.network.sendToServer( + new VeinDepletionMessage(dimensionId, oreVeinPosition.chunkX, oreVeinPosition.chunkZ, true)); } public static void toggleOreVeinDepleted(OreVeinPosition oreVeinPosition) { - oreVeinPosition = ClientCache.instance.getOreVein(oreVeinPosition.dimensionId, oreVeinPosition.chunkX, oreVeinPosition.chunkZ); + oreVeinPosition = ClientCache.instance + .getOreVein(oreVeinPosition.dimensionId, oreVeinPosition.chunkX, oreVeinPosition.chunkZ); + if (oreVeinPosition == OreVeinPosition.EMPTY_VEIN) return; oreVeinPosition.toggleDepleted(); ClientCache.instance.putOreVeins(Collections.singletonList(oreVeinPosition)); + VP.network.sendToServer( + new VeinDepletionMessage( + oreVeinPosition.dimensionId, + oreVeinPosition.chunkX, + oreVeinPosition.chunkZ, + oreVeinPosition.isDepleted())); } - public static void putProspectionResults(List oreVeins, List undergroundFluids) { + public static void putProspectionResults(List oreVeins, + List undergroundFluids) { ClientCache.instance.putOreVeins(oreVeins); ClientCache.instance.putUndergroundFluids(undergroundFluids); } @@ -134,7 +76,10 @@ public static void putProspectionResults(List oreVeins, List oreVeins, List undergroundFluids) { - // Skip networking if in single player - if(Utils.isLogicalClient()) { - ClientCache.instance.putOreVeins(oreVeins); - ClientCache.instance.putUndergroundFluids(undergroundFluids); - } - else { - VP.network.sendTo(new ProspectingNotification(oreVeins, undergroundFluids), player); - } + public static void sendProspectionResultsToClient(EntityPlayerMP player, List oreVeins, + List undergroundFluids) { + TeamProspectionDispatcher.deliverProspectingResults(player, oreVeins, undergroundFluids); } - public static List prospectOreVeinsWithinRadius(int dimensionId, int blockX, int blockZ, int blockRadius) { + public static List prospectOreVeinsWithinRadius(int dimensionId, int blockX, int blockZ, + int blockRadius) { return ServerCache.instance.prospectOreBlockRadius(dimensionId, blockX, blockZ, blockRadius); } - public static List prospectUndergroundFluidsWithingRadius(World world, int blockX, int blockZ, int blockRadius) { + public static List prospectUndergroundFluidsWithingRadius(World world, int blockX, + int blockZ, int blockRadius) { return ServerCache.instance.prospectUndergroundFluidBlockRadius(world, blockX, blockZ, blockRadius); } } diff --git a/src/main/java/com/sinthoras/visualprospecting/commands/CommandHelpers.java b/src/main/java/com/sinthoras/visualprospecting/commands/CommandHelpers.java new file mode 100644 index 00000000..829551bc --- /dev/null +++ b/src/main/java/com/sinthoras/visualprospecting/commands/CommandHelpers.java @@ -0,0 +1,99 @@ +package com.sinthoras.visualprospecting.commands; + +import static com.gtnewhorizon.gtnhlib.util.CommandUtils.argument; + +import java.util.concurrent.CompletableFuture; + +import net.minecraft.command.ICommandSender; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.EnumChatFormatting; + +import com.gtnewhorizon.gtnhlib.teams.Team; +import com.gtnewhorizon.gtnhlib.teams.TeamManager; +import com.mojang.brigadier.Command; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import com.sinthoras.visualprospecting.teams.TeamProspectionData; + +public final class CommandHelpers { + + public static final int SUCCESS = Command.SINGLE_SUCCESS; + public static final int FAILURE = 0; + + private CommandHelpers() {} + + public static RequiredArgumentBuilder playerNameArg(String name) { + return argument(name, StringArgumentType.word()).suggests(CommandHelpers::suggestOnlinePlayers); + } + + public static EntityPlayerMP requireOnlinePlayer(ICommandSender sender, String name) { + EntityPlayerMP target = MinecraftServer.getServer().getConfigurationManager().func_152612_a(name); + if (target == null) { + error(sender, "visualprospecting.command.player_offline", name); + } + return target; + } + + public static Team requireTeam(ICommandSender sender, EntityPlayerMP player) { + Team team = TeamManager.getTeamByPlayer(player.getUniqueID()); + if (team == null) { + error(sender, "visualprospecting.command.vp.team.no_team", player.getCommandSenderName()); + } + return team; + } + + public static TeamProspectionData requireTeamData(ICommandSender sender, Team team) { + TeamProspectionData data = (TeamProspectionData) team.getData(TeamProspectionData.DATA_KEY); + if (data == null) { + error(sender, "visualprospecting.command.vp.team.no_data"); + } + return data; + } + + public static MinecraftServer requireServer(ICommandSender sender) { + MinecraftServer server = MinecraftServer.getServer(); + if (server == null) { + error(sender, "visualprospecting.command.vpadmin.servercache.no_server"); + } + return server; + } + + public static int sendUsage(CommandContext ctx, String translationKey) { + return plain(ctx.getSource(), translationKey); + } + + public static int plain(ICommandSender sender, String translationKey, Object... args) { + sender.addChatMessage(new ChatComponentTranslation(translationKey, args)); + return SUCCESS; + } + + public static int success(ICommandSender sender, String transKey, Object... args) { + ChatComponentTranslation msg = new ChatComponentTranslation(transKey, args); + msg.getChatStyle().setColor(EnumChatFormatting.GREEN); + sender.addChatMessage(msg); + return SUCCESS; + } + + public static int error(ICommandSender sender, String transKey, Object... args) { + ChatComponentTranslation msg = new ChatComponentTranslation(transKey, args); + msg.getChatStyle().setColor(EnumChatFormatting.RED); + sender.addChatMessage(msg); + return FAILURE; + } + + private static CompletableFuture suggestOnlinePlayers(CommandContext ctx, + SuggestionsBuilder builder) { + String remaining = builder.getRemaining().toLowerCase(); + for (String name : MinecraftServer.getServer().getAllUsernames()) { + if (name.toLowerCase().startsWith(remaining)) { + builder.suggest(name); + } + } + return builder.buildFuture(); + } +} diff --git a/src/main/java/com/sinthoras/visualprospecting/database/ResetClientCacheCommand.java b/src/main/java/com/sinthoras/visualprospecting/commands/ResetClientCacheCommand.java similarity index 72% rename from src/main/java/com/sinthoras/visualprospecting/database/ResetClientCacheCommand.java rename to src/main/java/com/sinthoras/visualprospecting/commands/ResetClientCacheCommand.java index 2d581bbb..6cc48463 100644 --- a/src/main/java/com/sinthoras/visualprospecting/database/ResetClientCacheCommand.java +++ b/src/main/java/com/sinthoras/visualprospecting/commands/ResetClientCacheCommand.java @@ -1,4 +1,4 @@ -package com.sinthoras.visualprospecting.database; +package com.sinthoras.visualprospecting.commands; import net.minecraft.client.resources.I18n; import net.minecraft.command.CommandBase; @@ -6,22 +6,25 @@ import net.minecraft.util.ChatComponentTranslation; import net.minecraft.util.IChatComponent; +import com.sinthoras.visualprospecting.database.ClientCache; + public class ResetClientCacheCommand extends CommandBase { @Override public String getCommandName() { - return "visualprospectingresetprogress"; + return "vp_client_cache_reset"; } @Override public String getCommandUsage(ICommandSender sender) { - return I18n.format("visualprospecting.resetprogress.command"); + return I18n.format("visualprospecting.command.client.resetcache.usage"); } @Override public void processCommand(ICommandSender sender, String[] parameters) { ClientCache.instance.resetPlayerProgression(); - final IChatComponent confirmation = new ChatComponentTranslation("visualprospecting.resetprogress.confirmation"); + final IChatComponent confirmation = new ChatComponentTranslation( + "visualprospecting.command.client.resetcache.confirmation"); confirmation.getChatStyle().setItalic(true); sender.addChatMessage(confirmation); } diff --git a/src/main/java/com/sinthoras/visualprospecting/commands/VPAdminCommand.java b/src/main/java/com/sinthoras/visualprospecting/commands/VPAdminCommand.java new file mode 100644 index 00000000..7a6a52ff --- /dev/null +++ b/src/main/java/com/sinthoras/visualprospecting/commands/VPAdminCommand.java @@ -0,0 +1,70 @@ +package com.sinthoras.visualprospecting.commands; + +import static com.gtnewhorizon.gtnhlib.util.CommandUtils.literal; +import static com.sinthoras.visualprospecting.commands.CommandHelpers.sendUsage; + +import com.gtnewhorizon.gtnhlib.brigadier.BrigadierApi; +import com.sinthoras.visualprospecting.commands.admin.VPAdminCacheCommand; +import com.sinthoras.visualprospecting.commands.admin.VPAdminDebugCommand; +import com.sinthoras.visualprospecting.commands.admin.VPAdminTeamCommand; + +/** + * {@code /vp_admin} command, available to ops and from console. + */ +public final class VPAdminCommand { + + private VPAdminCommand() {} + + // spotless:off + public static void register() { + BrigadierApi.getCommandDispatcher().register(literal("vp_admin") + // Command root + .requires(src -> src.canCommandSenderUseCommand(2, "vp_admin")) + .executes(ctx -> sendUsage(ctx, "visualprospecting.command.vpadmin.root.usage")) + + // Tree for /vp_admin team + .then(literal("team") + .executes(ctx -> sendUsage(ctx, "visualprospecting.command.vpadmin.team.usage")) + + .then(literal("info") + .executes(ctx -> sendUsage(ctx, "visualprospecting.command.vpadmin.team.info.usage")) + .then(CommandHelpers.playerNameArg(VPAdminTeamCommand.ARG_PLAYER) + .executes(ctx -> VPAdminTeamCommand.cmdInfo(ctx, false)) + .then(literal("detailed").executes(ctx -> VPAdminTeamCommand.cmdInfo(ctx, true))))) + + .then(literal("upload") + .executes(ctx -> sendUsage(ctx, "visualprospecting.command.vpadmin.team.upload.usage")) + .then(CommandHelpers.playerNameArg(VPAdminTeamCommand.ARG_PLAYER) + .executes(VPAdminTeamCommand::cmdUpload))) + + .then(literal("clear") + .executes(ctx -> sendUsage(ctx, "visualprospecting.command.vpadmin.team.clear.usage")) + .then(CommandHelpers.playerNameArg(VPAdminTeamCommand.ARG_PLAYER) + .executes(VPAdminTeamCommand::cmdClear))) + ) + + // Tree for /vp_admin servercache + .then(literal("servercache") + .requires(src -> src.canCommandSenderUseCommand(4, "vp_admin")) + .executes(ctx -> sendUsage(ctx, "visualprospecting.command.vpadmin.servercache.usage")) + + .then(literal("rebuild") + .executes(ctx -> sendUsage(ctx, "visualprospecting.command.vpadmin.servercache.rebuild.usage")) + + .then(literal("all") + .executes(VPAdminCacheCommand::cmdRebuildAll)) + .then(literal("spawn") + .executes(VPAdminCacheCommand::cmdRebuildSpawn))) + ) + + // Tree for /vp_admin debug + .then(literal("debug") + .requires(src -> src.canCommandSenderUseCommand(4, "vp_admin")) + .executes(ctx -> sendUsage(ctx, "visualprospecting.command.vpadmin.debug.usage")) + + .then(literal("vein").executes(VPAdminDebugCommand::cmdDebugOreVein)) + ) + ); + } + // spotless:on +} diff --git a/src/main/java/com/sinthoras/visualprospecting/commands/VPCommand.java b/src/main/java/com/sinthoras/visualprospecting/commands/VPCommand.java new file mode 100644 index 00000000..71138a4d --- /dev/null +++ b/src/main/java/com/sinthoras/visualprospecting/commands/VPCommand.java @@ -0,0 +1,28 @@ +package com.sinthoras.visualprospecting.commands; + +import static com.gtnewhorizon.gtnhlib.util.CommandUtils.literal; +import static com.sinthoras.visualprospecting.commands.CommandHelpers.sendUsage; + +import com.gtnewhorizon.gtnhlib.brigadier.BrigadierApi; +import com.sinthoras.visualprospecting.commands.common.VPTeamCommand; + +/** + * {@code /vp} command, available to all players. + */ +public final class VPCommand { + + private VPCommand() {} + + // spotless:off + public static void register() { + BrigadierApi.getCommandDispatcher().register(literal("vp") + .executes(ctx -> sendUsage(ctx, "visualprospecting.command.vp.root.usage")) + + .then(literal("team").executes(ctx -> sendUsage(ctx, "visualprospecting.command.vp.team.usage")) + + .then(literal("info").executes(ctx -> VPTeamCommand.cmdInfo(ctx, false)) + .then(literal("detailed").executes(ctx -> VPTeamCommand.cmdInfo(ctx, true))) + ))); + } + // spotless:on +} diff --git a/src/main/java/com/sinthoras/visualprospecting/commands/admin/VPAdminCacheCommand.java b/src/main/java/com/sinthoras/visualprospecting/commands/admin/VPAdminCacheCommand.java new file mode 100644 index 00000000..45d62159 --- /dev/null +++ b/src/main/java/com/sinthoras/visualprospecting/commands/admin/VPAdminCacheCommand.java @@ -0,0 +1,52 @@ +package com.sinthoras.visualprospecting.commands.admin; + +import static com.sinthoras.visualprospecting.commands.CommandHelpers.requireServer; + +import java.io.IOException; +import java.util.zip.DataFormatException; + +import net.minecraft.command.ICommandSender; +import net.minecraft.server.MinecraftServer; + +import com.mojang.brigadier.context.CommandContext; +import com.sinthoras.visualprospecting.VP; +import com.sinthoras.visualprospecting.commands.CommandHelpers; +import com.sinthoras.visualprospecting.database.cachebuilder.WorldAnalysis; + +public class VPAdminCacheCommand { + + private VPAdminCacheCommand() {} + + public static int cmdRebuildAll(CommandContext ctx) { + return rebuild(ctx.getSource(), "rebuild", (world, server) -> world.cacheVeins()); + } + + public static int cmdRebuildSpawn(CommandContext ctx) { + return rebuild( + ctx.getSource(), + "rebuildspawn", + (world, server) -> world.cacheOverworldSpawnVeins(server.getEntityWorld().getSpawnPoint())); + } + + private static int rebuild(ICommandSender sender, String kind, RebuildAction action) { + MinecraftServer server = requireServer(sender); + if (server == null) return CommandHelpers.FAILURE; + + String prefix = "visualprospecting.command.vpadmin.servercache." + kind; + CommandHelpers.plain(sender, prefix + ".start"); + try { + WorldAnalysis world = new WorldAnalysis(server.getEntityWorld().getSaveHandler().getWorldDirectory()); + action.run(world, server); + } catch (IOException | DataFormatException e) { + VP.LOG.error("Could not rebuild vein cache ({})", kind, e); + return CommandHelpers.error(sender, prefix + ".failure"); + } + return CommandHelpers.success(sender, prefix + ".confirmation"); + } + + @FunctionalInterface + private interface RebuildAction { + + void run(WorldAnalysis world, MinecraftServer server) throws IOException, DataFormatException; + } +} diff --git a/src/main/java/com/sinthoras/visualprospecting/commands/admin/VPAdminDebugCommand.java b/src/main/java/com/sinthoras/visualprospecting/commands/admin/VPAdminDebugCommand.java new file mode 100644 index 00000000..54a10499 --- /dev/null +++ b/src/main/java/com/sinthoras/visualprospecting/commands/admin/VPAdminDebugCommand.java @@ -0,0 +1,192 @@ +package com.sinthoras.visualprospecting.commands.admin; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +import net.minecraft.command.ICommandSender; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.IChatComponent; +import net.minecraft.util.MathHelper; +import net.minecraft.world.World; +import net.minecraft.world.chunk.Chunk; + +import com.mojang.brigadier.context.CommandContext; +import com.sinthoras.visualprospecting.Utils; +import com.sinthoras.visualprospecting.VP; +import com.sinthoras.visualprospecting.commands.CommandHelpers; +import com.sinthoras.visualprospecting.database.OreVeinPosition; +import com.sinthoras.visualprospecting.database.ServerCache; +import com.sinthoras.visualprospecting.database.cachebuilder.ChunkAnalysis; +import com.sinthoras.visualprospecting.database.cachebuilder.DetailedChunkAnalysis; +import com.sinthoras.visualprospecting.database.cachebuilder.DimensionAnalysis; +import com.sinthoras.visualprospecting.database.cachebuilder.PartiallyLoadedChunk; +import com.sinthoras.visualprospecting.database.veintypes.VeinType; +import com.sinthoras.visualprospecting.database.veintypes.VeinTypeCaching; + +import gregtech.api.interfaces.IOreMaterial; +import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; +import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; + +public class VPAdminDebugCommand { + + private static final String VEIN = "visualprospecting.command.vpadmin.debug.vein."; + private static final String ANALYZE = "visualprospecting.command.vpadmin.debug.analyze."; + + private VPAdminDebugCommand() {} + + /** + * {@code /vp_admin debug vein}: inspect the central ore chunk the sender is standing on. Prints the server cache + * record and a live output of the cache-builder analysis for that chunk. + *

+ * Limitation: Doesn't do the whole cleanUpWithNeighbors logic + */ + public static int cmdDebugOreVein(CommandContext ctx) { + ICommandSender sender = ctx.getSource(); + + if (!(sender instanceof EntityPlayerMP player)) { + return CommandHelpers.error(sender, "visualprospecting.command.console_forbidden"); + } + + final int dimensionId = player.dimension; + final int chunkX = Utils + .mapToCenterOreChunkCoord(Utils.coordBlockToChunk(MathHelper.floor_double(player.posX))); + final int chunkZ = Utils + .mapToCenterOreChunkCoord(Utils.coordBlockToChunk(MathHelper.floor_double(player.posZ))); + final String dimName = new DimensionAnalysis(dimensionId).dimensionName; + + IChatComponent header = new ChatComponentTranslation(VEIN + "header", chunkX, chunkZ, dimensionId, dimName); + header.getChatStyle().setBold(true); + sender.addChatMessage(header); + + printCacheRecord(sender, dimensionId, chunkX, chunkZ); + printDryRunAnalysis(sender, player.worldObj, dimName, dimensionId, chunkX, chunkZ); + + return CommandHelpers.SUCCESS; + } + + // Print data stored in server cache + private static void printCacheRecord(ICommandSender sender, int dimensionId, int chunkX, int chunkZ) { + final OreVeinPosition vein = ServerCache.instance.getOreVein(dimensionId, chunkX, chunkZ); + if (vein.veinType == VeinType.NO_VEIN) { + CommandHelpers.plain(sender, VEIN + "cache_none"); + return; + } + CommandHelpers.plain(sender, VEIN + "type", vein.veinType.name, vein.veinType.getVeinName()); + CommandHelpers.plain(sender, VEIN + "source", vein.getSource()); + } + + // Run the cache-builder analysis on the live chunk (output only, nothing is written to cache) + private static void printDryRunAnalysis(ICommandSender sender, World world, String dimName, int dimensionId, + int chunkX, int chunkZ) { + CommandHelpers.plain(sender, ANALYZE + "title"); + + final PartiallyLoadedChunk chunk; + try { + chunk = loadLiveChunk(world, chunkX, chunkZ); + } catch (Exception e) { + VP.LOG.error( + "/vp_admin debug vein: failed to read live chunk at {}, {} in dim {}", + chunkX, + chunkZ, + dimensionId, + e); + CommandHelpers.error(sender, ANALYZE + "error", String.valueOf(e)); + return; + } + + // Raw ore tally + final Reference2IntOpenHashMap oreCounts = new Reference2IntOpenHashMap<>(); + final int[] minY = { Integer.MAX_VALUE }; + final int[] maxY = { Integer.MIN_VALUE }; + chunk.forEachOre((x, y, z, info) -> { + oreCounts.addTo(info.material, 1); + if (y < minY[0]) minY[0] = y; + if (y > maxY[0]) maxY[0] = y; + }); + + if (oreCounts.isEmpty()) { + CommandHelpers.plain(sender, ANALYZE + "no_ores"); + return; + } + + int total = 0; + for (var entry : oreCounts.reference2IntEntrySet()) { + total += entry.getIntValue(); + } + CommandHelpers.plain(sender, ANALYZE + "stats", total, oreCounts.size(), minY[0], maxY[0]); + + final List materials = new ArrayList<>(oreCounts.keySet()); + materials.sort(Comparator.comparingInt(oreCounts::getInt).reversed()); + for (IOreMaterial ore : materials) { + CommandHelpers.plain(sender, ANALYZE + "ore_line", ore.getLocalizedName(), oreCounts.getInt(ore)); + } + + final IOreMaterial dominant = materials.get(0); + CommandHelpers.plain(sender, ANALYZE + "dominant", dominant.getLocalizedName(), oreCounts.getInt(dominant)); + + if (dimName != null && !dimName.isEmpty() && !VeinTypeCaching.hasVeinsInDimension(dimName)) { + CommandHelpers.plain(sender, ANALYZE + "no_dim_veins", dimName); + } + + printCandidateReasoning(sender, oreCounts, dimName, dominant); + printResolvedVerdicts(sender, chunk, dimName, dimensionId, chunkX, chunkZ); + } + + // Mirror the inputs of DetailedChunkAnalysis.getMatchedVein() + private static void printCandidateReasoning(ICommandSender sender, Reference2IntOpenHashMap oreCounts, + String dimName, IOreMaterial dominant) { + final List candidates = new ArrayList<>(); + for (VeinType vein : VeinTypeCaching.getVeinTypesForOre(dominant)) { + if (dimName.isEmpty() || vein.getAllowedDimensions().contains(dimName)) { + candidates.add(vein); + } + } + if (candidates.isEmpty()) { + CommandHelpers.plain(sender, ANALYZE + "candidates_none"); + return; + } + + CommandHelpers.plain(sender, ANALYZE + "candidates_header", dominant.getLocalizedName(), candidates.size()); + int fullMatches = 0; + int oneMissingMatches = 0; + for (VeinType candidate : candidates) { + final boolean full = candidate.matchesWithSpecificPrimaryOrSecondary(oreCounts.keySet(), dimName, dominant); + final boolean oneMissing = candidate.matchesWithOneMissingOre(oreCounts.keySet(), dimName, dominant); + if (full) fullMatches++; + if (oneMissing) oneMissingMatches++; + CommandHelpers.plain(sender, ANALYZE + "candidate_line", candidate.name, full, oneMissing); + } + CommandHelpers.plain(sender, ANALYZE + "match_summary", fullMatches, oneMissingMatches); + } + + // Print Verdict + private static void printResolvedVerdicts(ICommandSender sender, PartiallyLoadedChunk chunk, String dimName, + int dimensionId, int chunkX, int chunkZ) { + final DetailedChunkAnalysis detailed = new DetailedChunkAnalysis(dimensionId, dimName, chunkX, chunkZ); + detailed.processMinecraftChunk(chunk); + detailed.resolve(new Long2IntOpenHashMap()); + final VeinType resolved = detailed.getResolvedVeinType(); + if (resolved == VeinType.NO_VEIN) { + CommandHelpers.plain(sender, ANALYZE + "resolved_none"); + } else { + CommandHelpers.plain(sender, ANALYZE + "resolved", resolved.name, resolved.getVeinName()); + } + + final ChunkAnalysis fast = new ChunkAnalysis(dimName); + fast.processMinecraftChunk(chunk); + if (fast.matchesSingleVein()) { + CommandHelpers.plain(sender, ANALYZE + "fastpath", fast.getMatchedVein().name); + } else { + CommandHelpers.plain(sender, ANALYZE + "fastpath_ambiguous"); + } + } + + private static PartiallyLoadedChunk loadLiveChunk(World world, int chunkX, int chunkZ) { + final Chunk live = world.getChunkFromChunkCoords(chunkX, chunkZ); + final PartiallyLoadedChunk chunk = new PartiallyLoadedChunk(); + chunk.loadFromLiveChunk(live); + return chunk; + } +} diff --git a/src/main/java/com/sinthoras/visualprospecting/commands/admin/VPAdminTeamCommand.java b/src/main/java/com/sinthoras/visualprospecting/commands/admin/VPAdminTeamCommand.java new file mode 100644 index 00000000..3e6e6142 --- /dev/null +++ b/src/main/java/com/sinthoras/visualprospecting/commands/admin/VPAdminTeamCommand.java @@ -0,0 +1,86 @@ +package com.sinthoras.visualprospecting.commands.admin; + +import net.minecraft.command.ICommandSender; +import net.minecraft.entity.player.EntityPlayerMP; + +import com.gtnewhorizon.gtnhlib.teams.Team; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.sinthoras.visualprospecting.Config; +import com.sinthoras.visualprospecting.VP; +import com.sinthoras.visualprospecting.commands.CommandHelpers; +import com.sinthoras.visualprospecting.commands.common.VPTeamCommand; +import com.sinthoras.visualprospecting.network.RequestTeamUploadMessage; +import com.sinthoras.visualprospecting.teams.TeamProspectionData; + +public class VPAdminTeamCommand { + + public static final String ARG_PLAYER = "player"; + + private VPAdminTeamCommand() {} + + public static int cmdInfo(CommandContext ctx, boolean detailed) { + ICommandSender sender = ctx.getSource(); + String playerName = StringArgumentType.getString(ctx, ARG_PLAYER); + + EntityPlayerMP target = CommandHelpers.requireOnlinePlayer(sender, playerName); + if (target == null) { + return CommandHelpers.FAILURE; + } else { + return VPTeamCommand.cmdInfo(ctx, detailed, target); + } + } + + public static int cmdClear(CommandContext ctx) { + ICommandSender sender = ctx.getSource(); + String playerName = StringArgumentType.getString(ctx, ARG_PLAYER); + + EntityPlayerMP target = CommandHelpers.requireOnlinePlayer(sender, playerName); + if (target == null) return CommandHelpers.FAILURE; + + Team team = CommandHelpers.requireTeam(sender, target); + if (team == null) return CommandHelpers.FAILURE; + + TeamProspectionData data = CommandHelpers.requireTeamData(sender, team); + if (data == null) return CommandHelpers.FAILURE; + + int veins = 0, fluids = 0; + for (int dim : data.knownDimensions()) { + veins += data.getDiscoveredVeinKeys(dim).size(); + fluids += data.getDiscoveredFluidKeys(dim).size(); + } + + data.clear(); + team.markDirty(); + + return CommandHelpers.success( + sender, + "visualprospecting.command.vpadmin.team.clear.success", + team.getTeamName(), + veins, + fluids); + } + + public static int cmdUpload(CommandContext ctx) { + ICommandSender sender = ctx.getSource(); + String playerName = StringArgumentType.getString(ctx, ARG_PLAYER); + + if (!Config.enableTeamSharing) { + return CommandHelpers.error(sender, "visualprospecting.command.sharing_disabled"); + } + + EntityPlayerMP target = CommandHelpers.requireOnlinePlayer(sender, playerName); + if (target == null) return CommandHelpers.FAILURE; + + Team team = CommandHelpers.requireTeam(sender, target); + if (team == null) return CommandHelpers.FAILURE; + + VP.network.sendTo(new RequestTeamUploadMessage(), target); + + return CommandHelpers.success( + sender, + "visualprospecting.command.vpadmin.team.upload.requested", + target.getCommandSenderName(), + team.getTeamName()); + } +} diff --git a/src/main/java/com/sinthoras/visualprospecting/commands/common/VPTeamCommand.java b/src/main/java/com/sinthoras/visualprospecting/commands/common/VPTeamCommand.java new file mode 100644 index 00000000..41a39f32 --- /dev/null +++ b/src/main/java/com/sinthoras/visualprospecting/commands/common/VPTeamCommand.java @@ -0,0 +1,123 @@ +package com.sinthoras.visualprospecting.commands.common; + +import java.util.Arrays; + +import net.minecraft.command.ICommandSender; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.IChatComponent; +import net.minecraft.world.World; +import net.minecraftforge.common.DimensionManager; + +import com.gtnewhorizon.gtnhlib.teams.Team; +import com.gtnewhorizon.gtnhlib.teams.TeamManager; +import com.mojang.brigadier.context.CommandContext; +import com.sinthoras.visualprospecting.commands.CommandHelpers; +import com.sinthoras.visualprospecting.teams.TeamProspectionData; + +import it.unimi.dsi.fastutil.ints.IntSet; + +public final class VPTeamCommand { + + private VPTeamCommand() {} + + public static int cmdInfo(CommandContext ctx, boolean detailed) { + return cmdInfo(ctx, detailed, null); + } + + public static int cmdInfo(CommandContext ctx, boolean detailed, EntityPlayerMP target) { + ICommandSender sender = ctx.getSource(); + + EntityPlayerMP subject = target; + if (subject == null) { + if (!(sender instanceof EntityPlayerMP self)) { + return CommandHelpers.error(sender, "visualprospecting.command.console_needs_player"); + } + subject = self; + } + + Team team = CommandHelpers.requireTeam(sender, subject); + if (team == null) return CommandHelpers.FAILURE; + + TeamProspectionData data = CommandHelpers.requireTeamData(sender, team); + if (data == null) return CommandHelpers.FAILURE; + + printInfoReport(sender, team, data, detailed); + return CommandHelpers.SUCCESS; + } + + private static void printInfoReport(ICommandSender sender, Team team, TeamProspectionData data, boolean detailed) { + IntSet knownDimensions = data.knownDimensions(); + + long totalVeins = 0, totalFluids = 0, totalDepleted = 0; + for (int dim : knownDimensions) { + totalVeins += data.getDiscoveredVeinKeys(dim).size(); + totalFluids += data.getDiscoveredFluidKeys(dim).size(); + totalDepleted += data.getDepletedVeinKeys(dim).size(); + } + + int memberCount = team.getMembers().size(); + int onlineCount = countOnline(team); + + IChatComponent header = new ChatComponentTranslation( + "visualprospecting.command.vp.team.info.header", + team.getTeamName(), + memberCount, + onlineCount); + header.getChatStyle().setBold(true); + sender.addChatMessage(header); + + if (knownDimensions.isEmpty()) { + IChatComponent empty = new ChatComponentTranslation("visualprospecting.command.vp.team.info.empty"); + empty.getChatStyle().setColor(EnumChatFormatting.GRAY); + sender.addChatMessage(empty); + return; + } + + sender.addChatMessage( + new ChatComponentTranslation( + "visualprospecting.command.vp.team.info.summary", + knownDimensions.size(), + totalVeins, + totalDepleted, + totalFluids)); + + if (!detailed) return; + + int[] sortedDims = knownDimensions.toIntArray(); + Arrays.sort(sortedDims); + for (int dim : sortedDims) { + int v = data.getDiscoveredVeinKeys(dim).size(); + int f = data.getDiscoveredFluidKeys(dim).size(); + int d = data.getDepletedVeinKeys(dim).size(); + if (v == 0 && f == 0) continue; + sender.addChatMessage( + new ChatComponentTranslation( + "visualprospecting.command.vp.team.info.dim_line", + dimLabel(dim), + v, + d, + f)); + } + } + + private static int countOnline(Team team) { + int[] count = { 0 }; + TeamManager.forEachOnlineTeamMember(team, p -> count[0]++); + return count[0]; + } + + private static String dimLabel(int dim) { + try { + World world = DimensionManager.getWorld(dim); + if (world != null && world.provider != null) { + String name = world.provider.getDimensionName(); + if (name != null && !name.isEmpty()) { + return dim + " (" + name + ")"; + } + } + } catch (Exception ignored) {} + return Integer.toString(dim); + } +} diff --git a/src/main/java/com/sinthoras/visualprospecting/database/ClientCache.java b/src/main/java/com/sinthoras/visualprospecting/database/ClientCache.java index ea7e23f3..80449f74 100644 --- a/src/main/java/com/sinthoras/visualprospecting/database/ClientCache.java +++ b/src/main/java/com/sinthoras/visualprospecting/database/ClientCache.java @@ -1,143 +1,177 @@ package com.sinthoras.visualprospecting.database; -import com.sinthoras.visualprospecting.*; -import com.sinthoras.visualprospecting.hooks.ProspectingNotificationEvent; -import com.sinthoras.visualprospecting.network.ProspectingRequest; -import gregtech.common.blocks.GT_TileEntity_Ores; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + import net.minecraft.client.Minecraft; import net.minecraft.client.entity.EntityClientPlayerMP; -import net.minecraft.client.resources.I18n; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.tileentity.TileEntity; import net.minecraft.util.ChatComponentTranslation; import net.minecraft.util.EnumChatFormatting; import net.minecraft.util.IChatComponent; import net.minecraft.world.World; import net.minecraftforge.common.MinecraftForge; -import java.io.File; -import java.util.ArrayList; -import java.util.List; +import org.apache.commons.io.FileUtils; + +import com.sinthoras.visualprospecting.Config; +import com.sinthoras.visualprospecting.Tags; +import com.sinthoras.visualprospecting.Utils; +import com.sinthoras.visualprospecting.VP; +import com.sinthoras.visualprospecting.hooks.ProspectingNotificationEvent; +import com.sinthoras.visualprospecting.network.ProspectingRequest; +import com.sinthoras.visualprospecting.network.VeinDepletionMessage; + +import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import gregtech.api.events.OreInteractEvent; +import gregtech.api.interfaces.IOreMaterial; +import gregtech.common.ores.OreInfo; +import gregtech.common.ores.OreManager; public class ClientCache extends WorldCache { public static final ClientCache instance = new ClientCache(); + public ClientCache() { + + } + protected File getStorageDirectory() { final EntityClientPlayerMP player = Minecraft.getMinecraft().thePlayer; - return new File(Utils.getSubDirectory(Tags.CLIENT_DIR), player.getDisplayName() + "_" + player.getPersistentID().toString()); + File oldCacheDir = new File( + Tags.CLIENT_DIR, + player.getDisplayName() + "_" + player.getPersistentID().toString()); + File newCacheDir = new File(Tags.CLIENT_DIR, player.getPersistentID().toString()); + if (oldCacheDir.exists()) { + convertOldCache(oldCacheDir, newCacheDir); + } + + return newCacheDir; } private void notifyNewOreVein(OreVeinPosition oreVeinPosition) { final String location = "(" + (oreVeinPosition.getBlockX() + 8) + "," + (oreVeinPosition.getBlockZ() + 8) + ")"; - final IChatComponent veinNotification = new ChatComponentTranslation("visualprospecting.vein.prospected", I18n.format(oreVeinPosition.veinType.name), location); - veinNotification.getChatStyle().setItalic(true); - veinNotification.getChatStyle().setColor(EnumChatFormatting.GRAY); - Minecraft.getMinecraft().thePlayer.addChatMessage(veinNotification); + Utils.sendChatMessage( + new ChatComponentTranslation( + "visualprospecting.vein.prospected", + oreVeinPosition.veinType.getVeinName(), + location), + true, + EnumChatFormatting.GRAY); final String oreNames = String.join(", ", oreVeinPosition.veinType.getOreMaterialNames()); - final IChatComponent oresNotification = new ChatComponentTranslation("visualprospecting.vein.contents", oreNames); - oresNotification.getChatStyle().setItalic(true); - oresNotification.getChatStyle().setColor(EnumChatFormatting.GRAY); - Minecraft.getMinecraft().thePlayer.addChatMessage(oresNotification); + Utils.sendChatMessage( + new ChatComponentTranslation("visualprospecting.vein.contents", oreNames), + true, + EnumChatFormatting.GRAY); } public void putOreVeins(List oreVeinPositions) { - if(oreVeinPositions.size() == 1) { - final OreVeinPosition oreVeinPosition = oreVeinPositions.get(0); - if(putOreVein(oreVeinPosition) != DimensionCache.UpdateResult.AlreadyKnown) { - MinecraftForge.EVENT_BUS.post(new ProspectingNotificationEvent.OreVein(oreVeinPosition)); - notifyNewOreVein(oreVeinPosition); - } + putOreVeins(oreVeinPositions, false); + } + + public void putOreVeins(List oreVeinPositions, boolean silent) { + int newOreVeins = 0; + for (OreVeinPosition vein : oreVeinPositions) { + if (putOreVein(vein) == DimensionCache.UpdateResult.AlreadyKnown) continue; + MinecraftForge.EVENT_BUS.post(new ProspectingNotificationEvent.OreVein(vein)); + newOreVeins++; } - else if(oreVeinPositions.size() > 1) { - int newOreVeins = 0; - for(OreVeinPosition oreVeinPosition : oreVeinPositions) { - if(putOreVein(oreVeinPosition) != DimensionCache.UpdateResult.AlreadyKnown) { - MinecraftForge.EVENT_BUS.post(new ProspectingNotificationEvent.OreVein(oreVeinPosition)); - newOreVeins++; - } - } - if(newOreVeins > 0) { - final IChatComponent oreVeinNotification = new ChatComponentTranslation("visualprospecting.veins.prospected", newOreVeins); - oreVeinNotification.getChatStyle().setItalic(true); - oreVeinNotification.getChatStyle().setColor(EnumChatFormatting.GRAY); - Minecraft.getMinecraft().thePlayer.addChatMessage(oreVeinNotification); - } + if (silent || newOreVeins == 0) return; + + if (oreVeinPositions.size() == 1) { + notifyNewOreVein(oreVeinPositions.get(0)); + } else { + Utils.sendChatMessage( + new ChatComponentTranslation("visualprospecting.veins.prospected", newOreVeins), + true, + EnumChatFormatting.GRAY); } } public void toggleOreVein(int dimensionId, int chunkX, int chunkZ) { super.toggleOreVein(dimensionId, chunkX, chunkZ); + OreVeinPosition vein = getOreVein(dimensionId, chunkX, chunkZ); + if (vein != OreVeinPosition.EMPTY_VEIN) { + VP.network.sendToServer(new VeinDepletionMessage(dimensionId, chunkX, chunkZ, vein.isDepleted())); + } } public void putUndergroundFluids(List undergroundFluids) { + putUndergroundFluids(undergroundFluids, false); + } + + public void putUndergroundFluids(List undergroundFluids, boolean silent) { int newUndergroundFluids = 0; int updatedUndergroundFluids = 0; - for(UndergroundFluidPosition undergroundFluidPosition : undergroundFluids) { + for (UndergroundFluidPosition undergroundFluidPosition : undergroundFluids) { DimensionCache.UpdateResult updateResult = putUndergroundFluids(undergroundFluidPosition); - if(updateResult == DimensionCache.UpdateResult.New) { - MinecraftForge.EVENT_BUS.post(new ProspectingNotificationEvent.UndergroundFluid(undergroundFluidPosition)); + if (updateResult == DimensionCache.UpdateResult.New) { + MinecraftForge.EVENT_BUS + .post(new ProspectingNotificationEvent.UndergroundFluid(undergroundFluidPosition)); newUndergroundFluids++; - } - else if(updateResult == DimensionCache.UpdateResult.Updated) { - MinecraftForge.EVENT_BUS.post(new ProspectingNotificationEvent.UndergroundFluid(undergroundFluidPosition)); + } else if (updateResult == DimensionCache.UpdateResult.Updated) { + MinecraftForge.EVENT_BUS + .post(new ProspectingNotificationEvent.UndergroundFluid(undergroundFluidPosition)); updatedUndergroundFluids++; } } - if(newUndergroundFluids > 0 && updatedUndergroundFluids > 0) { - final IChatComponent undergroundFluidsNotification = new ChatComponentTranslation("visualprospecting.undergroundfluid.prospected.newandupdated", newUndergroundFluids, updatedUndergroundFluids); - undergroundFluidsNotification.getChatStyle().setItalic(true); - undergroundFluidsNotification.getChatStyle().setColor(EnumChatFormatting.GRAY); - Minecraft.getMinecraft().thePlayer.addChatMessage(undergroundFluidsNotification); - } - else { - if(newUndergroundFluids > 0) { - final IChatComponent undergroundFluidsNotification = new ChatComponentTranslation("visualprospecting.undergroundfluid.prospected.onlynew", newUndergroundFluids); - undergroundFluidsNotification.getChatStyle().setItalic(true); - undergroundFluidsNotification.getChatStyle().setColor(EnumChatFormatting.GRAY); - Minecraft.getMinecraft().thePlayer.addChatMessage(undergroundFluidsNotification); - } - if(updatedUndergroundFluids > 0) { - final IChatComponent undergroundFluidsNotification = new ChatComponentTranslation("visualprospecting.undergroundfluid.prospected.onlyupdated", updatedUndergroundFluids); - undergroundFluidsNotification.getChatStyle().setItalic(true); - undergroundFluidsNotification.getChatStyle().setColor(EnumChatFormatting.GRAY); - Minecraft.getMinecraft().thePlayer.addChatMessage(undergroundFluidsNotification); - } + if (silent) return; + + IChatComponent undergroundFluidsNotification = null; + if (newUndergroundFluids > 0 && updatedUndergroundFluids > 0) { + undergroundFluidsNotification = new ChatComponentTranslation( + "visualprospecting.undergroundfluid.prospected.newandupdated", + newUndergroundFluids, + updatedUndergroundFluids); + } else if (newUndergroundFluids > 0) { + undergroundFluidsNotification = new ChatComponentTranslation( + "visualprospecting.undergroundfluid.prospected.onlynew", + newUndergroundFluids); + } else if (updatedUndergroundFluids > 0) { + undergroundFluidsNotification = new ChatComponentTranslation( + "visualprospecting.undergroundfluid.prospected.onlyupdated", + updatedUndergroundFluids); } + + if (undergroundFluidsNotification == null) return; + Utils.sendChatMessage(undergroundFluidsNotification, true, EnumChatFormatting.GRAY); } - public void onOreInteracted(World world, int blockX, int blockY, int blockZ, EntityPlayer entityPlayer) { - if(world.isRemote && Config.enableProspecting - && Minecraft.getMinecraft().thePlayer == entityPlayer) { - final TileEntity tTileEntity = world.getTileEntity(blockX, blockY, blockZ); - if (tTileEntity instanceof GT_TileEntity_Ores) { - final short oreMetaData = ((GT_TileEntity_Ores) tTileEntity).mMetaData; - if (Utils.isSmallOreId(oreMetaData) == false - && oreMetaData != 0) { - final int chunkX = Utils.coordBlockToChunk(blockX); - final int chunkZ = Utils.coordBlockToChunk(blockZ); - final OreVeinPosition oreVeinPosition = getOreVein(entityPlayer.dimension, chunkX, chunkZ); - final short materialId = Utils.oreIdToMaterialId(oreMetaData); - if(oreVeinPosition.veinType.containsOre(materialId) == false && ProspectingRequest.canSendRequest()) { - VP.network.sendToServer(new ProspectingRequest(entityPlayer.dimension, blockX, blockY, blockZ, materialId)); - } + @SubscribeEvent + public void onOreClicked(OreInteractEvent event) { + onOreInteracted(event.world, event.x, event.y, event.z, event.player); + } + + @SuppressWarnings("unchecked") + public void onOreInteracted(World world, int x, int y, int z, EntityPlayer player) { + if (world.isRemote && Config.enableProspecting && Minecraft.getMinecraft().thePlayer == player) { + try (OreInfo info = OreManager.getOreInfo(world, x, y, z)) { + if (info == null || info.isSmall) return; + + final int chunkX = Utils.coordBlockToChunk(x); + final int chunkZ = Utils.coordBlockToChunk(z); + final OreVeinPosition oreVeinPosition = getOreVein(player.dimension, chunkX, chunkZ); + + if (!oreVeinPosition.veinType.containsOre(info.material) && ProspectingRequest.canSendRequest()) { + VP.network.sendToServer(new ProspectingRequest(player.dimension, x, y, z, info.material)); } } } } public void resetPlayerProgression() { - Utils.deleteDirectoryRecursively(oreVeinCacheDirectory); - Utils.deleteDirectoryRecursively(undergroundFluidCacheDirectory); - oreVeinCacheDirectory.mkdirs(); - undergroundFluidCacheDirectory.mkdirs(); + Utils.deleteDirectoryRecursively(worldCache); + // noinspection ResultOfMethodCallIgnored + worldCache.mkdirs(); reset(); } public List getAllOreVeins() { List allOreVeins = new ArrayList<>(); - for(DimensionCache dimension : dimensions.values()) { + for (DimensionCache dimension : dimensions.values()) { allOreVeins.addAll(dimension.getAllOreVeins()); } return allOreVeins; @@ -145,9 +179,26 @@ public List getAllOreVeins() { public List getAllUndergroundFluids() { List allUndergroundFluids = new ArrayList<>(); - for(DimensionCache dimension : dimensions.values()) { + for (DimensionCache dimension : dimensions.values()) { allUndergroundFluids.addAll(dimension.getAllUndergroundFluids()); } return allUndergroundFluids; } + + private static void convertOldCache(File oldCache, File newCache) { + if (!oldCache.exists()) return; + File[] files = oldCache.listFiles(); + if (files != null && files.length > 0 && newCache.exists()) { + for (File file : files) { + if (!file.isDirectory()) continue; + try { + FileUtils.copyDirectoryToDirectory(file, newCache); + } catch (IOException ignored) {} + } + Utils.deleteDirectoryRecursively(oldCache); + } else { + // noinspection ResultOfMethodCallIgnored + oldCache.renameTo(newCache); + } + } } diff --git a/src/main/java/com/sinthoras/visualprospecting/database/DimensionCache.java b/src/main/java/com/sinthoras/visualprospecting/database/DimensionCache.java index 71d2ed1a..e6c889e5 100644 --- a/src/main/java/com/sinthoras/visualprospecting/database/DimensionCache.java +++ b/src/main/java/com/sinthoras/visualprospecting/database/DimensionCache.java @@ -1,16 +1,61 @@ package com.sinthoras.visualprospecting.database; -import com.sinthoras.visualprospecting.VP; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.nbt.NBTTagString; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidRegistry; + import com.sinthoras.visualprospecting.Utils; +import com.sinthoras.visualprospecting.VP; import com.sinthoras.visualprospecting.database.veintypes.VeinType; import com.sinthoras.visualprospecting.database.veintypes.VeinTypeCaching; -import net.minecraft.world.ChunkCoordIntPair; -import net.minecraftforge.fluids.Fluid; -import net.minecraftforge.fluids.FluidRegistry; -import java.nio.ByteBuffer; -import java.util.*; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +/** + * Cache of ore veins and underground fluids. Stored as NBT file per dimension (e.g. {@code DIM0.dat}). + *

+ * + *

+ * Root: {
+ *   "version": 3,  // int   Absence means a legacy format {@link LegacyDimensionCacheLoader}
+ *   "dim":     <dimensionId>,      // int
+ *   "ores":    <ores compound>,    // optional
+ *   "fluids":  <fluids compound>   // optional
+ * }
+ * 
+ * + * {@code ores} and {@code fluids} use a palette. + * + *
+ * ores: {
+ *   "palette":       [String, ...],  // unique vein type
+ *   "chunkX":        int[N],         // chunk X
+ *   "chunkZ":        int[N],         // chunk Z
+ *   "veinTypeIndex": int[N],         // index into palette
+ *   "depleted":      byte[N],        // bool
+ *   "source":        byte[N]         // {@link VeinSource}
+ * }
+ *
+ * fluids: {
+ *   "palette":        [String, ...], // unique fluid names
+ *   "chunkX":         int[N],
+ *   "chunkZ":         int[N],
+ *   "fluidTypeIndex": int[N],
+ *   "chunkData":      int[N * chunkDataSize], // fluid amounts for 8x8 area
+ *   "chunkDataSize":  int
+ * }
+ * 
+ */ public class DimensionCache { public enum UpdateResult { @@ -19,137 +64,395 @@ public enum UpdateResult { New } - private final Map oreChunks = new HashMap<>(); - private final Map undergroundFluids = new HashMap<>(); - private final Set changedOrNewOreChunks = new HashSet<>(); - private final Set changedOrNewUndergroundFluids = new HashSet<>(); + private static final int CURRENT_FORMAT_VERSION = 3; + + private final Long2ObjectOpenHashMap oreChunks = new Long2ObjectOpenHashMap<>(); + private final Long2ObjectOpenHashMap undergroundFluids = new Long2ObjectOpenHashMap<>(); public final int dimensionId; + private boolean isDirty = false; + private boolean preventSaving = false; + private boolean requiresOreRescan = false; public DimensionCache(int dimensionId) { this.dimensionId = dimensionId; } - public ByteBuffer saveOreChunks() { - if(changedOrNewOreChunks.isEmpty() == false) { - final ByteBuffer byteBuffer = ByteBuffer.allocate(changedOrNewOreChunks.size() * (2 * Integer.BYTES + Short.BYTES)); - changedOrNewOreChunks.stream().map(oreChunks::get).forEach(oreVeinPosition -> { - byteBuffer.putInt(oreVeinPosition.chunkX); - byteBuffer.putInt(oreVeinPosition.chunkZ); - short veinTypeId = VeinTypeCaching.getVeinTypeId(oreVeinPosition.veinType); - if(oreVeinPosition.isDepleted()) { - veinTypeId |= 0x8000; + public void loadFromNbt(NBTTagCompound compound) { + int version = compound.getInteger("version"); + + if (version > CURRENT_FORMAT_VERSION) { + preventSaving = true; + VP.LOG.warn( + "Dimension {}: unsupported format version {}, data will not be modified.", + dimensionId, + version); + return; + } + + if (version == CURRENT_FORMAT_VERSION) { + loadOres(compound.getCompoundTag("ores")); + loadFluids(compound.getCompoundTag("fluids")); + } else { + // version key absent => v2 or older + LegacyDimensionCacheLoader.loadV2Ores(this, compound.getCompoundTag("ores")); + LegacyDimensionCacheLoader.loadV2Fluids(this, compound.getCompoundTag("fluids")); + } + } + + public NBTTagCompound saveToNbt() { + NBTTagCompound compound = new NBTTagCompound(); + compound.setInteger("version", CURRENT_FORMAT_VERSION); + compound.setInteger("dim", dimensionId); + compound.setTag("ores", saveOres()); + compound.setTag("fluids", saveFluids()); + isDirty = false; + return compound; + } + + private void loadOres(NBTTagCompound ores) { + if (!ores.hasKey("palette")) return; + + NBTTagList paletteTag = ores.getTagList("palette", 8); + String[] palette = new String[paletteTag.tagCount()]; + for (int i = 0; i < palette.length; i++) { + palette[i] = paletteTag.getStringTagAt(i); + } + + int[] chunkXArray = ores.getIntArray("chunkX"); + int[] chunkZArray = ores.getIntArray("chunkZ"); + int[] veinTypeIndexArray = ores.getIntArray("veinTypeIndex"); + byte[] depletedArray = ores.getByteArray("depleted"); + byte[] sourceArray = ores.getByteArray("source"); + + int size = chunkXArray.length; + oreChunks.ensureCapacity(oreChunks.size() + size); + + int unknownVeinTypes = 0; + int repairedCoordinates = 0; + int discardedRescans = 0; + int collapsedDuplicates = 0; + for (int i = 0; i < size; i++) { + int index = veinTypeIndexArray[i]; + if (index < 0 || index >= palette.length) { + unknownVeinTypes++; + continue; + } + VeinType veinType = VeinTypeCaching.getVeinType(palette[index]); + if (veinType == VeinType.NO_VEIN) { + unknownVeinTypes++; + continue; + } + VeinSource source = VeinSource.fromByte(sourceArray[i]); + OreVeinPosition position = new OreVeinPosition( + dimensionId, + chunkXArray[i], + chunkZArray[i], + veinType, + depletedArray[i] == 1, + source); + if (position.chunkX != chunkXArray[i] || position.chunkZ != chunkZArray[i]) { + repairedCoordinates++; + if (source == VeinSource.RESCAN) { + discardedRescans++; + requiresOreRescan = true; + continue; } - byteBuffer.putShort(veinTypeId); - }); - changedOrNewOreChunks.clear(); - byteBuffer.flip(); - return byteBuffer; - } - return null; - } - - public ByteBuffer saveUndergroundFluids() { - if(changedOrNewUndergroundFluids.isEmpty() == false) { - final ByteBuffer byteBuffer = ByteBuffer.allocate(changedOrNewUndergroundFluids.size() * (Long.BYTES + Integer.BYTES * (1 + VP.undergroundFluidSizeChunkX * VP.undergroundFluidSizeChunkZ))); - changedOrNewUndergroundFluids.stream().map(undergroundFluids::get).forEach(undergroundFluidPosition -> { - byteBuffer.putInt(undergroundFluidPosition.chunkX); - byteBuffer.putInt(undergroundFluidPosition.chunkZ); - byteBuffer.putInt(undergroundFluidPosition.fluid.getID()); - for(int offsetChunkX = 0; offsetChunkX < VP.undergroundFluidSizeChunkX; offsetChunkX++) { - for (int offsetChunkZ = 0; offsetChunkZ < VP.undergroundFluidSizeChunkZ; offsetChunkZ++) { - byteBuffer.putInt(undergroundFluidPosition.chunks[offsetChunkX][offsetChunkZ]); - } + } + long key = getOreVeinKey(position.chunkX, position.chunkZ); + OreVeinPosition stored = oreChunks.get(key); + if (stored == null) { + oreChunks.put(key, position); + } else { + collapsedDuplicates++; + if (position.getSource().canOverwrite(stored.getSource())) { + oreChunks.put(key, position.joinDepletedState(stored)); + } else { + stored.joinDepletedState(position); } - }); - changedOrNewUndergroundFluids.clear(); - byteBuffer.flip(); - return byteBuffer; - } - return null; - } - - public void loadCache(ByteBuffer oreChunksBuffer, ByteBuffer undergroundFluidsBuffer) { - if(oreChunksBuffer != null) { - while (oreChunksBuffer.remaining() >= Integer.BYTES * 2 + Short.BYTES) { - final int chunkX = oreChunksBuffer.getInt(); - final int chunkZ = oreChunksBuffer.getInt(); - final short veinTypeId = oreChunksBuffer.getShort(); - final boolean depleted = (veinTypeId & 0x8000) > 0; - final VeinType veinType = VeinTypeCaching.getVeinType((short)(veinTypeId & 0x7FFF)); - oreChunks.put(getOreVeinKey(chunkX, chunkZ), new OreVeinPosition(dimensionId, chunkX, chunkZ, veinType, depleted)); } } - if(undergroundFluidsBuffer != null) { - while (undergroundFluidsBuffer.remaining() >= Integer.BYTES * (3 + VP.undergroundFluidSizeChunkX * VP.undergroundFluidSizeChunkZ)) { - final int chunkX = undergroundFluidsBuffer.getInt(); - final int chunkZ = undergroundFluidsBuffer.getInt(); - final Fluid fluid = FluidRegistry.getFluid(undergroundFluidsBuffer.getInt()); - final int[][] chunks = new int[VP.undergroundFluidSizeChunkX][VP.undergroundFluidSizeChunkZ]; - for(int offsetChunkX = 0; offsetChunkX < VP.undergroundFluidSizeChunkX; offsetChunkX++) { - for (int offsetChunkZ = 0; offsetChunkZ < VP.undergroundFluidSizeChunkZ; offsetChunkZ++) { - chunks[offsetChunkX][offsetChunkZ] = undergroundFluidsBuffer.getInt(); - } - } - undergroundFluids.put(getUndergroundFluidKey(chunkX, chunkZ), new UndergroundFluidPosition(dimensionId, chunkX, chunkZ, fluid, chunks)); + if (unknownVeinTypes > 0) { + VP.LOG.warn( + "Dimension {}: skipped {} entries with unknown vein type names while loading cache.", + dimensionId, + unknownVeinTypes); + } + if (repairedCoordinates > 0) { + markDirty(); + VP.LOG.warn( + "Dimension {}: found {} misaligned ore vein cache coordinates, repaired {}, discarded {} entries " + + "that require a rescan, and collapsed {} duplicates. The cache will be rewritten.", + dimensionId, + repairedCoordinates, + repairedCoordinates - discardedRescans, + discardedRescans, + collapsedDuplicates); + } + } + + private void loadFluids(NBTTagCompound fluids) { + if (!fluids.hasKey("palette")) return; + + NBTTagList paletteTag = fluids.getTagList("palette", 8); + String[] palette = new String[paletteTag.tagCount()]; + for (int i = 0; i < palette.length; i++) { + palette[i] = paletteTag.getStringTagAt(i); + } + + int[] chunkXArray = fluids.getIntArray("chunkX"); + int[] chunkZArray = fluids.getIntArray("chunkZ"); + int[] fluidTypeIndexArray = fluids.getIntArray("fluidTypeIndex"); + int[] allChunkData = fluids.getIntArray("chunkData"); + int chunkDataSize = fluids.getInteger("chunkDataSize"); + + int fluidCount = chunkXArray.length; + undergroundFluids.ensureCapacity(undergroundFluids.size() + fluidCount); + + int unknownFluids = 0; + for (int i = 0; i < fluidCount; i++) { + int index = fluidTypeIndexArray[i]; + if (index < 0 || index >= palette.length) { + unknownFluids++; + continue; } + Fluid fluid = FluidRegistry.getFluid(palette[index]); + if (fluid == null) { + unknownFluids++; + continue; + } + int[][] chunks = new int[VP.undergroundFluidSizeChunkX][VP.undergroundFluidSizeChunkZ]; + int baseOffset = i * chunkDataSize; + for (int x = 0; x < VP.undergroundFluidSizeChunkX; x++) { + System.arraycopy( + allChunkData, + baseOffset + x * VP.undergroundFluidSizeChunkZ, + chunks[x], + 0, + VP.undergroundFluidSizeChunkZ); + } + undergroundFluids.put( + getUndergroundFluidKey(chunkXArray[i], chunkZArray[i]), + new UndergroundFluidPosition(dimensionId, chunkXArray[i], chunkZArray[i], fluid, chunks)); + } + if (unknownFluids > 0) { + VP.LOG.warn( + "Dimension {}: skipped {} entries with unknown fluid names while loading cache.", + dimensionId, + unknownFluids); } } - private ChunkCoordIntPair getOreVeinKey(int chunkX, int chunkZ) { - return new ChunkCoordIntPair(Utils.mapToCenterOreChunkCoord(chunkX), Utils.mapToCenterOreChunkCoord(chunkZ)); + private NBTTagCompound saveOres() { + NBTTagCompound compound = new NBTTagCompound(); + int size = oreChunks.size(); + if (size == 0) return compound; + + long[] sortedKeys = oreChunks.keySet().toLongArray(); + Arrays.sort(sortedKeys); + + List palette = new ArrayList<>(); + Map paletteIndex = new HashMap<>(); + for (long key : sortedKeys) { + String name = oreChunks.get(key).veinType.name; + if (!paletteIndex.containsKey(name)) { + paletteIndex.put(name, palette.size()); + palette.add(name); + } + } + + int[] chunkXArray = new int[size]; + int[] chunkZArray = new int[size]; + int[] veinTypeIndexArray = new int[size]; + byte[] depletedArray = new byte[size]; + byte[] sourceArray = new byte[size]; + + for (int i = 0; i < size; i++) { + OreVeinPosition vein = oreChunks.get(sortedKeys[i]); + chunkXArray[i] = vein.chunkX; + chunkZArray[i] = vein.chunkZ; + veinTypeIndexArray[i] = paletteIndex.get(vein.veinType.name); + depletedArray[i] = vein.isDepleted() ? (byte) 1 : (byte) 0; + sourceArray[i] = vein.getSourceByte(); + } + + NBTTagList paletteTag = new NBTTagList(); + for (String name : palette) { + paletteTag.appendTag(new NBTTagString(name)); + } + + compound.setTag("palette", paletteTag); + compound.setIntArray("chunkX", chunkXArray); + compound.setIntArray("chunkZ", chunkZArray); + compound.setIntArray("veinTypeIndex", veinTypeIndexArray); + compound.setByteArray("depleted", depletedArray); + compound.setByteArray("source", sourceArray); + + return compound; } - public UpdateResult putOreVein(final OreVeinPosition oreVeinPosition) { - final ChunkCoordIntPair key = getOreVeinKey(oreVeinPosition.chunkX, oreVeinPosition.chunkZ); - if(oreChunks.containsKey(key) == false) { - oreChunks.put(key, oreVeinPosition); - changedOrNewOreChunks.add(key); - return UpdateResult.New; + private NBTTagCompound saveFluids() { + NBTTagCompound compound = new NBTTagCompound(); + int size = undergroundFluids.size(); + if (size == 0) return compound; + + int chunkDataSize = VP.undergroundFluidSizeChunkX * VP.undergroundFluidSizeChunkZ; + + long[] sortedKeys = undergroundFluids.keySet().toLongArray(); + Arrays.sort(sortedKeys); + + List palette = new ArrayList<>(); + Map paletteIndex = new HashMap<>(); + for (long key : sortedKeys) { + String name = undergroundFluids.get(key).fluid.getName(); + if (!paletteIndex.containsKey(name)) { + paletteIndex.put(name, palette.size()); + palette.add(name); + } } - final OreVeinPosition storedOreVeinPosition = oreChunks.get(key); - if(storedOreVeinPosition.veinType != oreVeinPosition.veinType) { - oreChunks.put(key, oreVeinPosition.joinDepletedState(storedOreVeinPosition)); - changedOrNewOreChunks.add(key); - return UpdateResult.New; + + int[] chunkXArray = new int[size]; + int[] chunkZArray = new int[size]; + int[] fluidTypeIndexArray = new int[size]; + int[] allChunkData = new int[size * chunkDataSize]; + + for (int i = 0; i < size; i++) { + UndergroundFluidPosition fluid = undergroundFluids.get(sortedKeys[i]); + chunkXArray[i] = fluid.chunkX; + chunkZArray[i] = fluid.chunkZ; + fluidTypeIndexArray[i] = paletteIndex.get(fluid.fluid.getName()); + int baseOffset = i * chunkDataSize; + for (int x = 0; x < VP.undergroundFluidSizeChunkX; x++) { + System.arraycopy( + fluid.chunks[x], + 0, + allChunkData, + baseOffset + x * VP.undergroundFluidSizeChunkZ, + VP.undergroundFluidSizeChunkZ); + } } - return UpdateResult.AlreadyKnown; + + NBTTagList paletteTag = new NBTTagList(); + for (String name : palette) { + paletteTag.appendTag(new NBTTagString(name)); + } + + compound.setTag("palette", paletteTag); + compound.setIntArray("chunkX", chunkXArray); + compound.setIntArray("chunkZ", chunkZArray); + compound.setIntArray("fluidTypeIndex", fluidTypeIndexArray); + compound.setIntArray("chunkData", allChunkData); + compound.setInteger("chunkDataSize", chunkDataSize); + + return compound; + } + + void putOreFromLegacyLoad(OreVeinPosition pos) { + oreChunks.put(getOreVeinKey(pos.chunkX, pos.chunkZ), pos); + } + + void putFluidFromLegacyLoad(UndergroundFluidPosition pos) { + undergroundFluids.put(getUndergroundFluidKey(pos.chunkX, pos.chunkZ), pos); + } + + void ensureOreCapacityForLegacyLoad(int expectedAdditions) { + oreChunks.ensureCapacity(oreChunks.size() + expectedAdditions); + } + + void ensureFluidCapacityForLegacyLoad(int expectedAdditions) { + undergroundFluids.ensureCapacity(undergroundFluids.size() + expectedAdditions); + } + + boolean hasOres() { + return !oreChunks.isEmpty(); + } + + boolean hasFluids() { + return !undergroundFluids.isEmpty(); + } + + boolean requiresOreRescan() { + return requiresOreRescan; + } + + void markPreventSaving() { + preventSaving = true; + } + + private long getOreVeinKey(int chunkX, int chunkZ) { + return Utils.chunkCoordsToKey(Utils.mapToCenterOreChunkCoord(chunkX), Utils.mapToCenterOreChunkCoord(chunkZ)); + } + + private long getUndergroundFluidKey(int chunkX, int chunkZ) { + return Utils.chunkCoordsToKey( + Utils.mapToCornerUndergroundFluidChunkCoord(chunkX), + Utils.mapToCornerUndergroundFluidChunkCoord(chunkZ)); } public void toggleOreVein(int chunkX, int chunkZ) { - final ChunkCoordIntPair key = getOreVeinKey(chunkX, chunkZ); - if(oreChunks.containsKey(key)) { - oreChunks.get(key).toggleDepleted(); - changedOrNewOreChunks.add(key); + final long key = getOreVeinKey(chunkX, chunkZ); + final OreVeinPosition oreVeinPosition = oreChunks.get(key); + if (oreVeinPosition != null) { + oreVeinPosition.toggleDepleted(); + markDirty(); } } - public OreVeinPosition getOreVein(int chunkX, int chunkZ) { - final ChunkCoordIntPair key = getOreVeinKey(chunkX, chunkZ); - return oreChunks.getOrDefault(key, new OreVeinPosition(dimensionId, chunkX, chunkZ, VeinType.NO_VEIN, true)); - } + public UpdateResult putOreVein(final OreVeinPosition oreVeinPosition) { + if (oreVeinPosition.veinType == VeinType.NO_VEIN) { + return UpdateResult.AlreadyKnown; + } - private ChunkCoordIntPair getUndergroundFluidKey(int chunkX, int chunkZ) { - return new ChunkCoordIntPair(Utils.mapToCornerUndergroundFluidChunkCoord(chunkX), Utils.mapToCornerUndergroundFluidChunkCoord(chunkZ)); + final long key = getOreVeinKey(oreVeinPosition.chunkX, oreVeinPosition.chunkZ); + final OreVeinPosition storedOreVeinPosition = oreChunks.get(key); + if (storedOreVeinPosition == null) { + oreChunks.put(key, oreVeinPosition); + markDirty(); + return UpdateResult.New; + } + + // Stop update if lower trusted VeinSource + if (!oreVeinPosition.getSource().canOverwrite(storedOreVeinPosition.getSource())) { + return UpdateResult.AlreadyKnown; + } + + if (storedOreVeinPosition.veinType != oreVeinPosition.veinType) { + oreChunks.put(key, oreVeinPosition.joinDepletedState(storedOreVeinPosition)); + markDirty(); + return UpdateResult.New; + } + + // If identical entry but "better" source we update it + if (storedOreVeinPosition.getSourceByte() != oreVeinPosition.getSourceByte()) { + oreChunks.put(key, oreVeinPosition.joinDepletedState(storedOreVeinPosition)); + markDirty(); + return UpdateResult.Updated; + } + return UpdateResult.AlreadyKnown; } public UpdateResult putUndergroundFluid(final UndergroundFluidPosition undergroundFluid) { - final ChunkCoordIntPair key = getUndergroundFluidKey(undergroundFluid.chunkX, undergroundFluid.chunkZ); - if(undergroundFluids.containsKey(key) == false) { - changedOrNewUndergroundFluids.add(key); + final long key = getUndergroundFluidKey(undergroundFluid.chunkX, undergroundFluid.chunkZ); + final UndergroundFluidPosition storedUndergroundFluid = undergroundFluids.get(key); + if (storedUndergroundFluid == null) { undergroundFluids.put(key, undergroundFluid); + markDirty(); return UpdateResult.New; - } - else if(undergroundFluids.get(key).equals(undergroundFluid) == false) { - changedOrNewUndergroundFluids.add(key); + } else if (!storedUndergroundFluid.equals(undergroundFluid)) { undergroundFluids.put(key, undergroundFluid); + markDirty(); return UpdateResult.Updated; } return UpdateResult.AlreadyKnown; } + public OreVeinPosition getOreVein(int chunkX, int chunkZ) { + final long key = getOreVeinKey(chunkX, chunkZ); + return oreChunks.getOrDefault(key, OreVeinPosition.EMPTY_VEIN); + } + public UndergroundFluidPosition getUndergroundFluid(int chunkX, int chunkZ) { - final ChunkCoordIntPair key = getUndergroundFluidKey(chunkX, chunkZ); - return undergroundFluids.getOrDefault(key, UndergroundFluidPosition.getNotProspected(dimensionId, chunkX, chunkZ)); + final long key = getUndergroundFluidKey(chunkX, chunkZ); + return undergroundFluids.getOrDefault(key, UndergroundFluidPosition.NOT_PROSPECTED); } public Collection getAllOreVeins() { @@ -159,4 +462,53 @@ public Collection getAllOreVeins() { public Collection getAllUndergroundFluids() { return undergroundFluids.values(); } + + public boolean isDirty() { + return isDirty && !preventSaving; + } + + public void markDirty() { + isDirty = true; + } + + /** + * Reset selected veins; these veins need not be present. Input coords are in chunk coordinates, NOT block coords. + * Will not error on bad input, but it also probably won't do anything useful. startChunks should be less than their + * respective endChunks. + */ + public void clearOreVeins(int startChunkX, int startChunkZ, int endChunkX, int endChunkZ) { + clearOreVeins(startChunkX, startChunkZ, endChunkX, endChunkZ, EnumSet.noneOf(VeinSource.class)); + } + + /** + * Reset selected veins within the area whose source is not in {@code protectedSources}. + */ + public void clearOreVeins(int startChunkX, int startChunkZ, int endChunkX, int endChunkZ, + EnumSet protectedSources) { + // This method iterates for each chunk mapped. In many cases, it is probably faster to iterate over chunks in + // the area to be cleared instead. i.e. if (chunksInClearArea < totalChunksMapped) {useAltIterator()}. If + // someone calls this enough to make it a problem, they can add that. + boolean removedAny = oreChunks.long2ObjectEntrySet().removeIf(entry -> { + OreVeinPosition val = entry.getValue(); + final boolean withinX = val.chunkX >= startChunkX && val.chunkX <= endChunkX; + final boolean withinZ = val.chunkZ >= startChunkZ && val.chunkZ <= endChunkZ; + if (!withinX || !withinZ) return false; + return !protectedSources.contains(val.getSource()); + }); + if (removedAny) { + markDirty(); + } + } + + /** + * Remove all ore veins from this dimension whose source is not in {@code protectedSources}. + */ + public void clearOreVeinsExcept(EnumSet protectedSources) { + boolean removedAny = oreChunks.long2ObjectEntrySet() + .removeIf(entry -> !protectedSources.contains(entry.getValue().getSource())); + if (removedAny) { + markDirty(); + } + } + } diff --git a/src/main/java/com/sinthoras/visualprospecting/database/DimensionNameResolver.java b/src/main/java/com/sinthoras/visualprospecting/database/DimensionNameResolver.java new file mode 100644 index 00000000..f723ebf2 --- /dev/null +++ b/src/main/java/com/sinthoras/visualprospecting/database/DimensionNameResolver.java @@ -0,0 +1,48 @@ +package com.sinthoras.visualprospecting.database; + +import net.minecraft.world.WorldProvider; +import net.minecraft.world.WorldServer; +import net.minecraftforge.common.DimensionManager; + +import com.sinthoras.visualprospecting.VP; + +import cpw.mods.fml.common.Optional; +import gregtech.api.enums.Mods; +import micdoodle8.mods.galacticraft.api.galaxies.CelestialBody; +import micdoodle8.mods.galacticraft.api.prefab.world.gen.WorldProviderSpace; + +/** + * Resolves a dimension name from a {@code dimensionId}, regardless of if that dimension is currently loaded or not. + */ +public final class DimensionNameResolver { + + private DimensionNameResolver() {} + + public static String resolve(int dimensionId) { + final WorldServer world = DimensionManager.getWorld(dimensionId); + WorldProvider provider = world != null ? world.provider : null; + if (provider == null) { + try { + provider = DimensionManager.createProviderFor(dimensionId); + } catch (Throwable t) { + VP.LOG.warn("Could not resolve a provider for dimensionId={}: {}", dimensionId, t); + return ""; + } + } + + final String celestialName = Mods.GalacticraftCore.isModLoaded() ? getCelestialBodyName(provider) : null; + if (celestialName != null) return celestialName; + + final String name = provider.getDimensionName(); + return name == null ? "" : name; + } + + @Optional.Method(modid = Mods.ModIDs.GALACTICRAFT_CORE) + private static String getCelestialBodyName(WorldProvider provider) { + if (provider instanceof WorldProviderSpace space) { + final CelestialBody body = space.getCelestialBody(); + return body == null ? null : body.getName(); + } + return null; + } +} diff --git a/src/main/java/com/sinthoras/visualprospecting/database/LegacyDimensionCacheLoader.java b/src/main/java/com/sinthoras/visualprospecting/database/LegacyDimensionCacheLoader.java new file mode 100644 index 00000000..d4b84e9d --- /dev/null +++ b/src/main/java/com/sinthoras/visualprospecting/database/LegacyDimensionCacheLoader.java @@ -0,0 +1,262 @@ +package com.sinthoras.visualprospecting.database; + +import java.io.File; +import java.io.IOException; +import java.io.Reader; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.Collection; +import java.util.Map; + +import javax.annotation.Nullable; + +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidRegistry; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import com.sinthoras.visualprospecting.Tags; +import com.sinthoras.visualprospecting.VP; +import com.sinthoras.visualprospecting.database.veintypes.VeinType; +import com.sinthoras.visualprospecting.database.veintypes.VeinTypeCaching; + +import it.unimi.dsi.fastutil.shorts.Short2ObjectMap; +import it.unimi.dsi.fastutil.shorts.Short2ObjectMaps; +import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap; + +/** + * One-time loaders for legacy on-disk formats that {@link DimensionCache}. + *
    + *
  • v0 — pre-NBT raw binary (separate files under {@code oreChunks/} and {@code undergroundFluids/}). Vein + * types referenced by numeric ID via a {@code veintypesLUT} JSON file.
  • + *
  • v1 — NBT compound-per-vein nested under {@code ores}/{@code fluids}. May still use numeric IDs.
  • + *
  • v2 — NBT parallel-array layout (no {@code version} key on the root compound).
  • + *
+ */ +final class LegacyDimensionCacheLoader { + + private static final File OLD_ID_FILE = new File(Tags.VISUALPROSPECTING_DIR, "veintypesLUT"); + private static Short2ObjectMap idConversionMap; + + private LegacyDimensionCacheLoader() {} + + @SuppressWarnings("unchecked") + static void loadV2Ores(DimensionCache target, NBTTagCompound ores) { + // v2 (parallel arrays) vs v1 (one nested compound per vein) + if (ores.hasKey("chunkX") && ores.hasKey("chunkZ")) { + int[] chunkXArray = ores.getIntArray("chunkX"); + int[] chunkZArray = ores.getIntArray("chunkZ"); + byte[] depletedFlags = ores.getByteArray("depleted"); + NBTTagList veinTypeNamesList = ores.getTagList("veinTypeNames", 8); + + int size = chunkXArray.length; + int unknownVeinTypes = 0; + target.ensureOreCapacityForLegacyLoad(size); + + for (int i = 0; i < size; i++) { + int chunkX = chunkXArray[i]; + int chunkZ = chunkZArray[i]; + boolean depleted = depletedFlags[i] == 1; + String veinTypeName = veinTypeNamesList.getStringTagAt(i); + VeinType veinType = VeinTypeCaching.getVeinType(veinTypeName); + if (veinType == VeinType.NO_VEIN) { + unknownVeinTypes++; + continue; + } + target.putOreFromLegacyLoad( + new OreVeinPosition( + target.dimensionId, + chunkX, + chunkZ, + veinType, + depleted, + VeinSource.UNKNOWN)); + } + if (unknownVeinTypes > 0) { + VP.LOG.warn( + "Dimension {}: skipped {} entries with unknown vein type names while loading cache.", + target.dimensionId, + unknownVeinTypes); + } + } else { + for (NBTBase base : (Collection) ores.tagMap.values()) { + NBTTagCompound veinCompound = (NBTTagCompound) base; + int chunkX = veinCompound.getInteger("chunkX"); + int chunkZ = veinCompound.getInteger("chunkZ"); + boolean depleted = veinCompound.getBoolean("depleted"); + VeinType veinType; + if (veinCompound.hasKey("veinTypeId")) { + veinType = getVeinFromId(target, veinCompound.getShort("veinTypeId")); + if (veinType == null) continue; + } else { + veinType = VeinTypeCaching.getVeinType(veinCompound.getString("veinTypeName")); + if (veinType == VeinType.NO_VEIN) continue; + } + target.putOreFromLegacyLoad( + new OreVeinPosition( + target.dimensionId, + chunkX, + chunkZ, + veinType, + depleted, + VeinSource.UNKNOWN)); + } + } + if (target.hasOres()) target.markDirty(); + } + + @SuppressWarnings("unchecked") + static void loadV2Fluids(DimensionCache target, NBTTagCompound fluids) { + // v2 (parallel arrays) vs v1 (one nested compound per fluid) + if (fluids.hasKey("chunkX") && fluids.hasKey("chunkZ")) { + int[] chunkXArray = fluids.getIntArray("chunkX"); + int[] chunkZArray = fluids.getIntArray("chunkZ"); + int[] allChunkData = fluids.getIntArray("chunkData"); + int chunkDataSize = fluids.getInteger("chunkDataSize"); + NBTTagList fluidNamesList = fluids.getTagList("fluidNames", 8); + + int fluidCount = chunkXArray.length; + int unknownFluids = 0; + target.ensureFluidCapacityForLegacyLoad(fluidCount); + + for (int fluidIndex = 0; fluidIndex < fluidCount; fluidIndex++) { + int chunkX = chunkXArray[fluidIndex]; + int chunkZ = chunkZArray[fluidIndex]; + String fluidName = fluidNamesList.getStringTagAt(fluidIndex); + Fluid fluid = FluidRegistry.getFluid(fluidName); + if (fluid == null) { + unknownFluids++; + continue; + } + int[][] chunks = new int[VP.undergroundFluidSizeChunkX][VP.undergroundFluidSizeChunkZ]; + int baseOffset = fluidIndex * chunkDataSize; + for (int x = 0; x < VP.undergroundFluidSizeChunkX; x++) { + System.arraycopy( + allChunkData, + baseOffset + x * VP.undergroundFluidSizeChunkZ, + chunks[x], + 0, + VP.undergroundFluidSizeChunkZ); + } + target.putFluidFromLegacyLoad( + new UndergroundFluidPosition(target.dimensionId, chunkX, chunkZ, fluid, chunks)); + } + if (unknownFluids > 0) { + VP.LOG.warn( + "Dimension {}: skipped {} entries with unknown fluid names while loading cache.", + target.dimensionId, + unknownFluids); + } + } else { + for (NBTBase base : (Collection) fluids.tagMap.values()) { + NBTTagCompound fluidCompound = (NBTTagCompound) base; + int chunkX = fluidCompound.getInteger("chunkX"); + int chunkZ = fluidCompound.getInteger("chunkZ"); + String fluidName = fluidCompound.getString("fluidName"); + Fluid fluid = FluidRegistry.getFluid(fluidName); + if (fluid == null) continue; + int[][] chunks = new int[VP.undergroundFluidSizeChunkX][VP.undergroundFluidSizeChunkZ]; + NBTTagList chunkList = fluidCompound.getTagList("chunks", 11); + for (int i = 0; i < VP.undergroundFluidSizeChunkX; i++) { + chunks[i] = chunkList.func_150306_c(i); + } + target.putFluidFromLegacyLoad( + new UndergroundFluidPosition(target.dimensionId, chunkX, chunkZ, fluid, chunks)); + } + } + if (target.hasFluids()) target.markDirty(); + } + + static void loadV0Binary(DimensionCache target, @Nullable ByteBuffer oreChunksBuffer, + @Nullable ByteBuffer undergroundFluidsBuffer) { + if (oreChunksBuffer != null) { + while (oreChunksBuffer.remaining() >= Integer.BYTES * 2 + Short.BYTES) { + final int chunkX = oreChunksBuffer.getInt(); + final int chunkZ = oreChunksBuffer.getInt(); + final short veinTypeId = oreChunksBuffer.getShort(); + final boolean depleted = (veinTypeId & 0x8000) > 0; + final VeinType veinType = getVeinFromId(target, (short) (veinTypeId & 0x7FFF)); + if (veinType == null) continue; + + target.putOreFromLegacyLoad( + new OreVeinPosition( + target.dimensionId, + chunkX, + chunkZ, + veinType, + depleted, + VeinSource.UNKNOWN)); + } + } + if (undergroundFluidsBuffer != null) { + while (undergroundFluidsBuffer.remaining() + >= Integer.BYTES * (3 + VP.undergroundFluidSizeChunkX * VP.undergroundFluidSizeChunkZ)) { + final int chunkX = undergroundFluidsBuffer.getInt(); + final int chunkZ = undergroundFluidsBuffer.getInt(); + final int fluidIDorNameLength = undergroundFluidsBuffer.getInt(); + final Fluid fluid; + if (fluidIDorNameLength < 0) { // name length + byte[] fluidNameBytes = new byte[-fluidIDorNameLength]; + undergroundFluidsBuffer.get(fluidNameBytes); + String fluidName = new String(fluidNameBytes, StandardCharsets.UTF_8); + fluid = FluidRegistry.getFluid(fluidName); + } else { // ID (legacy save format) + fluid = FluidRegistry.getFluid(fluidIDorNameLength); + } + final int[][] chunks = new int[VP.undergroundFluidSizeChunkX][VP.undergroundFluidSizeChunkZ]; + for (int offsetChunkX = 0; offsetChunkX < VP.undergroundFluidSizeChunkX; offsetChunkX++) { + for (int offsetChunkZ = 0; offsetChunkZ < VP.undergroundFluidSizeChunkZ; offsetChunkZ++) { + chunks[offsetChunkX][offsetChunkZ] = undergroundFluidsBuffer.getInt(); + } + } + if (fluid != null) { + target.putFluidFromLegacyLoad( + new UndergroundFluidPosition(target.dimensionId, chunkX, chunkZ, fluid, chunks)); + } + } + } + } + + private static @Nullable VeinType getVeinFromId(DimensionCache target, short veinTypeId) { + final String veinTypeName = getIdConversionMap().get(veinTypeId); + if (veinTypeName == null) { + target.markPreventSaving(); + VP.LOG.warn( + "Not loading ores in dimension {}. Couldn't find vein type for id {}, file {} may be missing.", + target.dimensionId, + veinTypeId, + OLD_ID_FILE.getAbsolutePath()); + return null; + } + return VeinTypeCaching.getVeinType(veinTypeName); + } + + private static Short2ObjectMap getIdConversionMap() { + if (idConversionMap != null) { + return idConversionMap; + } + + if (!OLD_ID_FILE.exists()) { + return Short2ObjectMaps.emptyMap(); + } + try { + final Gson gson = new Gson(); + final Reader reader = Files.newBufferedReader(OLD_ID_FILE.toPath()); + final Map map = gson.fromJson(reader, new TypeToken>() {}.getType()); + reader.close(); + Short2ObjectMap result = new Short2ObjectOpenHashMap<>(); + result.put((short) 0, Tags.ORE_MIX_NONE_NAME); + for (Map.Entry entry : map.entrySet()) { + result.put(entry.getValue().shortValue(), entry.getKey()); + } + return idConversionMap = result; + } catch (IOException e) { + VP.LOG.error("Failed to read legacy file at {}", OLD_ID_FILE.getAbsolutePath(), e); + return Short2ObjectMaps.emptyMap(); + } + } +} diff --git a/src/main/java/com/sinthoras/visualprospecting/database/OreVeinPosition.java b/src/main/java/com/sinthoras/visualprospecting/database/OreVeinPosition.java index f2ecd2a6..a58b3bb1 100644 --- a/src/main/java/com/sinthoras/visualprospecting/database/OreVeinPosition.java +++ b/src/main/java/com/sinthoras/visualprospecting/database/OreVeinPosition.java @@ -1,33 +1,39 @@ package com.sinthoras.visualprospecting.database; +import static cpw.mods.fml.common.network.ByteBufUtils.varIntByteCount; + +import java.nio.charset.StandardCharsets; + import com.sinthoras.visualprospecting.Utils; import com.sinthoras.visualprospecting.database.veintypes.VeinType; -import com.sinthoras.visualprospecting.database.veintypes.VeinTypeCaching; public class OreVeinPosition { - private static final int MAX_BYTES = 3 * Integer.BYTES + Byte.BYTES; + public static final int BYTES_WITHOUT_VEIN_NAME = 3 * Integer.BYTES + 2 * Byte.BYTES; + public static final OreVeinPosition EMPTY_VEIN = new OreVeinPosition( + 0, + 0, + 0, + VeinType.NO_VEIN, + true, + VeinSource.UNKNOWN); public final int dimensionId; public final int chunkX; public final int chunkZ; public final VeinType veinType; - private boolean depleted = false; - - public OreVeinPosition(int dimensionId, int chunkX, int chunkZ, VeinType veinType) { - this.dimensionId = dimensionId; - this.chunkX = Utils.mapToCenterOreChunkCoord(chunkX); - this.chunkZ = Utils.mapToCenterOreChunkCoord(chunkZ); - this.veinType = veinType; - } + private boolean depleted; + private final byte source; - public OreVeinPosition(int dimensionId, int chunkX, int chunkZ, VeinType veinType, boolean depleted) { + public OreVeinPosition(int dimensionId, int chunkX, int chunkZ, VeinType veinType, boolean depleted, + VeinSource source) { this.dimensionId = dimensionId; this.chunkX = Utils.mapToCenterOreChunkCoord(chunkX); this.chunkZ = Utils.mapToCenterOreChunkCoord(chunkZ); this.veinType = veinType; this.depleted = depleted; + this.source = source.toByte(); } public int getBlockX() { @@ -38,10 +44,30 @@ public int getBlockZ() { return Utils.coordChunkToBlock(chunkZ) + 8; } + public int getBytes() { + int veinNameLength = veinType.name.getBytes(StandardCharsets.UTF_8).length; + + return BYTES_WITHOUT_VEIN_NAME + veinNameLength; + } + + public int getPacketBytes() { + int veinNameLength = veinType.name.getBytes(StandardCharsets.UTF_8).length; + + return BYTES_WITHOUT_VEIN_NAME + varIntByteCount(veinNameLength) + veinNameLength; + } + public boolean isDepleted() { return depleted; } + public VeinSource getSource() { + return VeinSource.fromByte(source); + } + + public byte getSourceByte() { + return source; + } + public void toggleDepleted() { depleted = !depleted; } @@ -51,7 +77,4 @@ public OreVeinPosition joinDepletedState(final OreVeinPosition other) { return this; } - public static int getMaxBytes() { - return MAX_BYTES + VeinTypeCaching.getLongesOreNameLength(); - } } diff --git a/src/main/java/com/sinthoras/visualprospecting/database/ServerCache.java b/src/main/java/com/sinthoras/visualprospecting/database/ServerCache.java index 3378b5dd..5348d276 100644 --- a/src/main/java/com/sinthoras/visualprospecting/database/ServerCache.java +++ b/src/main/java/com/sinthoras/visualprospecting/database/ServerCache.java @@ -1,45 +1,96 @@ package com.sinthoras.visualprospecting.database; -import com.sinthoras.visualprospecting.VP; +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.world.World; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidStack; + +import com.sinthoras.visualprospecting.Config; import com.sinthoras.visualprospecting.Tags; import com.sinthoras.visualprospecting.Utils; +import com.sinthoras.visualprospecting.VP; import com.sinthoras.visualprospecting.database.veintypes.VeinType; import com.sinthoras.visualprospecting.database.veintypes.VeinTypeCaching; -import com.sinthoras.visualprospecting.integration.gregtech.UndergroundFluidsWrapper; -import net.minecraft.world.World; -import net.minecraftforge.fluids.Fluid; -import net.minecraftforge.fluids.FluidStack; +import com.sinthoras.visualprospecting.network.ProspectingNotification; +import com.sinthoras.visualprospecting.network.ProspectingRequest; +import com.sinthoras.visualprospecting.teams.TeamProspectionDispatcher; -import java.io.File; -import java.util.ArrayList; -import java.util.List; +import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import gregtech.api.events.OreInteractEvent; +import gregtech.api.events.VeinGenerateEvent; +import gregtech.api.interfaces.IOreMaterial; +import gregtech.common.UndergroundOil; +import gregtech.common.WorldgenGTOreLayer; +import gregtech.common.ores.OreInfo; +import gregtech.common.ores.OreManager; public class ServerCache extends WorldCache { public static final ServerCache instance = new ServerCache(); + private ServerCache() {} + protected File getStorageDirectory() { return Utils.getSubDirectory(Tags.SERVER_DIR); } - public synchronized void notifyOreVeinGeneration(int dimensionId, int chunkX, int chunkZ, final VeinType veinType) { - if(veinType != VeinType.NO_VEIN) { - super.putOreVein(new OreVeinPosition(dimensionId, chunkX, chunkZ, veinType)); + public synchronized void notifyOreVeinGeneration(int dimensionId, int chunkX, int chunkZ, final VeinType veinType, + VeinSource source) { + if (veinType != VeinType.NO_VEIN) { + super.putOreVein(new OreVeinPosition(dimensionId, chunkX, chunkZ, veinType, false, source)); } } - public void notifyOreVeinGeneration(int dimensionId, int chunkX, int chunkZ, final String veinName) { - notifyOreVeinGeneration(dimensionId, chunkX, chunkZ, VeinTypeCaching.getVeinType(veinName)); + @SubscribeEvent + public void onVeinGenerated(VeinGenerateEvent event) { + if (event.result == WorldgenGTOreLayer.ORE_PLACED && !event.layer.mWorldGenName.equals("NoOresInVein")) { + notifyOreVeinGeneration( + event.world.provider.dimensionId, + event.oreSeedX, + event.oreSeedZ, + VeinTypeCaching.getVeinType(event.layer.mWorldGenName), + VeinSource.GENERATED); + } + } + + @SubscribeEvent + public void onOreClicked(OreInteractEvent event) { + World world = event.world; + int x = event.x; + int y = event.y; + int z = event.z; + EntityPlayer player = event.player; + + if (!world.isRemote && Config.enableProspecting) { + IOreMaterial material; + + try (OreInfo info = OreManager.getOreInfo(world, x, y, z)) { + material = info != null && info.isNatural ? info.material : null; + } + + ProspectingNotification response = ProspectingRequest + .prospect(new ProspectingRequest(world.provider.dimensionId, x, y, z, material), event.world); + + if (response != null) { + TeamProspectionDispatcher.deliverProspectingResults((EntityPlayerMP) player, response); + } + } } - public List prospectOreChunks(int dimensionId, int minChunkX, int minChunkZ, int maxChunkX, int maxChunkZ) { + public List prospectOreChunks(int dimensionId, int minChunkX, int minChunkZ, int maxChunkX, + int maxChunkZ) { minChunkX = Utils.mapToCenterOreChunkCoord(minChunkX); minChunkZ = Utils.mapToCenterOreChunkCoord(minChunkZ); maxChunkX = Utils.mapToCenterOreChunkCoord(maxChunkX); maxChunkZ = Utils.mapToCenterOreChunkCoord(maxChunkZ); List oreVeinPositions = new ArrayList<>(); - for(int chunkX = minChunkX; chunkX <= maxChunkX; chunkX = Utils.mapToCenterOreChunkCoord(chunkX + 3)) { + for (int chunkX = minChunkX; chunkX <= maxChunkX; chunkX = Utils.mapToCenterOreChunkCoord(chunkX + 3)) { for (int chunkZ = minChunkZ; chunkZ <= maxChunkZ; chunkZ = Utils.mapToCenterOreChunkCoord(chunkZ + 3)) { final OreVeinPosition oreVeinPosition = getOreVein(dimensionId, chunkX, chunkZ); if (oreVeinPosition.veinType != VeinType.NO_VEIN) { @@ -50,8 +101,10 @@ public List prospectOreChunks(int dimensionId, int minChunkX, i return oreVeinPositions; } - public List prospectOreBlocks(int dimensionId, int minBlockX, int minBlockZ, int maxBlockX, int maxBlockZ) { - return prospectOreChunks(dimensionId, + public List prospectOreBlocks(int dimensionId, int minBlockX, int minBlockZ, int maxBlockX, + int maxBlockZ) { + return prospectOreChunks( + dimensionId, Utils.coordBlockToChunk(minBlockX), Utils.coordBlockToChunk(minBlockZ), Utils.coordBlockToChunk(maxBlockX), @@ -59,19 +112,29 @@ public List prospectOreBlocks(int dimensionId, int minBlockX, i } public List prospectOreBlockRadius(int dimensionId, int blockX, int blockZ, int blockRadius) { - return prospectOreBlocks(dimensionId, blockX - blockRadius, blockZ - blockRadius, blockX + blockRadius, blockZ + blockRadius); + return prospectOreBlocks( + dimensionId, + blockX - blockRadius, + blockZ - blockRadius, + blockX + blockRadius, + blockZ + blockRadius); } - public List prospectUndergroundFluidBlockRadius(World world, int blockX, int blockZ, int undergroundFluidBlockRadius) { - final int minChunkX = Utils.mapToCornerUndergroundFluidChunkCoord(Utils.coordBlockToChunk(blockX - undergroundFluidBlockRadius)); - final int minChunkZ = Utils.mapToCornerUndergroundFluidChunkCoord(Utils.coordBlockToChunk(blockZ - undergroundFluidBlockRadius)); + public List prospectUndergroundFluidBlockRadius(World world, int blockX, int blockZ, + int undergroundFluidBlockRadius) { + final int minChunkX = Utils + .mapToCornerUndergroundFluidChunkCoord(Utils.coordBlockToChunk(blockX - undergroundFluidBlockRadius)); + final int minChunkZ = Utils + .mapToCornerUndergroundFluidChunkCoord(Utils.coordBlockToChunk(blockZ - undergroundFluidBlockRadius)); // Equals to ceil(undergroundFluidBlockRadius / (VP.undergroundFluidFieldSizeChunkX * VP.chunkWidth)) - final int undergroundFluidRadius = (undergroundFluidBlockRadius + VP.undergroundFluidSizeChunkX * VP.chunkWidth - 1) / (VP.undergroundFluidSizeChunkX * VP.chunkWidth); + final int undergroundFluidRadius = (undergroundFluidBlockRadius + VP.undergroundFluidSizeChunkX * VP.chunkWidth + - 1) / (VP.undergroundFluidSizeChunkX * VP.chunkWidth); - List foundUndergroundFluids = new ArrayList<>((2 * undergroundFluidRadius + 1) * (2 * undergroundFluidRadius + 1)); + List foundUndergroundFluids = new ArrayList<>( + (2 * undergroundFluidRadius + 1) * (2 * undergroundFluidRadius + 1)); - for(int undergroundFluidX = 0; undergroundFluidX < 2 * undergroundFluidRadius + 1; undergroundFluidX++) { + for (int undergroundFluidX = 0; undergroundFluidX < 2 * undergroundFluidRadius + 1; undergroundFluidX++) { for (int undergroundFluidZ = 0; undergroundFluidZ < 2 * undergroundFluidRadius + 1; undergroundFluidZ++) { final int chunkX = minChunkX + undergroundFluidX * VP.undergroundFluidSizeChunkX; final int chunkZ = minChunkZ + undergroundFluidZ * VP.undergroundFluidSizeChunkZ; @@ -79,7 +142,8 @@ public List prospectUndergroundFluidBlockRadius(World Fluid fluid = null; for (int offsetChunkX = 0; offsetChunkX < VP.undergroundFluidSizeChunkX; offsetChunkX++) { for (int offsetChunkZ = 0; offsetChunkZ < VP.undergroundFluidSizeChunkZ; offsetChunkZ++) { - final FluidStack prospectedFluid = UndergroundFluidsWrapper.prospectFluid(world, chunkX + offsetChunkX, chunkZ + offsetChunkZ); + final FluidStack prospectedFluid = UndergroundOil + .undergroundOil(world, chunkX + offsetChunkX, chunkZ + offsetChunkZ, -1); if (prospectedFluid != null) { fluid = prospectedFluid.getFluid(); chunks[offsetChunkX][offsetChunkZ] = prospectedFluid.amount; @@ -87,7 +151,8 @@ public List prospectUndergroundFluidBlockRadius(World } } if (fluid != null) { - foundUndergroundFluids.add(new UndergroundFluidPosition(world.provider.dimensionId, chunkX, chunkZ, fluid, chunks)); + foundUndergroundFluids.add( + new UndergroundFluidPosition(world.provider.dimensionId, chunkX, chunkZ, fluid, chunks)); } } } diff --git a/src/main/java/com/sinthoras/visualprospecting/database/TransferCache.java b/src/main/java/com/sinthoras/visualprospecting/database/TransferCache.java index 4b1ee12e..db5003ce 100644 --- a/src/main/java/com/sinthoras/visualprospecting/database/TransferCache.java +++ b/src/main/java/com/sinthoras/visualprospecting/database/TransferCache.java @@ -1,8 +1,13 @@ package com.sinthoras.visualprospecting.database; -import com.sinthoras.visualprospecting.Config; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; -import java.util.*; +import com.sinthoras.visualprospecting.Config; public class TransferCache { @@ -12,16 +17,16 @@ public class TransferCache { private final Map> sharedUndergroundFluids = new HashMap<>(); private final Queue timestamp = new LinkedList<>(); - public void addClientProspectionData(String uuid, List oreVeins, List undergroundFluids) { + public void addClientProspectionData(String uuid, List oreVeins, + List undergroundFluids) { sharedOreVeins.remove(uuid); sharedUndergroundFluids.remove(uuid); timestamp.remove(uuid); - final int oreVeinPositionSizeInRam = OreVeinPosition.getMaxBytes() + 3 * Byte.BYTES; // JVM represents everything aligned at 4 bytes - final int newEntryBytes = oreVeins.size() * oreVeinPositionSizeInRam + undergroundFluids.size() * UndergroundFluidPosition.BYTES; + final int newEntryBytes = oreVeins.stream().mapToInt(OreVeinPosition::getBytes).sum() + + undergroundFluids.size() * UndergroundFluidPosition.BYTES; - while(getUsedMemory() > (Config.maxTransferCacheSizeMB << 20) - newEntryBytes - && timestamp.isEmpty() == false) { + while (getUsedMemory() > (Config.maxTransferCacheSizeMB << 20) - newEntryBytes && !timestamp.isEmpty()) { String oldestUUID = timestamp.remove(); sharedOreVeins.remove(oldestUUID); sharedUndergroundFluids.remove(oldestUUID); @@ -45,8 +50,8 @@ public List getSharedUndergroundFluidsFrom(String uuid } private int getUsedMemory() { - final int oreVeinPositionSizeInRam = OreVeinPosition.getMaxBytes() + 3 * Byte.BYTES; // JVM represents everything aligned at 4 bytes - return sharedOreVeins.values().stream().mapToInt(oreVeins -> oreVeins.size() * oreVeinPositionSizeInRam).sum() - + sharedUndergroundFluids.values().stream().mapToInt(undergroundFluids -> undergroundFluids.size() * UndergroundFluidPosition.BYTES).sum(); + return sharedOreVeins.values().stream().flatMap(List::stream).mapToInt(OreVeinPosition::getBytes).sum() + + sharedUndergroundFluids.values().stream() + .mapToInt(undergroundFluids -> undergroundFluids.size() * UndergroundFluidPosition.BYTES).sum(); } } diff --git a/src/main/java/com/sinthoras/visualprospecting/database/UndergroundFluidPosition.java b/src/main/java/com/sinthoras/visualprospecting/database/UndergroundFluidPosition.java index a7b1212c..a6279707 100644 --- a/src/main/java/com/sinthoras/visualprospecting/database/UndergroundFluidPosition.java +++ b/src/main/java/com/sinthoras/visualprospecting/database/UndergroundFluidPosition.java @@ -1,24 +1,25 @@ package com.sinthoras.visualprospecting.database; -import com.sinthoras.visualprospecting.Utils; -import com.sinthoras.visualprospecting.VP; +import java.util.Arrays; + import net.minecraftforge.fluids.Fluid; -import java.util.Arrays; +import com.sinthoras.visualprospecting.Utils; +import com.sinthoras.visualprospecting.VP; public class UndergroundFluidPosition { - public static final int BYTES = (3 + 1 + VP.undergroundFluidSizeChunkX * VP.undergroundFluidSizeChunkZ) * Integer.BYTES; + public static final int BYTES = (3 + 1 + VP.undergroundFluidSizeChunkX * VP.undergroundFluidSizeChunkZ) + * Integer.BYTES; + public static final UndergroundFluidPosition NOT_PROSPECTED = new UndergroundFluidPosition(0, 0, 0, null, null); public final int dimensionId; public final int chunkX; public final int chunkZ; public final Fluid fluid; public final int[][] chunks; - - public static UndergroundFluidPosition getNotProspected(int dimensionId, int chunkX, int chunkZ) { - return new UndergroundFluidPosition(dimensionId, chunkX, chunkZ, null, null); - } + private final int minProduction; + private final int maxProduction; public UndergroundFluidPosition(int dimensionId, int chunkX, int chunkZ, Fluid fluid, int[][] chunks) { this.dimensionId = dimensionId; @@ -26,6 +27,23 @@ public UndergroundFluidPosition(int dimensionId, int chunkX, int chunkZ, Fluid f this.chunkZ = Utils.mapToCornerUndergroundFluidChunkCoord(chunkZ); this.fluid = fluid; this.chunks = chunks; + + int smallest = Integer.MAX_VALUE; + int largest = Integer.MIN_VALUE; + if (chunks != null) { + for (int cx = 0; cx < VP.undergroundFluidSizeChunkX; cx++) { + for (int cz = 0; cz < VP.undergroundFluidSizeChunkZ; cz++) { + int amount = chunks[cx][cz]; + if (amount < smallest) smallest = amount; + if (amount > largest) largest = amount; + } + } + } else { + smallest = 0; + largest = 0; + } + this.minProduction = smallest; + this.maxProduction = largest; } public int getBlockX() { @@ -37,27 +55,11 @@ public int getBlockZ() { } public int getMinProduction() { - int smallest = Integer.MAX_VALUE; - for(int chunkX = 0; chunkX < VP.undergroundFluidSizeChunkX; chunkX++) { - for (int chunkZ = 0; chunkZ < VP.undergroundFluidSizeChunkZ; chunkZ++) { - if (chunks[chunkX][chunkZ] < smallest) { - smallest = chunks[chunkX][chunkZ]; - } - } - } - return smallest; + return minProduction; } public int getMaxProduction() { - int largest = Integer.MIN_VALUE; - for(int chunkX = 0; chunkX < VP.undergroundFluidSizeChunkX; chunkX++) { - for (int chunkZ = 0; chunkZ < VP.undergroundFluidSizeChunkZ; chunkZ++) { - if (chunks[chunkX][chunkZ] > largest) { - largest = chunks[chunkX][chunkZ]; - } - } - } - return largest; + return maxProduction; } public boolean isProspected() { @@ -65,8 +67,7 @@ public boolean isProspected() { } public boolean equals(UndergroundFluidPosition other) { - return dimensionId == other.dimensionId - && chunkX == other.chunkX + return dimensionId == other.dimensionId && chunkX == other.chunkX && chunkZ == other.chunkZ && fluid == other.fluid && Arrays.deepEquals(chunks, other.chunks); diff --git a/src/main/java/com/sinthoras/visualprospecting/database/VeinSource.java b/src/main/java/com/sinthoras/visualprospecting/database/VeinSource.java new file mode 100644 index 00000000..0b992a95 --- /dev/null +++ b/src/main/java/com/sinthoras/visualprospecting/database/VeinSource.java @@ -0,0 +1,47 @@ +package com.sinthoras.visualprospecting.database; + +/** + * Added for v3 of the server/client cache format. Stored as a {@code byte} for tinier footprint. + *

+ * Never change or reuse the {@code id} of an existing constant, it would silently corrupt existing caches. + */ +public enum VeinSource { + + // id = byte on disk to match to this enum | trustLevel = trust hierarchy for overwriting + UNKNOWN(0, 0), + RESCAN(2, 1), + API(3, 2), + GENERATED(1, 3); + + private final int id; + private final int trustLevel; + + VeinSource(int id, int trustLevel) { + this.id = id; + this.trustLevel = trustLevel; + } + + public boolean canOverwrite(VeinSource existing) { + return this.trustLevel >= existing.trustLevel; + } + + public byte toByte() { + return (byte) id; + } + + private static final VeinSource[] BY_ID; + static { + int maxId = 0; + for (VeinSource source : values()) { + maxId = Math.max(maxId, source.id); + } + BY_ID = new VeinSource[maxId + 1]; + for (VeinSource source : values()) { + BY_ID[source.id] = source; + } + } + + public static VeinSource fromByte(byte id) { + return ((id >= 0) && (id < BY_ID.length) && (BY_ID[id] != null)) ? BY_ID[id] : UNKNOWN; + } +} diff --git a/src/main/java/com/sinthoras/visualprospecting/database/WorldCache.java b/src/main/java/com/sinthoras/visualprospecting/database/WorldCache.java index 464a4a38..c1934208 100644 --- a/src/main/java/com/sinthoras/visualprospecting/database/WorldCache.java +++ b/src/main/java/com/sinthoras/visualprospecting/database/WorldCache.java @@ -1,118 +1,192 @@ package com.sinthoras.visualprospecting.database; -import com.sinthoras.visualprospecting.Tags; -import com.sinthoras.visualprospecting.Utils; -import com.sinthoras.visualprospecting.database.veintypes.VeinType; - import java.io.File; import java.nio.ByteBuffer; -import java.util.HashMap; +import java.util.EnumSet; import java.util.HashSet; import java.util.Map; import java.util.Set; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.ChunkCoordinates; + +import com.sinthoras.visualprospecting.Tags; +import com.sinthoras.visualprospecting.Utils; + +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; + public abstract class WorldCache { - protected final Map dimensions = new HashMap<>(); - private boolean needsSaving = false; - protected File oreVeinCacheDirectory; - protected File undergroundFluidCacheDirectory; + protected final Int2ObjectMap dimensions = new Int2ObjectOpenHashMap<>(); + protected File worldCache; private boolean isLoaded = false; protected abstract File getStorageDirectory(); public boolean loadVeinCache(String worldId) { - if(isLoaded ) { - return true; - } + if (isLoaded) return true; isLoaded = true; - final File worldCacheDirectory = new File(getStorageDirectory(), worldId); - oreVeinCacheDirectory = new File(worldCacheDirectory, Tags.OREVEIN_DIR); - undergroundFluidCacheDirectory = new File(worldCacheDirectory, Tags.UNDERGROUNDFLUID_DIR); - oreVeinCacheDirectory.mkdirs(); - undergroundFluidCacheDirectory.mkdirs(); - final Map oreVeinDimensionBuffers = Utils.getDIMFiles(oreVeinCacheDirectory); - final Map undergroundFluidDimensionBuffers = Utils.getDIMFiles(undergroundFluidCacheDirectory); + worldCache = new File(getStorageDirectory(), worldId); + + if (loadLegacyVeinCache(worldCache)) return true; + + final File[] dimensionFiles = worldCache.listFiles(); + if (dimensionFiles == null || dimensionFiles.length == 0) return false; + + boolean loadedAny = false; + boolean requiresOreRescan = false; + for (File dimensionFile : dimensionFiles) { + final String fileName = dimensionFile.getName(); + if (!dimensionFile.isFile() || !fileName.endsWith(".dat")) { + continue; + } + + final NBTTagCompound dimCompound = Utils.readNBT(dimensionFile); + if (dimCompound == null) continue; + + final int dimensionId = dimCompound.getInteger("dim"); + final DimensionCache dimension = dimensions.computeIfAbsent(dimensionId, DimensionCache::new); + dimension.loadFromNbt(dimCompound); + requiresOreRescan |= dimension.requiresOreRescan(); + loadedAny = true; + } + + return loadedAny && !requiresOreRescan; + } + + private boolean loadLegacyVeinCache(File worldCacheDirectory) { + File oreVeinCacheDirectory = new File(worldCacheDirectory, Tags.OREVEIN_DIR); + File undergroundFluidCacheDirectory = new File(worldCacheDirectory, Tags.UNDERGROUNDFLUID_DIR); + + if (!oreVeinCacheDirectory.exists() && !undergroundFluidCacheDirectory.exists()) { + return false; + } + + final Map oreVeinDimensionBuffers = Utils.getLegacyDimFiles(oreVeinCacheDirectory); + final Map undergroundFluidDimensionBuffers = Utils + .getLegacyDimFiles(undergroundFluidCacheDirectory); final Set dimensionsIds = new HashSet<>(); dimensionsIds.addAll(oreVeinDimensionBuffers.keySet()); dimensionsIds.addAll(undergroundFluidDimensionBuffers.keySet()); - if(dimensionsIds.isEmpty()) { + dimensionsIds.addAll(dimensions.keySet()); + if (dimensionsIds.isEmpty()) { return false; } - for(int dimensionId : dimensionsIds) { - final DimensionCache dimension = new DimensionCache(dimensionId); - dimension.loadCache(oreVeinDimensionBuffers.get(dimensionId), undergroundFluidDimensionBuffers.get(dimensionId)); + for (int dimensionId : dimensionsIds) { + DimensionCache dimension = dimensions.get(dimensionId); + if (dimension == null) { + dimension = new DimensionCache(dimensionId); + } + LegacyDimensionCacheLoader.loadV0Binary( + dimension, + oreVeinDimensionBuffers.get(dimensionId), + undergroundFluidDimensionBuffers.get(dimensionId)); + dimension.markDirty(); dimensions.put(dimensionId, dimension); } + + Utils.deleteDirectoryRecursively(oreVeinCacheDirectory); + Utils.deleteDirectoryRecursively(undergroundFluidCacheDirectory); + saveVeinCache(); return true; } public void saveVeinCache() { - if(needsSaving) { - for (DimensionCache dimension : dimensions.values()) { - final ByteBuffer oreVeinBuffer = dimension.saveOreChunks(); - if (oreVeinBuffer != null) { - Utils.appendToFile(new File(oreVeinCacheDirectory.toPath() + "/DIM" + dimension.dimensionId), oreVeinBuffer); - } - final ByteBuffer undergroundFluidBuffer = dimension.saveUndergroundFluids(); - if(undergroundFluidBuffer != null) { - Utils.appendToFile(new File(undergroundFluidCacheDirectory.toPath() + "/DIM" + dimension.dimensionId), undergroundFluidBuffer); - } - } - needsSaving = false; + for (DimensionCache dimension : dimensions.values()) { + if (!dimension.isDirty()) continue; + File dimFile = new File(worldCache, "DIM" + dimension.dimensionId + ".dat"); + Utils.writeNBT(dimFile, dimension.saveToNbt()); } } public void reset() { dimensions.clear(); - needsSaving = false; isLoaded = false; } - private DimensionCache.UpdateResult updateSaveFlag(DimensionCache.UpdateResult updateResult) { - needsSaving |= updateResult != DimensionCache.UpdateResult.AlreadyKnown; - return updateResult; + /** + * Remove ore-vein entries across all dimensions whose source is not in {@code protectedSources}. + */ + public void resetExcept(EnumSet protectedSources) { + for (DimensionCache dim : dimensions.values()) { + dim.clearOreVeinsExcept(protectedSources); + } } - protected DimensionCache.UpdateResult putOreVein(final OreVeinPosition oreVeinPosition) { - DimensionCache dimension = dimensions.get(oreVeinPosition.dimensionId); - if(dimension == null) { - dimension = new DimensionCache(oreVeinPosition.dimensionId); - dimensions.put(oreVeinPosition.dimensionId, dimension); + /** + * Reset some chunks. Not all, and (usually) not none - but some. Input coords are in chunk coordinates, NOT block + * coords. + * + * @param dimID The dimension ID. + * @param startX The X coord of the starting chunk. Must be less than endX. + * @param startZ The Z coord of the starting chunk. Must be less than endZ. + * @param endX The X coord of the ending chunk. + * @param endZ The Z coord of the ending chunk. + */ + public void resetSome(int dimID, int startX, int startZ, int endX, int endZ) { + resetSome(dimID, startX, startZ, endX, endZ, EnumSet.noneOf(VeinSource.class)); + } + + /** + * Same as {@link #resetSome(int, int, int, int, int)} but preserves entries whose source is in + * {@code protectedSources}. + */ + public void resetSome(int dimID, int startX, int startZ, int endX, int endZ, EnumSet protectedSources) { + DimensionCache dim = dimensions.get(dimID); + if (dim != null) { + dim.clearOreVeins(startX, startZ, endX, endZ, protectedSources); + isLoaded = false; } - return updateSaveFlag(dimension.putOreVein(oreVeinPosition)); + } + + public void resetSpawnChunks(ChunkCoordinates spawn, int dimID) { + resetSpawnChunks(spawn, dimID, EnumSet.noneOf(VeinSource.class)); + } + + public void resetSpawnChunks(ChunkCoordinates spawn, int dimID, EnumSet protectedSources) { + int spawnChunkX = Utils.coordBlockToChunk(spawn.posX); + int spawnChunkZ = Utils.coordBlockToChunk(spawn.posZ); + + int spawnChunksRadius = 8; + int startX = spawnChunkX - spawnChunksRadius; + int startZ = spawnChunkZ - spawnChunksRadius; + int endX = spawnChunkX + spawnChunksRadius; + int endZ = spawnChunkZ + spawnChunksRadius; + + resetSome(dimID, startX, startZ, endX, endZ, protectedSources); + } + + protected DimensionCache.UpdateResult putOreVein(final OreVeinPosition oreVeinPosition) { + DimensionCache dimension = dimensions.computeIfAbsent(oreVeinPosition.dimensionId, DimensionCache::new); + return dimension.putOreVein(oreVeinPosition); } protected void toggleOreVein(int dimensionId, int chunkX, int chunkZ) { DimensionCache dimension = dimensions.get(dimensionId); - if(dimension != null) { + if (dimension != null) { dimension.toggleOreVein(chunkX, chunkZ); } - needsSaving = true; } public OreVeinPosition getOreVein(int dimensionId, int chunkX, int chunkZ) { DimensionCache dimension = dimensions.get(dimensionId); - if(dimension == null) { - return new OreVeinPosition(dimensionId, chunkX, chunkZ, VeinType.NO_VEIN, true); + if (dimension == null) { + return OreVeinPosition.EMPTY_VEIN; } return dimension.getOreVein(chunkX, chunkZ); } protected DimensionCache.UpdateResult putUndergroundFluids(final UndergroundFluidPosition undergroundFluid) { - DimensionCache dimension = dimensions.get(undergroundFluid.dimensionId); - if(dimension == null) { - dimension = new DimensionCache(undergroundFluid.dimensionId); - dimensions.put(undergroundFluid.dimensionId, dimension); - } - return updateSaveFlag(dimension.putUndergroundFluid(undergroundFluid)); + DimensionCache dimension = dimensions.computeIfAbsent(undergroundFluid.dimensionId, DimensionCache::new); + return dimension.putUndergroundFluid(undergroundFluid); } public UndergroundFluidPosition getUndergroundFluid(int dimensionId, int chunkX, int chunkZ) { DimensionCache dimension = dimensions.get(dimensionId); - if(dimension == null) { - return UndergroundFluidPosition.getNotProspected(dimensionId, chunkX, chunkZ); + if (dimension == null) { + return UndergroundFluidPosition.NOT_PROSPECTED; } return dimension.getUndergroundFluid(chunkX, chunkZ); } diff --git a/src/main/java/com/sinthoras/visualprospecting/database/WorldIdHandler.java b/src/main/java/com/sinthoras/visualprospecting/database/WorldIdHandler.java index e020d9b6..b714070c 100644 --- a/src/main/java/com/sinthoras/visualprospecting/database/WorldIdHandler.java +++ b/src/main/java/com/sinthoras/visualprospecting/database/WorldIdHandler.java @@ -1,11 +1,12 @@ package com.sinthoras.visualprospecting.database; -import com.sinthoras.visualprospecting.Tags; +import java.util.UUID; + import net.minecraft.nbt.NBTTagCompound; import net.minecraft.world.WorldSavedData; import net.minecraft.world.WorldServer; -import java.util.UUID; +import com.sinthoras.visualprospecting.Tags; public class WorldIdHandler extends WorldSavedData { diff --git a/src/main/java/com/sinthoras/visualprospecting/database/cachebuilder/AnalysisProgressTracker.java b/src/main/java/com/sinthoras/visualprospecting/database/cachebuilder/AnalysisProgressTracker.java index bd5c4ea0..65ad3477 100644 --- a/src/main/java/com/sinthoras/visualprospecting/database/cachebuilder/AnalysisProgressTracker.java +++ b/src/main/java/com/sinthoras/visualprospecting/database/cachebuilder/AnalysisProgressTracker.java @@ -1,12 +1,14 @@ package com.sinthoras.visualprospecting.database.cachebuilder; +import java.io.File; +import java.text.DecimalFormat; + +import net.minecraft.server.MinecraftServer; + import com.sinthoras.visualprospecting.Config; import com.sinthoras.visualprospecting.Utils; import com.sinthoras.visualprospecting.VP; -import net.minecraft.server.MinecraftServer; - -import java.io.File; -import java.text.DecimalFormat; +import com.sinthoras.visualprospecting.mixins.early.minecraft.MinecraftServerAccessor; public class AnalysisProgressTracker { @@ -29,19 +31,11 @@ public static synchronized void dimensionProcessed() { updateLog(); } - public static synchronized void announceFastDimension(int dimensionId) { - final String message = "Processing dimension with id " + dimensionId + " with fast scanning."; - VP.info(message); - if(Utils.isLogicalClient()) { - MinecraftServer.getServer().userMessage = message; - } - } - - public static synchronized void announceSlowDimension(int dimensionId) { - final String message = "Processing dimension with id " + dimensionId + " with slow (safe) scanning."; - VP.info(message); - if(Utils.isLogicalClient()) { - MinecraftServer.getServer().userMessage = message; + public static synchronized void announceDimension(int dimensionId) { + final String message = "Processing dimension with id " + dimensionId + "."; + VP.LOG.info(message); + if (Utils.isLogicalClient()) { + ((MinecraftServerAccessor) MinecraftServer.getServer()).setUserMessage(message); } } @@ -58,16 +52,19 @@ public static synchronized void regionFileProcessed() { private static synchronized void updateLog() { long timestamp = System.currentTimeMillis(); - if(timestamp - (Config.cacheGenerationLogUpdateMinTime * 1000) > lastLogUpdate) { + if (timestamp - (Config.cacheGenerationLogUpdateMinTime * 1000L) > lastLogUpdate) { lastLogUpdate = timestamp; - final String message = "Caching GT ore generation meta data - Dimension (" - + (dimensionsProcessed + 1) + "/" + numberOfDimensions + ") " - + (numberOfRegionFiles == 0 ? 0 : ((regionFilesProcessed * 100) / numberOfRegionFiles)) + "%"; - VP.info(message); - if(Utils.isLogicalClient()) { - MinecraftServer.getServer().userMessage = message + "%"; // Escape % for String.format + final String message = "Caching GT ore generation meta data - Dimension (" + (dimensionsProcessed + 1) + + "/" + + numberOfDimensions + + ") " + + (numberOfRegionFiles == 0 ? 0 : ((regionFilesProcessed * 100) / numberOfRegionFiles)) + + "%"; + VP.LOG.info(message); + if (Utils.isLogicalClient()) { + // Escape % for String.format + ((MinecraftServerAccessor) MinecraftServer.getServer()).setUserMessage(message + "%"); } - } } @@ -76,18 +73,20 @@ public static synchronized void processingFinished() { DecimalFormat format = new DecimalFormat(); format.setMinimumFractionDigits(1); format.setMaximumFractionDigits(1); - final String message = "Parsing complete! Thank you for your patience. - Duration: " + format.format(elapsedTimeMS / 1000) + "sec"; - VP.info(message); - if(Utils.isLogicalClient()) { - MinecraftServer.getServer().userMessage = message; + final String message = "Parsing complete! Thank you for your patience. - Duration: " + + format.format(elapsedTimeMS / 1000) + + "sec"; + VP.LOG.info(message); + if (Utils.isLogicalClient()) { + ((MinecraftServerAccessor) MinecraftServer.getServer()).setUserMessage(message); } } public static synchronized void notifyCorruptFile(File regionFile) { final String message = "Encountered corrupt/malformed/modified save file: " + regionFile; - VP.info(message); - if(Utils.isLogicalClient()) { - MinecraftServer.getServer().userMessage = message; + VP.LOG.info(message); + if (Utils.isLogicalClient()) { + ((MinecraftServerAccessor) MinecraftServer.getServer()).setUserMessage(message); } } } diff --git a/src/main/java/com/sinthoras/visualprospecting/database/cachebuilder/ChunkAnalysis.java b/src/main/java/com/sinthoras/visualprospecting/database/cachebuilder/ChunkAnalysis.java index 006c6fa6..8379866c 100644 --- a/src/main/java/com/sinthoras/visualprospecting/database/cachebuilder/ChunkAnalysis.java +++ b/src/main/java/com/sinthoras/visualprospecting/database/cachebuilder/ChunkAnalysis.java @@ -3,48 +3,61 @@ import com.sinthoras.visualprospecting.VP; import com.sinthoras.visualprospecting.database.veintypes.VeinType; import com.sinthoras.visualprospecting.database.veintypes.VeinTypeCaching; -import io.xol.enklume.nbt.*; -import java.util.HashSet; -import java.util.Set; +import gregtech.api.interfaces.IOreMaterial; +import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; // A slim, but faster version to identify >90% of veins public class ChunkAnalysis { - private final Set ores = new HashSet<>(); - private final Set matchedVeins = new HashSet<>(); + private VeinType matchedVein = VeinType.NO_VEIN; + private final Reference2IntOpenHashMap oreCounts = new Reference2IntOpenHashMap<>(); private int minVeinBlockY = VP.minecraftWorldHeight; + private IOreMaterial mostCommonOre; + private final String dimName; - public void processMinecraftChunk(final NBTCompound chunkRoot) { - for (final NBTNamed tileEntity : ((NBTList) chunkRoot.getTag("Level.TileEntities")).elements) { - final GregTechOre gtOre = new GregTechOre((NBTCompound) tileEntity); - if(gtOre.isValidGTOre) { - ores.add(gtOre.metaData); - if(minVeinBlockY > gtOre.blockY) { - minVeinBlockY = gtOre.blockY; - } + public ChunkAnalysis(String dimName) { + this.dimName = dimName; + } + + public void processMinecraftChunk(final PartiallyLoadedChunk chunk) { + chunk.forEachOre((x, y, z, info) -> { + oreCounts.addTo(info.material, 1); + + if (minVeinBlockY > y) { + minVeinBlockY = y; + } + }); + + int highestOreCount = -1; + for (var entry : oreCounts.reference2IntEntrySet()) { + if (entry.getIntValue() > highestOreCount) { + highestOreCount = entry.getIntValue(); + mostCommonOre = entry.getKey(); } } } public boolean matchesSingleVein() { - for(VeinType veinType : VeinTypeCaching.veinTypes) { - if(veinType.matches(ores)) { - matchedVeins.add(veinType); + if (oreCounts.isEmpty()) return true; + if (oreCounts.size() > 4) return false; + for (VeinType vein : VeinTypeCaching.getVeinTypesForOre(mostCommonOre)) { + if (vein.containsAllFoundOres(oreCounts.keySet(), dimName, mostCommonOre, minVeinBlockY)) { + if (matchedVein != VeinType.NO_VEIN) { + return false; + } + matchedVein = vein; } } - return matchedVeins.size() <= 1; + return matchedVein != VeinType.NO_VEIN; } // Result only valid if matchesSingleVein() returned true public VeinType getMatchedVein() { - if(matchedVeins.isEmpty()) { - return VeinType.NO_VEIN; - } - return matchedVeins.stream().findAny().get(); + return matchedVein; } public int getVeinBlockY() { return minVeinBlockY; } -} \ No newline at end of file +} diff --git a/src/main/java/com/sinthoras/visualprospecting/database/cachebuilder/DetailedChunkAnalysis.java b/src/main/java/com/sinthoras/visualprospecting/database/cachebuilder/DetailedChunkAnalysis.java index 835bd041..1aa8564e 100644 --- a/src/main/java/com/sinthoras/visualprospecting/database/cachebuilder/DetailedChunkAnalysis.java +++ b/src/main/java/com/sinthoras/visualprospecting/database/cachebuilder/DetailedChunkAnalysis.java @@ -1,15 +1,20 @@ package com.sinthoras.visualprospecting.database.cachebuilder; -import com.sinthoras.visualprospecting.VP; +import java.util.Set; +import java.util.stream.IntStream; + import com.sinthoras.visualprospecting.Utils; +import com.sinthoras.visualprospecting.VP; import com.sinthoras.visualprospecting.database.OreVeinPosition; import com.sinthoras.visualprospecting.database.ServerCache; import com.sinthoras.visualprospecting.database.veintypes.VeinType; import com.sinthoras.visualprospecting.database.veintypes.VeinTypeCaching; -import io.xol.enklume.nbt.*; -import java.util.*; -import java.util.stream.IntStream; +import gregtech.api.interfaces.IOreMaterial; +import it.unimi.dsi.fastutil.longs.Long2IntMap; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import it.unimi.dsi.fastutil.objects.ObjectSet; +import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; // Slower, but more sophisticated approach to identify overlapping veins public class DetailedChunkAnalysis { @@ -18,112 +23,141 @@ public class DetailedChunkAnalysis { public final int chunkX; public final int chunkZ; // For each height we count how often a ore (short) has occured - private final Map[] oresPerY = new HashMap[VP.minecraftWorldHeight]; + private final Reference2IntOpenHashMap[] oresPerY = new Reference2IntOpenHashMap[VP.minecraftWorldHeight]; + private final String dimName; + private VeinType resolvedVeinType = VeinType.NO_VEIN; - public DetailedChunkAnalysis(int dimensionId, int chunkX, int chunkZ) { + // Surrounding ore-chunk neighbours, to resolve overlap + private static final int[][] NEIGHBOR_OFFSETS = { { -3, 3 }, { 0, 3 }, { 3, 3 }, { 3, 0 }, { 3, -3 }, { 0, -3 }, + { -3, -3 }, { -3, 0 } }; + + public DetailedChunkAnalysis(int dimensionId, String dimName, int chunkX, int chunkZ) { this.dimensionId = dimensionId; this.chunkX = chunkX; this.chunkZ = chunkZ; + this.dimName = dimName; } - public void processMinecraftChunk(final NBTCompound chunkRoot) { - for (final NBTNamed tileEntity : ((NBTList) chunkRoot.getTag("Level.TileEntities")).elements) { - final GregTechOre gtOre = new GregTechOre((NBTCompound) tileEntity); - if(gtOre.isValidGTOre) { - if(oresPerY[gtOre.blockY] == null) { - oresPerY[gtOre.blockY] = new HashMap<>(); - } - if(oresPerY[gtOre.blockY].containsKey(gtOre.metaData) == false) { - oresPerY[gtOre.blockY].put(gtOre.metaData, 0); - } - oresPerY[gtOre.blockY].put(gtOre.metaData, oresPerY[gtOre.blockY].get(gtOre.metaData) + 1); + public void processMinecraftChunk(final PartiallyLoadedChunk chunk) { + chunk.forEachOre((x, y, z, info) -> { + if (oresPerY[y] == null) { + oresPerY[y] = new Reference2IntOpenHashMap<>(); } - } + + oresPerY[y].addTo(info.material, 1); + }); } - public void cleanUpWithNeighbors(final Map veinChunkY) { - final OreVeinPosition[] neighbors = new OreVeinPosition[] { - ServerCache.instance.getOreVein(dimensionId, chunkX - 3, chunkZ + 3), - ServerCache.instance.getOreVein(dimensionId, chunkX, chunkZ + 3), - ServerCache.instance.getOreVein(dimensionId, chunkX + 3, chunkZ + 3), - ServerCache.instance.getOreVein(dimensionId, chunkX + 3, chunkZ), - ServerCache.instance.getOreVein(dimensionId, chunkX + 3, chunkZ - 3), - ServerCache.instance.getOreVein(dimensionId, chunkX, chunkZ - 3), - ServerCache.instance.getOreVein(dimensionId, chunkX - 3, chunkZ - 3), - ServerCache.instance.getOreVein(dimensionId, chunkX - 3, chunkZ) - }; - final int[] neighborVeinBlockY = new int[] { - veinChunkY.getOrDefault(Utils.chunkCoordsToKey(Utils.mapToCenterOreChunkCoord(chunkX - 3), Utils.mapToCenterOreChunkCoord(chunkZ + 3)), 0), - veinChunkY.getOrDefault(Utils.chunkCoordsToKey(Utils.mapToCenterOreChunkCoord(chunkX), Utils.mapToCenterOreChunkCoord(chunkZ + 3)), 0), - veinChunkY.getOrDefault(Utils.chunkCoordsToKey(Utils.mapToCenterOreChunkCoord(chunkX + 3), Utils.mapToCenterOreChunkCoord(chunkZ + 3)), 0), - veinChunkY.getOrDefault(Utils.chunkCoordsToKey(Utils.mapToCenterOreChunkCoord(chunkX + 3), Utils.mapToCenterOreChunkCoord(chunkZ)), 0), - veinChunkY.getOrDefault(Utils.chunkCoordsToKey(Utils.mapToCenterOreChunkCoord(chunkX + 3), Utils.mapToCenterOreChunkCoord(chunkZ - 3)), 0), - veinChunkY.getOrDefault(Utils.chunkCoordsToKey(Utils.mapToCenterOreChunkCoord(chunkX), Utils.mapToCenterOreChunkCoord(chunkZ - 3)), 0), - veinChunkY.getOrDefault(Utils.chunkCoordsToKey(Utils.mapToCenterOreChunkCoord(chunkX - 3), Utils.mapToCenterOreChunkCoord(chunkZ - 3)), 0), - veinChunkY.getOrDefault(Utils.chunkCoordsToKey(Utils.mapToCenterOreChunkCoord(chunkX - 3), Utils.mapToCenterOreChunkCoord(chunkZ)), 0) - }; - - // Remove all generated ores from neighbors. They could also be generated in the same chunk, - // but that case is rare and therefore, neglected - for(int neighborId = 0; neighborId < neighbors.length; neighborId++) { - final OreVeinPosition neighbor = neighbors[neighborId]; - final boolean atCoordinateAxis = Math.abs(neighbor.chunkX - chunkX) < 3 || Math.abs(neighbor.chunkZ - chunkZ) < 3; - final boolean canOverlap = atCoordinateAxis ? neighbor.veinType.canOverlapIntoNeighborOreChunkAtCoordinateAxis() : neighbor.veinType.canOverlapIntoNeighborOreChunk(); - if (neighbor.veinType != VeinType.NO_VEIN && canOverlap) { - final int veinBlockY = neighborVeinBlockY[neighborId]; - for (int layerBlockY = 0; layerBlockY < VeinType.veinHeight; layerBlockY++) { - final int blockY = veinBlockY + layerBlockY; - if(blockY > 255) { - break; - } - if(oresPerY[blockY] != null) { - for (short metaData : neighbor.veinType.getOresAtLayer(layerBlockY)) { - oresPerY[blockY].remove(metaData); - } + // Resolve chunk vein against its neighbours and cache the result. + public void resolve(final Long2IntMap veinChunkY) { + cleanUpWithNeighbors(veinChunkY); + resolvedVeinType = getMatchedVein(); + } + + public VeinType getResolvedVeinType() { + return resolvedVeinType; + } + + private void cleanUpWithNeighbors(final Long2IntMap veinChunkY) { + // Remove ores that spilled over from neighbouring veins. + for (int[] neighborOffset : NEIGHBOR_OFFSETS) { + final int neighborChunkX = chunkX + neighborOffset[0]; + final int neighborChunkZ = chunkZ + neighborOffset[1]; + + final long neighborKey = Utils.chunkCoordsToKey( + Utils.mapToCenterOreChunkCoord(neighborChunkX), + Utils.mapToCenterOreChunkCoord(neighborChunkZ)); + if (!veinChunkY.containsKey(neighborKey)) continue; + final int neighborVeinBlockY = veinChunkY.get(neighborKey); + + final OreVeinPosition neighbor = ServerCache.instance + .getOreVein(dimensionId, neighborChunkX, neighborChunkZ); + if (neighbor.veinType == VeinType.NO_VEIN) continue; + + final boolean atCoordinateAxis = Math.abs(neighbor.chunkX - chunkX) < 3 + || Math.abs(neighbor.chunkZ - chunkZ) < 3; + final boolean canOverlap = atCoordinateAxis + ? neighbor.veinType.canOverlapIntoNeighborOreChunkAtCoordinateAxis() + : neighbor.veinType.canOverlapIntoNeighborOreChunk(); + if (!canOverlap) continue; + + for (int layerBlockY = 0; layerBlockY < VeinType.veinHeight; layerBlockY++) { + final int blockY = neighborVeinBlockY + layerBlockY; + if (blockY > 255) break; + + if (oresPerY[blockY] != null) { + for (IOreMaterial ore : neighbor.veinType.getOresAtLayer(layerBlockY)) { + oresPerY[blockY].removeInt(ore); } } } } } - public VeinType getMatchedVein() { - final Set matchedVeins = new HashSet<>(); + private VeinType getMatchedVein() { + final ObjectSet matchedVeins = new ObjectOpenHashSet<>(); + + final Reference2IntOpenHashMap allOres = new Reference2IntOpenHashMap<>(); - final Map allOres = new HashMap<>(); - for(Map oreLevel : oresPerY) { - if (oreLevel != null && oreLevel.isEmpty() == false) { - oreLevel.forEach((metaData, numberOfBlocks) -> allOres.merge(metaData, numberOfBlocks, Integer::sum)); + for (Reference2IntOpenHashMap oreLevel : oresPerY) { + if (oreLevel == null || oreLevel.isEmpty()) continue; + + for (var entry : oreLevel.reference2IntEntrySet()) { + allOres.addTo(entry.getKey(), entry.getIntValue()); } } - final Optional dominantOre = allOres.entrySet().stream() - .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) - .map(Map.Entry::getKey).findFirst(); - if(dominantOre.isPresent()) { - for(VeinType veinType : VeinTypeCaching.veinTypes) { - if(veinType.matchesWithSpecificPrimaryOrSecondary(allOres.keySet(), dominantOre.get())) { - matchedVeins.add(veinType); - } + if (allOres.isEmpty()) return VeinType.NO_VEIN; + + IOreMaterial dominantOre = null; + int highestOreCount = -1; + for (var entry : allOres.reference2IntEntrySet()) { + if (entry.getIntValue() > highestOreCount) { + highestOreCount = entry.getIntValue(); + dominantOre = entry.getKey(); } } - if(matchedVeins.size() == 1) { - return matchedVeins.stream().findAny().get(); - } - else if(matchedVeins.size() >= 2) { - matchedVeins.removeIf(veinType -> IntStream.range(veinType.minBlockY, veinType.maxBlockY).boxed().noneMatch(blockY -> isOreVeinGeneratedAtHeight(veinType, blockY))); + if (dominantOre == null) return VeinType.NO_VEIN; - if(matchedVeins.size() == 1) { - return matchedVeins.stream().findAny().get(); + for (VeinType veinType : VeinTypeCaching.getVeinTypesForOre(dominantOre)) { + if (veinType.matchesWithSpecificPrimaryOrSecondary(allOres.keySet(), dimName, dominantOre)) { + matchedVeins.add(veinType); } + } + + if (matchedVeins.size() == 1) { + return matchedVeins.iterator().next(); + } + if (matchedVeins.size() >= 2) { + matchedVeins.removeIf( + veinType -> IntStream.range(veinType.minBlockY, veinType.maxBlockY) + .noneMatch(blockY -> isOreVeinGeneratedAtHeight(veinType, blockY))); + return matchedVeins.size() == 1 ? matchedVeins.iterator().next() : VeinType.NO_VEIN; + } + if (ServerCache.instance.getOreVein(dimensionId, chunkX, chunkZ).veinType != VeinType.NO_VEIN) { + return VeinType.NO_VEIN; + } + return matchWithOneMissingOre(allOres.keySet(), dominantOre); + } + private VeinType matchWithOneMissingOre(Set foundOres, IOreMaterial dominantOre) { + VeinType onlyMatch = VeinType.NO_VEIN; + for (VeinType veinType : VeinTypeCaching.getVeinTypesForOre(dominantOre)) { + if (veinType.matchesWithOneMissingOre(foundOres, dimName, dominantOre)) { + if (onlyMatch != VeinType.NO_VEIN) { + return VeinType.NO_VEIN; + } + onlyMatch = veinType; + } } - return VeinType.NO_VEIN; + return onlyMatch; } private boolean isOreVeinGeneratedAtHeight(VeinType veinType, int blockY) { - for(int layer = 0; layer < VeinType.veinHeight; layer++) { - if(oresPerY[blockY + layer] == null || oresPerY[blockY + layer].keySet().containsAll(veinType.getOresAtLayer(layer)) == false) { + for (int layer = 0; layer < VeinType.veinHeight; layer++) { + if (oresPerY[blockY + layer] == null + || !oresPerY[blockY + layer].keySet().containsAll(veinType.getOresAtLayer(layer))) { return false; } } diff --git a/src/main/java/com/sinthoras/visualprospecting/database/cachebuilder/DimensionAnalysis.java b/src/main/java/com/sinthoras/visualprospecting/database/cachebuilder/DimensionAnalysis.java index 3c976f60..57ccc117 100644 --- a/src/main/java/com/sinthoras/visualprospecting/database/cachebuilder/DimensionAnalysis.java +++ b/src/main/java/com/sinthoras/visualprospecting/database/cachebuilder/DimensionAnalysis.java @@ -1,123 +1,245 @@ package com.sinthoras.visualprospecting.database.cachebuilder; -import com.sinthoras.visualprospecting.Config; -import com.sinthoras.visualprospecting.VP; -import com.sinthoras.visualprospecting.Utils; -import com.sinthoras.visualprospecting.database.ServerCache; -import com.sinthoras.visualprospecting.database.veintypes.VeinType; -import io.xol.enklume.MinecraftRegion; -import io.xol.enklume.MinecraftWorld; -import io.xol.enklume.nbt.NBTCompound; - import java.io.File; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.DataFormatException; +import net.minecraft.nbt.NBTTagCompound; + +import com.github.bsideup.jabel.Desugar; +import com.sinthoras.visualprospecting.Config; +import com.sinthoras.visualprospecting.Utils; +import com.sinthoras.visualprospecting.VP; +import com.sinthoras.visualprospecting.database.DimensionNameResolver; +import com.sinthoras.visualprospecting.database.ServerCache; +import com.sinthoras.visualprospecting.database.VeinSource; +import com.sinthoras.visualprospecting.database.veintypes.VeinType; + +import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.longs.Long2IntMap; +import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; + public class DimensionAnalysis { + private static final Pattern REGION_FILE_NAME = Pattern.compile("^r\\.-?\\d+\\.-?\\d+\\.mca$"); + private static final int INVALID_REGION_COORD = Integer.MIN_VALUE; + public final int dimensionId; + public final String dimensionName; public DimensionAnalysis(int dimensionId) { this.dimensionId = dimensionId; + this.dimensionName = DimensionNameResolver.resolve(dimensionId); } private interface IChunkHandler { - void processChunk(NBTCompound root, int chunkX, int chunkZ); + + void processChunk(PartiallyLoadedChunk chunk, int chunkX, int chunkZ); + } + + public void processMinecraftWorld(Collection regionFiles) { + if (regionFiles.isEmpty()) return; + + AnalysisProgressTracker.announceDimension(dimensionId); + + // Group region files by rows, skipping any with malformed names + final Int2ObjectAVLTreeMap> regionFilesByRow = new Int2ObjectAVLTreeMap<>(); + int malformedRegionFileCount = 0; + for (File regionFile : regionFiles) { + final int regionZ = parseRegionZ(regionFile); + if (regionZ == INVALID_REGION_COORD) { + VP.LOG.warn("Invalid region file found! {} continuing", regionFile.getAbsolutePath()); + malformedRegionFileCount++; + } else { + regionFilesByRow.computeIfAbsent(regionZ, value -> new ArrayList<>()).add(regionFile); + } + } + + final long maxRowSizeMBForHolding = Config.maxRegionRowFileMBForInMemoryScan; + int totalRegionFilePasses = regionFiles.size() - malformedRegionFileCount; + for (List row : regionFilesByRow.values()) { + if (rowSizeMB(row) > maxRowSizeMBForHolding) { + totalRegionFilePasses += row.size(); + } + } + AnalysisProgressTracker.setNumberOfRegionFiles(totalRegionFilePasses); + + final Long2IntOpenHashMap veinBlockY = new Long2IntOpenHashMap(); + + final int[] rows = regionFilesByRow.keySet().toIntArray(); + final Int2ObjectOpenHashMap heldRows = new Int2ObjectOpenHashMap<>(); + int rowToFinalize = 0; + for (int i = 0; i < rows.length; i++) { + final int regionZ = rows[i]; + heldRows.put(regionZ, fastPassRow(regionFilesByRow.get(regionZ), veinBlockY, maxRowSizeMBForHolding)); + + // Once both below and above rows are processed it's safe to finalize the middle one. + while (rowToFinalize < i && rows[rowToFinalize] + 1 <= regionZ) { + finalizeRow(heldRows.remove(rows[rowToFinalize]), veinBlockY); + rowToFinalize++; + } + } + while (rowToFinalize < rows.length) { + finalizeRow(heldRows.remove(rows[rowToFinalize]), veinBlockY); + rowToFinalize++; + } } - public void processMinecraftWorld(MinecraftWorld world) throws IOException { - final Map veinBlockY = new ConcurrentHashMap<>(); - final List regionFiles = world.getAllRegionFiles(dimensionId); - final long dimensionSizeMB = regionFiles.stream().mapToLong(File::length).sum() >> 20; - - if (dimensionSizeMB <= Config.maxDimensionSizeMBForFastScanning) { - AnalysisProgressTracker.announceFastDimension(dimensionId); - AnalysisProgressTracker.setNumberOfRegionFiles(regionFiles.size()); - - final Map chunksForSecondIdentificationPass = new ConcurrentHashMap<>(); - - regionFiles.parallelStream().forEach(regionFile -> { - executeForEachGeneratedOreChunk(regionFile, (root, chunkX, chunkZ) -> { - final ChunkAnalysis chunk = new ChunkAnalysis(); - chunk.processMinecraftChunk(root); - - if (chunk.matchesSingleVein()) { - ServerCache.instance.notifyOreVeinGeneration(dimensionId, chunkX, chunkZ, chunk.getMatchedVein()); - veinBlockY.put(Utils.chunkCoordsToKey(chunkX, chunkZ), chunk.getVeinBlockY()); - } else { - final DetailedChunkAnalysis detailedChunk = new DetailedChunkAnalysis(dimensionId, chunkX, chunkZ); - detailedChunk.processMinecraftChunk(root); - chunksForSecondIdentificationPass.put(Utils.chunkCoordsToKey(chunkX, chunkZ), detailedChunk); + // Fast-pass one region row. write single vein match, hold on ambiguous matches for later. + private RegionRowResult fastPassRow(List rowRegionFiles, Long2IntMap veinBlockY, + long maxRowSizeMBForHolding) { + final boolean holdInMemory = rowSizeMB(rowRegionFiles) <= maxRowSizeMBForHolding; + final Queue singleMatches = new ConcurrentLinkedQueue<>(); + final Queue ambiguousChunks = holdInMemory ? new ConcurrentLinkedQueue<>() : null; + + rowRegionFiles.parallelStream() + .forEach(regionFile -> executeForEachGeneratedOreChunk(regionFile, (chunk, chunkX, chunkZ) -> { + final ChunkAnalysis analysis = new ChunkAnalysis(dimensionName); + analysis.processMinecraftChunk(chunk); + + if (analysis.matchesSingleVein()) { + final VeinType veinType = analysis.getMatchedVein(); + if (veinType != VeinType.NO_VEIN) { + singleMatches.add(new PendingOreVein(chunkX, chunkZ, veinType, analysis.getVeinBlockY())); + } + } else if (holdInMemory) { + final DetailedChunkAnalysis detailedChunk = new DetailedChunkAnalysis( + dimensionId, + dimensionName, + chunkX, + chunkZ); + detailedChunk.processMinecraftChunk(chunk); + ambiguousChunks.add(detailedChunk); } - }); - }); + })); + + for (PendingOreVein singleMatch : singleMatches) { + ServerCache.instance.notifyOreVeinGeneration( + dimensionId, + singleMatch.chunkX, + singleMatch.chunkZ, + singleMatch.veinType, + VeinSource.RESCAN); + veinBlockY.put(Utils.chunkCoordsToKey(singleMatch.chunkX, singleMatch.chunkZ), singleMatch.veinBlockY); + } - chunksForSecondIdentificationPass.values().parallelStream().forEach(chunk -> { - chunk.cleanUpWithNeighbors(veinBlockY); - ServerCache.instance.notifyOreVeinGeneration(dimensionId, chunk.chunkX, chunk.chunkZ, chunk.getMatchedVein()); - }); + return holdInMemory ? RegionRowResult.held(ambiguousChunks) : RegionRowResult.reRead(rowRegionFiles); + } + + // Resolve a row's overlapping chunks against their neighbours + private void finalizeRow(RegionRowResult row, Long2IntMap veinBlockY) { + if (row == null) return; + + if (row.heldAmbiguousChunks != null) { + row.heldAmbiguousChunks.parallelStream().forEach(detailedChunk -> detailedChunk.resolve(veinBlockY)); + for (DetailedChunkAnalysis detailedChunk : row.heldAmbiguousChunks) { + ServerCache.instance.notifyOreVeinGeneration( + dimensionId, + detailedChunk.chunkX, + detailedChunk.chunkZ, + detailedChunk.getResolvedVeinType(), + VeinSource.RESCAN); + } } else { - AnalysisProgressTracker.announceSlowDimension(dimensionId); - AnalysisProgressTracker.setNumberOfRegionFiles(regionFiles.size() * 2); + final Queue resolved = new ConcurrentLinkedQueue<>(); + row.rowRegionFiles.parallelStream() + .forEach(regionFile -> executeForEachGeneratedOreChunk(regionFile, (chunk, chunkX, chunkZ) -> { + if (ServerCache.instance.getOreVein(dimensionId, chunkX, chunkZ).veinType == VeinType.NO_VEIN) { + final DetailedChunkAnalysis detailedChunk = new DetailedChunkAnalysis( + dimensionId, + dimensionName, + chunkX, + chunkZ); + detailedChunk.processMinecraftChunk(chunk); + detailedChunk.resolve(veinBlockY); + if (detailedChunk.getResolvedVeinType() != VeinType.NO_VEIN) { + resolved.add( + new PendingOreVein(chunkX, chunkZ, detailedChunk.getResolvedVeinType(), 0)); + } + } + })); + for (PendingOreVein result : resolved) { + ServerCache.instance.notifyOreVeinGeneration( + dimensionId, + result.chunkX, + result.chunkZ, + result.veinType, + VeinSource.RESCAN); + } + } + } - regionFiles.parallelStream().forEach(regionFile -> { - executeForEachGeneratedOreChunk(regionFile, (root, chunkX, chunkZ) -> { - final ChunkAnalysis chunk = new ChunkAnalysis(); - chunk.processMinecraftChunk(root); + private static int parseRegionZ(File regionFile) { + final Matcher matcher = REGION_FILE_NAME.matcher(regionFile.getName()); + if (!matcher.matches()) return INVALID_REGION_COORD; + try { + return Integer.parseInt(regionFile.getName().split("\\.")[2]); + } catch (NumberFormatException ignored) { + return INVALID_REGION_COORD; + } + } - if (chunk.matchesSingleVein()) { - ServerCache.instance.notifyOreVeinGeneration(dimensionId, chunkX, chunkZ, chunk.getMatchedVein()); - veinBlockY.put(Utils.chunkCoordsToKey(chunkX, chunkZ), chunk.getVeinBlockY()); - } - }); - }); - - regionFiles.parallelStream().forEach(regionFile -> { - executeForEachGeneratedOreChunk(regionFile, (root, chunkX, chunkZ) -> { - if (ServerCache.instance.getOreVein(dimensionId, chunkX, chunkZ).veinType == VeinType.NO_VEIN) { - final DetailedChunkAnalysis detailedChunk = new DetailedChunkAnalysis(dimensionId, chunkX, chunkZ); - detailedChunk.processMinecraftChunk(root); - detailedChunk.cleanUpWithNeighbors(veinBlockY); - ServerCache.instance.notifyOreVeinGeneration(dimensionId, detailedChunk.chunkX, detailedChunk.chunkZ, detailedChunk.getMatchedVein()); - } - }); - }); + private static long rowSizeMB(List regionFiles) { + return regionFiles.stream().mapToLong(File::length).sum() >> 20; + } + + @Desugar + private record RegionRowResult(Collection heldAmbiguousChunks, List rowRegionFiles) { + + static RegionRowResult held(Collection heldAmbiguousChunks) { + return new RegionRowResult(heldAmbiguousChunks, null); } + + static RegionRowResult reRead(List rowRegionFiles) { + return new RegionRowResult(null, rowRegionFiles); + } + } + + @Desugar + private record PendingOreVein(int chunkX, int chunkZ, VeinType veinType, int veinBlockY) { + } private void executeForEachGeneratedOreChunk(File regionFile, IChunkHandler chunkHandler) { try { - if ( !Pattern.matches("^r\\.-?\\d+\\.-?\\d+\\.mca$", regionFile.getName())) { - VP.warn("Invalid region file found! " + regionFile.getCanonicalPath() + " continuing"); - return; - } final String[] parts = regionFile.getName().split("\\."); final int regionChunkX = Integer.parseInt(parts[1]) << 5; final int regionChunkZ = Integer.parseInt(parts[2]) << 5; - final MinecraftRegion region = new MinecraftRegion(regionFile); - for (int localChunkX = 0; localChunkX < VP.chunksPerRegionFileX; localChunkX++) { - for (int localChunkZ = 0; localChunkZ < VP.chunksPerRegionFileZ; localChunkZ++) { - final int chunkX = regionChunkX + localChunkX; - final int chunkZ = regionChunkZ + localChunkZ; - - // Only process ore chunks - if (chunkX == Utils.mapToCenterOreChunkCoord(chunkX) && chunkZ == Utils.mapToCenterOreChunkCoord(chunkZ)) { - // Helpful read about 'root' structure: https://minecraft.fandom.com/wiki/Chunk_format - final NBTCompound root = region.getChunk(localChunkX, localChunkZ).getRootTag(); - - // root == null occurs when a chunk is not yet generated - if (root != null) { - chunkHandler.processChunk(root, chunkX, chunkZ); + try (RegionReader region = new RegionReader(regionFile)) { + for (int localChunkX = 0; localChunkX < VP.chunksPerRegionFileX; localChunkX++) { + for (int localChunkZ = 0; localChunkZ < VP.chunksPerRegionFileZ; localChunkZ++) { + final int chunkX = regionChunkX + localChunkX; + final int chunkZ = regionChunkZ + localChunkZ; + + // Only process ore chunks + if (chunkX != Utils.mapToCenterOreChunkCoord(chunkX)) continue; + if (chunkZ != Utils.mapToCenterOreChunkCoord(chunkZ)) continue; + + // Skip chunks already cached with higher trustLevel than VeinSource.RESCAN + if (!VeinSource.RESCAN.canOverwrite( + ServerCache.instance.getOreVein(dimensionId, chunkX, chunkZ).getSource())) { + continue; } + + NBTTagCompound chunk = region.getChunk(localChunkX, localChunkZ); + + if (chunk == null) continue; + + PartiallyLoadedChunk loadedChunk = new PartiallyLoadedChunk(); + loadedChunk.load(chunk, chunkX, chunkZ); + + chunkHandler.processChunk(loadedChunk, chunkX, chunkZ); } } } - region.close(); AnalysisProgressTracker.regionFileProcessed(); } catch (DataFormatException | IOException e) { AnalysisProgressTracker.notifyCorruptFile(regionFile); diff --git a/src/main/java/com/sinthoras/visualprospecting/database/cachebuilder/GregTechOre.java b/src/main/java/com/sinthoras/visualprospecting/database/cachebuilder/GregTechOre.java deleted file mode 100644 index 31705e6a..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/database/cachebuilder/GregTechOre.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.sinthoras.visualprospecting.database.cachebuilder; - -import io.xol.enklume.nbt.NBTCompound; -import io.xol.enklume.nbt.NBTInt; -import io.xol.enklume.nbt.NBTShort; -import io.xol.enklume.nbt.NBTString; - -import static com.sinthoras.visualprospecting.Utils.isSmallOreId; -import static com.sinthoras.visualprospecting.Utils.oreIdToMaterialId; - -public class GregTechOre { - - public final boolean isValidGTOre; - public final short metaData; - public final int blockY; - - public GregTechOre(NBTCompound tileEntity) { - final NBTString tagId = (NBTString) tileEntity.getTag("id"); - final NBTShort tagMeta = (NBTShort) tileEntity.getTag("m"); - final NBTInt tagBlockY = (NBTInt) tileEntity.getTag("y"); - - if (tagId != null && tagId.getText().equals("GT_TileEntity_Ores") && tagBlockY != null - && tagMeta != null && isSmallOreId(tagMeta.data) == false) { - metaData = oreIdToMaterialId(tagMeta.data); - isValidGTOre = true; - blockY = tagBlockY.data; - } - else { - isValidGTOre = false; - metaData = 0; - blockY = 0; - } - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/database/cachebuilder/PartiallyLoadedChunk.java b/src/main/java/com/sinthoras/visualprospecting/database/cachebuilder/PartiallyLoadedChunk.java new file mode 100644 index 00000000..4804fc99 --- /dev/null +++ b/src/main/java/com/sinthoras/visualprospecting/database/cachebuilder/PartiallyLoadedChunk.java @@ -0,0 +1,350 @@ +package com.sinthoras.visualprospecting.database.cachebuilder; + +import java.nio.ByteBuffer; +import java.nio.ShortBuffer; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import net.minecraft.block.Block; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.storage.ExtendedBlockStorage; +import net.minecraftforge.common.util.Constants.NBT; + +import com.gtnewhorizon.gtnhlib.util.data.ImmutableBlockMeta; +import com.sinthoras.visualprospecting.VP; + +import bartworks.system.material.Werkstoff; +import gregtech.api.enums.Materials; +import gregtech.api.interfaces.IOreMaterial; +import gregtech.common.ores.BWOreAdapter; +import gregtech.common.ores.GTOreAdapter; +import gregtech.common.ores.OreInfo; +import gregtech.common.ores.OreManager; +import it.unimi.dsi.fastutil.ints.Int2IntFunction; + +public class PartiallyLoadedChunk { + + private static final int SECTION_SIZE = 16; + private static final int BLOCKS_PER_EBS = SECTION_SIZE * SECTION_SIZE * SECTION_SIZE; + public static final int CHUNK_HEIGHT = 256; + private static final int SECTIONS_PER_CHUNK = CHUNK_HEIGHT / SECTION_SIZE; + private static final int NIBBLE_ARRAY_SIZE = BLOCKS_PER_EBS / 2; // Expected byte-array lengths for nibble arrays + + private final Int2IntFunction[] blocks = new Int2IntFunction[SECTIONS_PER_CHUNK]; + private final Int2IntFunction[] metas = new Int2IntFunction[SECTIONS_PER_CHUNK]; + + private List tiles; + + @SuppressWarnings("unchecked") + public void load(NBTTagCompound chunk, int chunkX, int chunkZ) { + NBTTagList sections = chunk.getCompoundTag("Level").getTagList("Sections", NBT.TAG_COMPOUND); + + List sectionTags = (List) sections.tagList; + + for (NBTTagCompound section : sectionTags) { + byte y = section.getByte("Y"); + + if (section.hasKey("Blocks16")) { + // NotEnoughIDs format: full 16-bit block IDs and metas stored as ShortBuffers. + loadNotEnoughIds(section, chunkX, y, chunkZ); + + } else if (section.hasKey("Add") || section.hasKey("BlocksB2Hi") || section.hasKey("Data1High")) { + // EndlessIDs format: block ID and meta split across multiple nibble/byte arrays. + // + // Block ID layout (24 bits total): + // bits 0x0000FF — "Blocks" byte array (vanilla low 8 bits) + // bits 0x000F00 — "Add" nibble array (NotEnoughIDs extension to 12 bits) + // bits 0x00F000 — "BlocksB2Hi" nibble array (EndlessIDs extension to 16 bits) + // bits 0xFF0000 — "BlocksB3" byte array (EndlessIDs extension to 24 bits) + // + // Meta layout (16 bits total): + // bits 0x000F — "Data" nibble array (vanilla low 4 bits) + // bits 0x00F0 — "Data1High" nibble array (EndlessIDs extension) + // bits 0xFF00 — "Data2" byte array (EndlessIDs extension, 1 byte per block) + // + // "Add", "BlocksB2Hi", "BlocksB3", "Data1High", and "Data2" are all optional + // Reference: + // https://github.com/GTMEGA/EndlessIDs/blob/master/src/main/java/com/falsepattern/endlessids/managers/BlockIDManager.java + // https://github.com/GTMEGA/EndlessIDs/blob/master/src/main/java/com/falsepattern/endlessids/managers/BlockMetaManager.java + loadEndlessIds(section, chunkX, y, chunkZ); + + } else { + // Vanilla format or unrecognized: skip block data. + // Old chunks only have ore TileEntities. + this.blocks[y] = i -> 0; + this.metas[y] = i -> 0; + } + } + + tiles = chunk.getCompoundTag("Level").getTagList("TileEntities", NBT.TAG_COMPOUND).tagList; + } + + /** + * Populates this chunk from an in-memory Chunk, to allow for real time debug command. + */ + public void loadFromLiveChunk(Chunk chunk) { + final ExtendedBlockStorage[] storage = chunk.getBlockStorageArray(); + for (int section = 0; section < SECTIONS_PER_CHUNK; section++) { + final ExtendedBlockStorage ebs = section < storage.length ? storage[section] : null; + if (ebs == null) { + this.blocks[section] = i -> 0; + this.metas[section] = i -> 0; + continue; + } + // withinSection index layout (matches forEachOre): bits 0-3 = x, 4-7 = z, 8-11 = y + this.blocks[section] = i -> Block + .getIdFromBlock(ebs.getBlockByExtId(i & 0xF, (i >> 8) & 0xF, (i >> 4) & 0xF)); + this.metas[section] = i -> ebs.getExtBlockMetadata(i & 0xF, (i >> 8) & 0xF, (i >> 4) & 0xF); + } + + this.tiles = new ArrayList<>(); + for (TileEntity tileEntity : chunk.chunkTileEntityMap.values()) { + final NBTTagCompound tag = new NBTTagCompound(); + tileEntity.writeToNBT(tag); + this.tiles.add(tag); + } + } + + /** + * Loads a chunk section saved in NotEnoughIDs format ("Blocks16" / "Data16"). Block IDs and metas are stored as + * flat ShortBuffers - one short per block, no bit packing. + */ + private void loadNotEnoughIds(NBTTagCompound section, int chunkX, byte y, int chunkZ) { + ShortBuffer blocks = ByteBuffer.wrap(section.getByteArray("Blocks16")).asShortBuffer(); + ShortBuffer metas = ByteBuffer.wrap(section.getByteArray("Data16")).asShortBuffer(); + + if (blocks.capacity() == 0 || metas.capacity() == 0) { + this.blocks[y] = i -> 0; + this.metas[y] = i -> 0; + return; + } + + if (blocks.capacity() != BLOCKS_PER_EBS) { + VP.LOG.error( + "Corrupt NotEnoughIDs section at X={}, Y={}, Z={}: Blocks16 length {} (expected {})", + chunkX, + y, + chunkZ, + blocks.capacity(), + BLOCKS_PER_EBS); + return; + } + + if (metas.capacity() != BLOCKS_PER_EBS) { + VP.LOG.error( + "Corrupt NotEnoughIDs section at X={}, Y={}, Z={}: Data16 length {} (expected {})", + chunkX, + y, + chunkZ, + metas.capacity(), + BLOCKS_PER_EBS); + return; + } + + // Mask with 0xFFFF to avoid sign extension when widening short -> int (IDs/metas >= 0x8000). + this.blocks[y] = i -> blocks.get(i) & 0xFFFF; + this.metas[y] = i -> metas.get(i) & 0xFFFF; + } + + /** + * Loads a chunk section saved in EndlessIDs format. "Blocks" and "Data" are mandatory; all other arrays are + * optional and treated as all-zero when absent. A corrupt mandatory array zeroes the whole section. + */ + private void loadEndlessIds(NBTTagCompound section, int chunkX, byte y, int chunkZ) { + ByteBuffer blockB1 = loadOptionalArray(section, "Blocks", BLOCKS_PER_EBS, chunkX, y, chunkZ); + ByteBuffer metaM1Lo = loadOptionalArray(section, "Data", NIBBLE_ARRAY_SIZE, chunkX, y, chunkZ); + if (blockB1 == null || metaM1Lo == null) { + VP.LOG.error( + "Corrupt EndlessIDs section at X={}, Y={}, Z={}: missing or invalid mandatory Blocks/Data array", + chunkX, + y, + chunkZ); + this.blocks[y] = i -> 0; + this.metas[y] = i -> 0; + return; + } + + ByteBuffer blockB2Lo = loadOptionalArray(section, "Add", NIBBLE_ARRAY_SIZE, chunkX, y, chunkZ); + ByteBuffer blockB2Hi = loadOptionalArray(section, "BlocksB2Hi", NIBBLE_ARRAY_SIZE, chunkX, y, chunkZ); + ByteBuffer blockB3 = loadOptionalArray(section, "BlocksB3", BLOCKS_PER_EBS, chunkX, y, chunkZ); + + this.blocks[y] = i -> { + int nibbleShift = (i & 1) * 4; + int id = blockB1.get(i) & 0xFF; + if (blockB2Lo != null) id |= ((blockB2Lo.get(i >> 1) >> nibbleShift) & 0xF) << 8; + if (blockB2Hi != null) id |= ((blockB2Hi.get(i >> 1) >> nibbleShift) & 0xF) << 12; + if (blockB3 != null) id |= (blockB3.get(i) & 0xFF) << 16; + return id; + }; + + ByteBuffer metaM1Hi = loadOptionalArray(section, "Data1High", NIBBLE_ARRAY_SIZE, chunkX, y, chunkZ); + ByteBuffer metaM2 = loadOptionalArray(section, "Data2", BLOCKS_PER_EBS, chunkX, y, chunkZ); + + this.metas[y] = i -> { + int nibbleShift = (i & 1) * 4; + int meta = (metaM1Lo.get(i >> 1) >> nibbleShift) & 0xF; + if (metaM1Hi != null) meta |= ((metaM1Hi.get(i >> 1) >> nibbleShift) & 0xF) << 4; + if (metaM2 != null) meta |= (metaM2.get(i) & 0xFF) << 8; + return meta; + }; + } + + /** + * Reads an optional byte array from a section NBT compound. Validates the length against {@code expectedLength} and + * returns null if the key is absent or the array is malformed. + */ + private ByteBuffer loadOptionalArray(NBTTagCompound section, String key, int expectedLength, int chunkX, byte y, + int chunkZ) { + if (!section.hasKey(key)) return null; + byte[] data = section.getByteArray(key); + if (data.length != expectedLength) { + VP.LOG.error( + "Corrupt EndlessIDs section at X={}, Y={}, Z={}: {} length {} (expected {})", + chunkX, + y, + chunkZ, + key, + data.length, + expectedLength); + return null; + } + return ByteBuffer.wrap(data); + } + + public interface OreConsumer { + + void visit(int x, int y, int z, OreInfo ore); + } + + @SuppressWarnings("unchecked") + public void forEachOre(OreConsumer consumer) { + for (PartiallyLoadedChunk.TileEntityInfo te : tileEntities()) { + if (!te.getId().equals("GT_TileEntity_Ores")) continue; + + short meta = te.getTag().getShort("m"); + boolean natural = te.getTag().getBoolean("n"); + + if (!natural) continue; + + ImmutableBlockMeta bm = GTOreAdapter.INSTANCE.transform(meta, true); + + try (OreInfo info = GTOreAdapter.INSTANCE.getOreInfo(bm.getBlock(), bm.getBlockMeta())) { + if (info == null || info.isSmall || info.material == null) continue; + + consumer.visit(te.getX(), te.getY(), te.getZ(), (OreInfo) (OreInfo) info); + } + } + + for (PartiallyLoadedChunk.TileEntityInfo te : tileEntities()) { + if (!te.getId().equals("bw.blockoresTE")) continue; + + short meta = te.getTag().getShort("m"); + boolean natural = te.getTag().getBoolean("n"); + + if (!natural) continue; + + ImmutableBlockMeta bm = BWOreAdapter.INSTANCE.transform(meta, true, false); + + try (OreInfo info = BWOreAdapter.INSTANCE.getOreInfo(bm.getBlock(), bm.getBlockMeta())) { + if (info == null || info.isSmall || info.material == null) continue; + + consumer.visit(te.getX(), te.getY(), te.getZ(), (OreInfo) (OreInfo) info); + } + } + + // Iterate per-section so the blocks/metas array lookups are done out of the inner loop + for (int section = 0; section < SECTIONS_PER_CHUNK; section++) { + Int2IntFunction blocks = this.blocks[section]; + Int2IntFunction metas = this.metas[section]; + + int baseY = section << 4; + for (int dy = 0; dy < SECTION_SIZE; dy++) { + int y = baseY + dy; + for (int z = 0; z < SECTION_SIZE; z++) { + for (int x = 0; x < SECTION_SIZE; x++) { + int withinSection = (dy << 8) | (z << 4) | x; + int id = blocks == null ? 0 : blocks.get(withinSection); + int meta = metas == null ? 0 : metas.get(withinSection); + Block block = Block.getBlockById(id); + + try (OreInfo info = OreManager.getOreInfo(block, meta)) { + if (info == null || info.isSmall || !info.isNatural || info.material == null) continue; + + consumer.visit(x, y, z, info); + } + } + } + } + } + } + + public interface TileEntityInfo { + + String getId(); + + int getX(); + + int getY(); + + int getZ(); + + NBTTagCompound getTag(); + } + + public Iterable tileEntities() { + return TileEntityIterator::new; + } + + private class TileEntityIterator implements Iterator { + + private final TileEntityInfoImpl info = new TileEntityInfoImpl(); + + private int tileIndex; + + @Override + public boolean hasNext() { + return tileIndex < tiles.size(); + } + + @Override + public TileEntityInfo next() { + info.tag = tiles.get(tileIndex++); + + return info; + } + } + + private static class TileEntityInfoImpl implements TileEntityInfo { + + public NBTTagCompound tag; + + @Override + public String getId() { + return tag.getString("id"); + } + + @Override + public int getX() { + return tag.getInteger("x"); + } + + @Override + public int getY() { + return tag.getInteger("y"); + } + + @Override + public int getZ() { + return tag.getInteger("z"); + } + + @Override + public NBTTagCompound getTag() { + return tag; + } + } +} diff --git a/src/main/java/com/sinthoras/visualprospecting/database/cachebuilder/RegionReader.java b/src/main/java/com/sinthoras/visualprospecting/database/cachebuilder/RegionReader.java new file mode 100644 index 00000000..5f9be3a7 --- /dev/null +++ b/src/main/java/com/sinthoras/visualprospecting/database/cachebuilder/RegionReader.java @@ -0,0 +1,82 @@ +package com.sinthoras.visualprospecting.database.cachebuilder; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.util.zip.DataFormatException; +import java.util.zip.InflaterInputStream; + +import javax.annotation.Nullable; + +import net.minecraft.nbt.CompressedStreamTools; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; + +import com.sinthoras.visualprospecting.VP; + +public class RegionReader implements AutoCloseable { + + private final RandomAccessFile is; + + public RegionReader(File regionFile) throws IOException, DataFormatException { + is = new RandomAccessFile(regionFile, "r"); + } + + private int offset(int x, int z) { + return ((x & 31) + (z & 31) * 32); + } + + public @Nullable NBTTagCompound getChunk(int x, int z) throws DataFormatException, IOException { + return getChunkInternal(x, z); + } + + public @Nullable NBTTagList getChunkTiles(int x, int z) { + try { + NBTTagCompound chunk = getChunkInternal(x, z); + if (chunk == null) return null; + + NBTTagCompound level = chunk.getCompoundTag("Level"); + + if (level.hasKey("TileEntities")) { + return level.getTagList("TileEntities", 10); + } + return null; + } catch (DataFormatException | IOException e) { + VP.LOG.error("Failed to read chunk tiles at ({}, {})", x, z, e); + return null; + } + } + + private @Nullable NBTTagCompound getChunkInternal(int x, int z) throws DataFormatException, IOException { + int l = offset(x, z); + is.seek(l * 4L); + int location = (is.read() << 16) + (is.read() << 8) + is.read(); + int size = is.read(); + if (size > 0) { + // Chunk non-void, load it + is.seek(location * 4096L); + // Read 4-bytes of data length + int compressedLength = (is.read() << 24) | (is.read() << 16) | (is.read() << 8) | is.read(); + // Read compression mode + int compression = is.read(); + if (compression != 2) return null; + + byte[] compressedDataBuffer = new byte[compressedLength]; + is.readFully(compressedDataBuffer, 0, compressedLength); + try (DataInputStream chunkData = new DataInputStream( + new BufferedInputStream( + new InflaterInputStream( + new ByteArrayInputStream(compressedDataBuffer, 0, compressedLength))))) { + return CompressedStreamTools.read(chunkData); + } + } + return null; + } + + public void close() throws IOException { + is.close(); + } +} diff --git a/src/main/java/com/sinthoras/visualprospecting/database/cachebuilder/WorldAnalysis.java b/src/main/java/com/sinthoras/visualprospecting/database/cachebuilder/WorldAnalysis.java index 793c56e0..39afd012 100644 --- a/src/main/java/com/sinthoras/visualprospecting/database/cachebuilder/WorldAnalysis.java +++ b/src/main/java/com/sinthoras/visualprospecting/database/cachebuilder/WorldAnalysis.java @@ -1,34 +1,111 @@ package com.sinthoras.visualprospecting.database.cachebuilder; -import com.sinthoras.visualprospecting.VP; -import com.sinthoras.visualprospecting.database.ServerCache; -import io.xol.enklume.MinecraftWorld; - import java.io.File; import java.io.IOException; -import java.util.List; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.EnumSet; +import java.util.stream.Stream; import java.util.zip.DataFormatException; +import net.minecraft.util.ChunkCoordinates; +import net.minecraft.world.WorldServer; +import net.minecraftforge.common.DimensionManager; + +import org.apache.commons.io.FileUtils; + +import com.sinthoras.visualprospecting.VP; +import com.sinthoras.visualprospecting.database.ServerCache; +import com.sinthoras.visualprospecting.database.VeinSource; +import com.sinthoras.visualprospecting.database.veintypes.VeinTypeCaching; + +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; +import it.unimi.dsi.fastutil.ints.IntSet; +import it.unimi.dsi.fastutil.ints.IntSets; + public class WorldAnalysis { - private final MinecraftWorld world; + private static final EnumSet RESCAN_PROTECTED = EnumSet + .of(VeinSource.GENERATED, VeinSource.API, VeinSource.UNKNOWN); + + private final File worldFolder; public WorldAnalysis(File worldDirectory) throws IOException { - world = new MinecraftWorld(worldDirectory); + this.worldFolder = worldDirectory; + } + + public void cacheOverworldSpawnVeins(ChunkCoordinates spawn) throws IOException, DataFormatException { + + VP.LOG.info("Starting to parse world save to cache GT vein locations near spawn. This might take some time..."); + ServerCache.instance.resetSpawnChunks(spawn, 0, RESCAN_PROTECTED); + + cacheVeins(IntSets.singleton(0)); } public void cacheVeins() throws IOException, DataFormatException { - VP.info("Starting to parse world save to cache GT vein locations. This might take some time..."); - ServerCache.instance.reset(); - final List dimensionIds = world.getDimensionIds(); + + VP.LOG.info("Starting to parse world save to cache GT vein locations. This might take some time..."); + ServerCache.instance.resetExcept(RESCAN_PROTECTED); + + cacheVeins(getDimensionIds()); + } + + private void cacheVeins(IntSet dimensionIds) { + AnalysisProgressTracker.setNumberOfDimensions(dimensionIds.size()); - for(int dimensionId : dimensionIds) { + for (int dimensionId : dimensionIds) { final DimensionAnalysis dimension = new DimensionAnalysis(dimensionId); - dimension.processMinecraftWorld(world); + + // Skip dimensions in with no known GT OreMixes + if (!VeinTypeCaching.hasVeinsInDimension(dimension.dimensionName)) { + VP.LOG.info( + "Skipping dimensionId={} name=\"{}\" - no ore veins generate in this dimension.", + dimensionId, + dimension.dimensionName); + AnalysisProgressTracker.dimensionProcessed(); + continue; + } + + final Collection regionFiles = getRegionFilesForDim(dimensionId); + dimension.processMinecraftWorld(regionFiles); AnalysisProgressTracker.dimensionProcessed(); } + AnalysisProgressTracker.processingFinished(); - VP.info("Saving ore vein cache..."); + VP.LOG.info("Saving ore vein cache..."); ServerCache.instance.saveVeinCache(); } + + public IntSet getDimensionIds() throws IOException { + IntSet dimensionIds = new IntOpenHashSet(); + dimensionIds.addAll(Arrays.asList(DimensionManager.getIDs())); + try (Stream files = Files.list(worldFolder.toPath())) { + files.filter(Files::isDirectory).map(path -> path.getFileName().toString()) + .filter(path -> path.startsWith("DIM")).map(path -> path.substring(3)).forEach(dim -> { + try { + dimensionIds.add(Integer.parseInt(dim)); + } catch (NumberFormatException ignored) {} + }); + } + return dimensionIds; + } + + public Collection getRegionFilesForDim(int dimensionId) { + WorldServer world = DimensionManager.getWorld(dimensionId); + File dimRegionDir; + if (world != null) { + dimRegionDir = new File(world.getChunkSaveLocation(), "region"); + } else { + final File dimFolder = dimensionId == 0 ? worldFolder : new File(worldFolder, "DIM" + dimensionId); + dimRegionDir = new File(dimFolder, "region"); + } + + if (dimRegionDir.exists()) { + return FileUtils.listFiles(dimRegionDir, new String[] { "mca" }, false); + } + return new ArrayList<>(); + } } diff --git a/src/main/java/com/sinthoras/visualprospecting/database/veintypes/BartworksOreMaterialProvider.java b/src/main/java/com/sinthoras/visualprospecting/database/veintypes/BartworksOreMaterialProvider.java deleted file mode 100644 index da8512aa..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/database/veintypes/BartworksOreMaterialProvider.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.sinthoras.visualprospecting.database.veintypes; - -import com.github.bartimaeusnek.bartworks.system.material.Werkstoff; -import cpw.mods.fml.relauncher.Side; -import cpw.mods.fml.relauncher.SideOnly; -import gregtech.api.enums.OrePrefixes; -import net.minecraft.util.IIcon; - -import static com.github.bartimaeusnek.bartworks.util.BW_ColorUtil.getColorFromRGBArray; - -public class BartworksOreMaterialProvider implements IOreMaterialProvider { - - private final Werkstoff material; - - public BartworksOreMaterialProvider(Werkstoff material) { - this.material = material; - } - - @Override - @SideOnly(Side.CLIENT) - public IIcon getIcon() { - return material.getTexSet().mTextures[OrePrefixes.ore.mTextureIndex].getIcon(); - } - - @Override - public int getColor() { - return getColorFromRGBArray(material.getRGBA()); - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/database/veintypes/GregTechOreMaterialProvider.java b/src/main/java/com/sinthoras/visualprospecting/database/veintypes/GregTechOreMaterialProvider.java deleted file mode 100644 index f9a754d2..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/database/veintypes/GregTechOreMaterialProvider.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.sinthoras.visualprospecting.database.veintypes; - -import cpw.mods.fml.relauncher.Side; -import cpw.mods.fml.relauncher.SideOnly; -import gregtech.api.enums.Materials; -import gregtech.api.enums.OrePrefixes; -import net.minecraft.util.IIcon; - -public class GregTechOreMaterialProvider implements IOreMaterialProvider { - - private final Materials material; - - public GregTechOreMaterialProvider(Materials material) { - this.material = material; - } - - @Override - @SideOnly(Side.CLIENT) - public IIcon getIcon() { - return material.mIconSet.mTextures[OrePrefixes.ore.mTextureIndex].getIcon(); - } - - @Override - public int getColor() { - return (material.mRGBa[0] << 16) | (material.mRGBa[1]) << 8 | material.mRGBa[2]; - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/database/veintypes/IOreMaterialProvider.java b/src/main/java/com/sinthoras/visualprospecting/database/veintypes/IOreMaterialProvider.java deleted file mode 100644 index c4ee26b8..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/database/veintypes/IOreMaterialProvider.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.sinthoras.visualprospecting.database.veintypes; - -import cpw.mods.fml.relauncher.Side; -import cpw.mods.fml.relauncher.SideOnly; -import net.minecraft.util.IIcon; - - -public interface IOreMaterialProvider { - - @SideOnly(Side.CLIENT) - IIcon getIcon(); - - int getColor(); -} diff --git a/src/main/java/com/sinthoras/visualprospecting/database/veintypes/VeinType.java b/src/main/java/com/sinthoras/visualprospecting/database/veintypes/VeinType.java index c92ec7b0..51e231b6 100644 --- a/src/main/java/com/sinthoras/visualprospecting/database/veintypes/VeinType.java +++ b/src/main/java/com/sinthoras/visualprospecting/database/veintypes/VeinType.java @@ -1,59 +1,118 @@ package com.sinthoras.visualprospecting.database.veintypes; -import com.sinthoras.visualprospecting.Tags; -import gregtech.api.GregTech_API; -import net.minecraft.util.EnumChatFormatting; - -import java.util.HashSet; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.List; -import java.util.Objects; -import java.util.Set; +import java.util.regex.Pattern; import java.util.stream.Collectors; +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumChatFormatting; + +import com.google.common.collect.ImmutableList; +import com.sinthoras.visualprospecting.Tags; + +import codechicken.nei.api.ItemFilter; +import galacticgreg.api.enums.DimensionDef.DimNames; +import gregtech.api.enums.OrePrefixes; +import gregtech.api.interfaces.IOreMaterial; +import gregtech.common.OreMixBuilder; +import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; + +@ParametersAreNonnullByDefault public class VeinType { + // A vein spans 9 block layers in GT WorldgenGTOreLayer. public static final int veinHeight = 9; public final String name; - public short veinId; - public final IOreMaterialProvider oreMaterialProvider; public final int blockSize; - public final short primaryOreMeta; - public final short secondaryOreMeta; - public final short inBetweenOreMeta; - public final short sporadicOreMeta; + public final IOreMaterial representativeOre; + public final IOreMaterial primaryOre; + public final IOreMaterial secondaryOre; + public final IOreMaterial inBetweenOre; + public final IOreMaterial sporadicOre; public final int minBlockY; public final int maxBlockY; - public final Set oresAsSet; + private final ReferenceOpenHashSet oresAsSet = new ReferenceOpenHashSet<>(); + private final List> oresByLayer; + private final List allowedDims = new ArrayList<>(); private boolean isHighlighted = true; + private final String localizedName; + private static final OrePrefixes[] SEARCH_PREFIXES = { OrePrefixes.dust, OrePrefixes.gem, OrePrefixes.ingot, + OrePrefixes.crushed, OrePrefixes.ore }; + @Nullable + private List oreSearchStacks; // Available after VisualProspecting post GT initialization - public static final VeinType NO_VEIN = new VeinType(Tags.ORE_MIX_NONE_NAME, null, 0, (short)-1, (short)-1, (short)-1, (short)-1, 0, 0); + public static final VeinType NO_VEIN = new VeinType(); + + private VeinType() { + this.name = Tags.ORE_MIX_NONE_NAME; + this.blockSize = 0; + this.minBlockY = 0; + this.maxBlockY = 255; + this.representativeOre = null; + this.primaryOre = null; + this.secondaryOre = null; + this.inBetweenOre = null; + this.sporadicOre = null; + this.localizedName = this.name; + this.oresByLayer = buildOresByLayer(); + } + + public VeinType(OreMixBuilder oreMix) { + name = oreMix.oreMixName; + localizedName = oreMix.getLocalizedName(); + blockSize = oreMix.size; + representativeOre = oreMix.representative; + oresAsSet.add(primaryOre = oreMix.primary); + oresAsSet.add(secondaryOre = oreMix.secondary); + oresAsSet.add(inBetweenOre = oreMix.between); + oresAsSet.add(sporadicOre = oreMix.sporadic); + oresByLayer = buildOresByLayer(); + minBlockY = Math.max(0, oreMix.minY - 6); + maxBlockY = Math.min(255, oreMix.maxY - 6); + allowedDims.addAll(oreMix.dimsEnabled); + + // Fuse entries for "The End" and "EndAsteroid" since they are both about same dimension. + // World gen swap which one is used around 16 chunks from central point. + final boolean inEnd = allowedDims.contains(DimNames.THE_END); + final boolean inEndAsteroid = allowedDims.contains(DimNames.ENDASTEROID); + if (inEnd != inEndAsteroid) { + allowedDims.add(inEnd ? DimNames.ENDASTEROID : DimNames.THE_END); + } + } + + public boolean containsAllFoundOres(Collection foundOres, String dimName, IOreMaterial specific, + int minY) { + return minY >= minBlockY && (primaryOre == specific || secondaryOre == specific) + && (dimName.isEmpty() || allowedDims.contains(dimName)) + && foundOres.containsAll(oresAsSet); + } - public VeinType(String name, IOreMaterialProvider oreMaterialProvider, int blockSize, short primaryOreMeta, short secondaryOreMeta, short inBetweenOreMeta, short sporadicOreMeta, int minBlockY, int maxBlockY) - { - this.name = name; - this.oreMaterialProvider = oreMaterialProvider; - this.blockSize = blockSize; - this.primaryOreMeta = primaryOreMeta; - this.secondaryOreMeta = secondaryOreMeta; - this.inBetweenOreMeta = inBetweenOreMeta; - this.sporadicOreMeta = sporadicOreMeta; - this.minBlockY = minBlockY; - this.maxBlockY = maxBlockY; - oresAsSet = new HashSet<>(); - oresAsSet.add(primaryOreMeta); - oresAsSet.add(secondaryOreMeta); - oresAsSet.add(inBetweenOreMeta); - oresAsSet.add(sporadicOreMeta); + public boolean matchesWithSpecificPrimaryOrSecondary(Collection foundOres, String dimName, + IOreMaterial specific) { + return (primaryOre == specific || secondaryOre == specific) + && (dimName.isEmpty() || allowedDims.contains(dimName)) + && foundOres.containsAll(oresAsSet); } - public boolean matches(Set foundOres) { - return foundOres.containsAll(oresAsSet); + public boolean matchesWithOneMissingOre(Collection foundOres, String dimName, + IOreMaterial dominantOre) { + // Only match veins with four distinct ores when exactly three were found. + if (oresAsSet.size() != 4 || foundOres.size() != 3) return false; + if (primaryOre != dominantOre && secondaryOre != dominantOre) return false; + if (!dimName.isEmpty() && !allowedDims.contains(dimName)) return false; + return oresAsSet.containsAll(foundOres); } - public boolean matchesWithSpecificPrimaryOrSecondary(Set foundOres, short specificMeta) { - return (primaryOreMeta == specificMeta || secondaryOreMeta == specificMeta) && foundOres.containsAll(oresAsSet); + public List getAllowedDimensions() { + return allowedDims; } public boolean canOverlapIntoNeighborOreChunk() { @@ -65,60 +124,96 @@ public boolean canOverlapIntoNeighborOreChunkAtCoordinateAxis() { return blockSize > 16; } - public boolean containsOre(short oreMetaData) { - return primaryOreMeta == oreMetaData - || secondaryOreMeta == oreMetaData - || inBetweenOreMeta == oreMetaData - || sporadicOreMeta == oreMetaData; - } - - public List getOreMaterialNames() { - return oresAsSet.stream() - .map(metaData -> GregTech_API.sGeneratedMaterials[metaData]) - .filter(Objects::nonNull) - .map(material -> EnumChatFormatting.GRAY + material.mLocalizedName) - .collect(Collectors.toList()); - } - - public Set getOresAtLayer(int layerBlockY) { - final Set result = new HashSet<>(); - switch(layerBlockY) { - case 0: - case 1: - case 2: - result.add(secondaryOreMeta); - result.add(sporadicOreMeta); - return result; - case 3: - result.add(secondaryOreMeta); - result.add(inBetweenOreMeta); - result.add(sporadicOreMeta); - return result; - case 4: - result.add(inBetweenOreMeta); - result.add(sporadicOreMeta); - return result; - case 5: - case 6: - result.add(primaryOreMeta); - result.add(inBetweenOreMeta); - result.add(sporadicOreMeta); - return result; - case 7: - case 8: - result.add(primaryOreMeta); - result.add(sporadicOreMeta); - return result; - default: - return result; + public boolean containsOre(IOreMaterial ore) { + return primaryOre == ore || secondaryOre == ore || inBetweenOre == ore || sporadicOre == ore; + } + + public ImmutableList getOreMaterialNames() { + return ImmutableList + .copyOf(oresAsSet.stream().map(IOreMaterial::getLocalizedName).collect(Collectors.toList())); + } + + public String getVeinName() { + return localizedName; + } + + public boolean matchesPattern(Pattern pattern) { + final List searchableStrings = new ArrayList<>(getOreMaterialNames()); + searchableStrings.add(getVeinName()); + return searchableStrings.stream().map(EnumChatFormatting::getTextWithoutFormattingCodes) + .map(String::toLowerCase).anyMatch(searchableString -> pattern.matcher(searchableString).find()); + } + + public boolean matchesItemFilter(ItemFilter filter) { + for (ItemStack stack : getOreSearchStacks()) { + if (filter.matches(stack)) return true; + } + return false; + } + + private List getOreSearchStacks() { + if (oreSearchStacks == null) { + oreSearchStacks = new ArrayList<>(); + for (IOreMaterial ore : oresAsSet) { + if (ore == null) continue; + for (OrePrefixes prefix : SEARCH_PREFIXES) { + final ItemStack stack = ore.getPart(prefix, 1); + if (stack != null) { + oreSearchStacks.add(stack); + break; + } + } + } + } + return oreSearchStacks; + } + + public List getOresAtLayer(int layerBlockY) { + if (layerBlockY < 0 || layerBlockY >= veinHeight) { + return Collections.emptyList(); + } + return oresByLayer.get(layerBlockY); + } + + // Layer -> ores mirrors GT WorldgenGTOreLayer placement + private List> buildOresByLayer() { + final List> layers = new ArrayList<>(veinHeight); + for (int layer = 0; layer < veinHeight; layer++) { + final List ores = new ArrayList<>(3); + switch (layer) { + case 0, 1, 2 -> { + ores.add(secondaryOre); + ores.add(sporadicOre); + } + case 3 -> { + ores.add(secondaryOre); + ores.add(inBetweenOre); + ores.add(sporadicOre); + } + case 4 -> { + ores.add(inBetweenOre); + ores.add(sporadicOre); + } + case 5, 6 -> { + ores.add(primaryOre); + ores.add(inBetweenOre); + ores.add(sporadicOre); + } + case 7, 8 -> { + ores.add(primaryOre); + ores.add(sporadicOre); + } + } + layers.add(ores); } + return layers; } public boolean isHighlighted() { return isHighlighted; } - public void setNEISearchHeighlight(boolean isHighlighted) { + public void setNEISearchHighlight(boolean isHighlighted) { this.isHighlighted = isHighlighted; } } diff --git a/src/main/java/com/sinthoras/visualprospecting/database/veintypes/VeinTypeCaching.java b/src/main/java/com/sinthoras/visualprospecting/database/veintypes/VeinTypeCaching.java index 3005d8d6..219e181f 100644 --- a/src/main/java/com/sinthoras/visualprospecting/database/veintypes/VeinTypeCaching.java +++ b/src/main/java/com/sinthoras/visualprospecting/database/veintypes/VeinTypeCaching.java @@ -1,173 +1,95 @@ package com.sinthoras.visualprospecting.database.veintypes; -import codechicken.nei.NEIClientConfig; -import codechicken.nei.SearchField; -import com.github.bartimaeusnek.bartworks.system.material.Werkstoff; -import com.github.bartimaeusnek.bartworks.system.oregen.BW_OreLayer; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.regex.Pattern; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + import com.sinthoras.visualprospecting.Tags; -import com.sinthoras.visualprospecting.Utils; -import gregtech.api.GregTech_API; -import gregtech.api.enums.Materials; -import gregtech.common.GT_Worldgen_GT_Ore_Layer; -import net.minecraft.client.resources.I18n; - -import java.io.File; -import java.util.*; -import java.util.stream.Collectors; - -import static com.sinthoras.visualprospecting.Utils.*; - -public class VeinTypeCaching implements Runnable { - - private static BiMap veinTypeLookupTableForIds = HashBiMap.create(); - private static Map veinTypeLookupTableForNames = new HashMap<>(); - private static Map veinTypeStorageInfo; - public static List veinTypes; - public static Set largeVeinOres; - private static int longesOreName = 0; - - // BartWorks initializes veins in FML preInit - // GalacticGreg initializes veins in FML postInit, but only copies all base game veins to make them available on all planets - // GregTech initializes veins in a thread in FML postInit - // Therefore, this method must be called after GregTech postInit - public void run() { - veinTypes = new ArrayList<>(); - largeVeinOres = new HashSet<>(); - veinTypes.add(VeinType.NO_VEIN); - - for(GT_Worldgen_GT_Ore_Layer vein : GT_Worldgen_GT_Ore_Layer.sList) { - if(vein.mWorldGenName.equals(Tags.ORE_MIX_NONE_NAME)) { - break; - } - final Materials material = getGregTechMaterial(vein.mPrimaryMeta); - - veinTypes.add(new VeinType( - vein.mWorldGenName, - new GregTechOreMaterialProvider(material), - vein.mSize, - vein.mPrimaryMeta, - vein.mSecondaryMeta, - vein.mBetweenMeta, - vein.mSporadicMeta, - Math.max(0, vein.mMinY - 6), // GregTech ore veins start at layer -1 and the blockY RNG adds another -5 offset - Math.min(255, vein.mMaxY - 6))); - } - if(isBartworksInstalled()) { - for(BW_OreLayer vein : BW_OreLayer.sList) { - final IOreMaterialProvider oreMaterialProvider = (vein.bwOres & 0b1000) == 0 ? - new GregTechOreMaterialProvider(getGregTechMaterial((short) vein.mPrimaryMeta)) - : new BartworksOreMaterialProvider(Werkstoff.werkstoffHashMap.get((short) vein.mPrimaryMeta)); - - veinTypes.add(new VeinType( - vein.mWorldGenName, - oreMaterialProvider, - vein.mSize, - (short) vein.mPrimaryMeta, - (short) vein.mSecondaryMeta, - (short) vein.mBetweenMeta, - (short) vein.mSporadicMeta, - Math.max(0, vein.mMinY), - Math.min(255, vein.mMaxY))); - } - } +import codechicken.nei.api.ItemFilter; +import gregtech.api.enums.OreMixes; +import gregtech.api.interfaces.IOreMaterial; +import it.unimi.dsi.fastutil.objects.Object2ObjectMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import it.unimi.dsi.fastutil.objects.ObjectSet; +import it.unimi.dsi.fastutil.objects.Reference2ObjectMap; +import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap; - // Assign veinTypeIds for efficient storage - loadVeinTypeStorageInfo(); +public class VeinTypeCaching { - final Optional maxVeinTypeIdOptional = veinTypeStorageInfo.values().stream().max(Short::compare); - short maxVeinTypeId = maxVeinTypeIdOptional.isPresent() ? maxVeinTypeIdOptional.get() : 0; + private static final Object2ObjectMap veinTypes = new Object2ObjectOpenHashMap<>(); - for(VeinType veinType : veinTypes) { - if(veinTypeStorageInfo.containsKey(veinType.name)) { - veinType.veinId = veinTypeStorageInfo.get(veinType.name); - } - else { - maxVeinTypeId++; - veinType.veinId = maxVeinTypeId; - veinTypeStorageInfo.put(veinType.name, veinType.veinId); - } - // Build LUT (id <-> object) - veinTypeLookupTableForIds.put(veinType.veinId, veinType); - - // Build LUT (name -> object) - veinTypeLookupTableForNames.put(veinType.name, veinType); - - // Build large vein LUT - if(veinType.canOverlapIntoNeighborOreChunk()) { - largeVeinOres.add(veinType.primaryOreMeta); - largeVeinOres.add(veinType.secondaryOreMeta); - largeVeinOres.add(veinType.inBetweenOreMeta); - largeVeinOres.add(veinType.sporadicOreMeta); - } + // Dimensions without registered OreMixes are not expected to match anything + private static final ObjectSet dimensionsWithVeins = new ObjectOpenHashSet<>(); + + // VeinTypes indexed by primary/secondary Ore in OreMix. For faster matching + private static final Reference2ObjectMap> veinTypesByPrimaryOrSecondary = new Reference2ObjectOpenHashMap<>(); + + public static void init() { + veinTypes.clear(); + veinTypes.put(Tags.ORE_MIX_NONE_NAME, VeinType.NO_VEIN); + + for (OreMixes mix : OreMixes.values()) { + veinTypes.put(mix.oreMixBuilder.oreMixName, new VeinType(mix.oreMixBuilder)); } - saveVeinTypeStorageInfo(); - for(VeinType veinType : veinTypes) { - if(veinType.name.length() > longesOreName) { - longesOreName = veinType.name.length(); + dimensionsWithVeins.clear(); + veinTypesByPrimaryOrSecondary.clear(); + for (VeinType veinType : veinTypes.values()) { + if (veinType == VeinType.NO_VEIN) continue; + dimensionsWithVeins.addAll(veinType.getAllowedDimensions()); + indexByOre(veinType.primaryOre, veinType); + if (veinType.secondaryOre != veinType.primaryOre) { + indexByOre(veinType.secondaryOre, veinType); } } } - private Materials getGregTechMaterial(short metaId) { - final Materials material = GregTech_API.sGeneratedMaterials[metaId]; - if(material == null) { - // Some materials are not registered in dev when their usage mod is not available. - return Materials.getAll().stream().filter(m -> m.mMetaItemSubID == metaId).findAny().get(); + private static void indexByOre(IOreMaterial ore, VeinType veinType) { + if (ore == null) return; + List matchingVeinTypes = veinTypesByPrimaryOrSecondary.get(ore); + if (matchingVeinTypes == null) { + matchingVeinTypes = new ArrayList<>(); + veinTypesByPrimaryOrSecondary.put(ore, matchingVeinTypes); } - return material; - } - - public static int getLongesOreNameLength() { - return longesOreName; + matchingVeinTypes.add(veinType); } - public static short getVeinTypeId(VeinType veinType) { - return veinTypeLookupTableForIds.inverse().get(veinType); + public static boolean hasVeinsInDimension(String dimensionName) { + return dimensionsWithVeins.contains(dimensionName); } - public static VeinType getVeinType(short veinTypeId) { - return veinTypeLookupTableForIds.getOrDefault(veinTypeId, VeinType.NO_VEIN); + public static @NotNull VeinType getVeinType(String veinTypeName) { + return veinTypes.getOrDefault(veinTypeName, VeinType.NO_VEIN); } - public static VeinType getVeinType(String veinTypeName) { - return veinTypeLookupTableForNames.getOrDefault(veinTypeName, VeinType.NO_VEIN); + public static Collection getVeinTypes() { + return veinTypes.values(); } - private static File getVeinTypeStorageInfoFile() { - final File directory = Utils.getSubDirectory(Tags.VISUALPROSPECTING_DIR); - directory.mkdirs(); - return new File(directory, "veintypesLUT"); + public static List getVeinTypesForOre(IOreMaterial dominantOre) { + final List matchingVeinTypes = veinTypesByPrimaryOrSecondary.get(dominantOre); + return matchingVeinTypes == null ? Collections.emptyList() : matchingVeinTypes; } - private static void loadVeinTypeStorageInfo() { - veinTypeStorageInfo = Utils.readFileToMap(getVeinTypeStorageInfoFile()); - } + public static void recalculateSearch(@Nullable Pattern filterPattern, @Nullable ItemFilter itemFilter) { + for (VeinType veinType : veinTypes.values()) { + if (veinType == VeinType.NO_VEIN) continue; + if (filterPattern == null && itemFilter == null) { + veinType.setNEISearchHighlight(true); + continue; + } - private static void saveVeinTypeStorageInfo() { - Utils.writeMapToFile(getVeinTypeStorageInfoFile(), veinTypeStorageInfo); - } + final boolean match = (filterPattern != null && veinType.matchesPattern(filterPattern)) + || (itemFilter != null && veinType.matchesItemFilter(itemFilter)); - public static void recalculateNEISearch() { - if(isNEIInstalled()) { - final boolean isSearchActive = SearchField.searchInventories(); - final String searchString = NEIClientConfig.getSearchExpression().toLowerCase(); - - for (VeinType veinType : veinTypes) { - if (veinType != VeinType.NO_VEIN) { - if (isSearchActive) { - List searchableStrings = veinType.getOreMaterialNames(); - searchableStrings.add(I18n.format(veinType.name)); - searchableStrings = searchableStrings.stream().map(String::toLowerCase).filter(searchableString -> searchableString.contains(searchString)).collect(Collectors.toList()); - veinType.setNEISearchHeighlight(searchableStrings.isEmpty() == false); - } else { - veinType.setNEISearchHeighlight(true); - } - } - } + veinType.setNEISearchHighlight(match); } } } diff --git a/src/main/java/com/sinthoras/visualprospecting/hooks/HooksClient.java b/src/main/java/com/sinthoras/visualprospecting/hooks/HooksClient.java index d3702b6a..d024f6e0 100644 --- a/src/main/java/com/sinthoras/visualprospecting/hooks/HooksClient.java +++ b/src/main/java/com/sinthoras/visualprospecting/hooks/HooksClient.java @@ -1,11 +1,20 @@ package com.sinthoras.visualprospecting.hooks; +import net.minecraft.client.Minecraft; +import net.minecraftforge.client.ClientCommandHandler; +import net.minecraftforge.common.MinecraftForge; + +import com.gtnewhorizons.navigator.api.NavigatorApi; +import com.gtnewhorizons.navigator.api.util.Util; import com.sinthoras.visualprospecting.Utils; -import com.sinthoras.visualprospecting.VP; -import com.sinthoras.visualprospecting.database.ResetClientCacheCommand; +import com.sinthoras.visualprospecting.commands.ResetClientCacheCommand; +import com.sinthoras.visualprospecting.database.ClientCache; +import com.sinthoras.visualprospecting.integration.model.layers.OreVeinLayerManager; +import com.sinthoras.visualprospecting.integration.model.layers.UndergroundFluidLayerManager; import com.sinthoras.visualprospecting.integration.voxelmap.VoxelMapEventHandler; +import com.sinthoras.visualprospecting.task.TaskManager; -import cpw.mods.fml.client.registry.ClientRegistry; +import cpw.mods.fml.common.FMLCommonHandler; import cpw.mods.fml.common.event.FMLInitializationEvent; import cpw.mods.fml.common.event.FMLPostInitializationEvent; import cpw.mods.fml.common.event.FMLPreInitializationEvent; @@ -14,61 +23,93 @@ import cpw.mods.fml.common.event.FMLServerStartingEvent; import cpw.mods.fml.common.event.FMLServerStoppedEvent; import cpw.mods.fml.common.event.FMLServerStoppingEvent; -import net.minecraft.client.settings.KeyBinding; -import net.minecraftforge.client.ClientCommandHandler; -import net.minecraftforge.common.MinecraftForge; - -import org.lwjgl.input.Keyboard; +import cpw.mods.fml.common.eventhandler.SubscribeEvent; public class HooksClient extends HooksShared { - @Override - // load "Do your mod setup. Build whatever data structures you care about. Register recipes." - public void fmlLifeCycleEvent(FMLPreInitializationEvent event) { - super.fmlLifeCycleEvent(event); - - VP.keyAction = new KeyBinding("visualprospecting.key.action.name", Keyboard.KEY_DELETE, "visualprospecting.key.action.category"); - ClientRegistry.registerKeyBinding(VP.keyAction); - } - - @Override - // load "Do your mod setup. Build whatever data structures you care about. Register recipes." - public void fmlLifeCycleEvent(FMLInitializationEvent event) { - super.fmlLifeCycleEvent(event); - } - - @Override - // postInit "Handle interaction with other mods, complete your setup based on this." - public void fmlLifeCycleEvent(FMLPostInitializationEvent event) { - super.fmlLifeCycleEvent(event); - ClientCommandHandler.instance.registerCommand(new ResetClientCacheCommand()); - if(Utils.isVoxelMapInstalled()) { - MinecraftForge.EVENT_BUS.register(new VoxelMapEventHandler()); - } - } - - @Override - public void fmlLifeCycleEvent(FMLServerAboutToStartEvent event) { - super.fmlLifeCycleEvent(event); - } - - @Override - public void fmlLifeCycleEvent(FMLServerStartingEvent event) { - super.fmlLifeCycleEvent(event); - } - - @Override - public void fmlLifeCycleEvent(FMLServerStartedEvent event) { - super.fmlLifeCycleEvent(event); - } - - @Override - public void fmlLifeCycleEvent(FMLServerStoppingEvent event) { - super.fmlLifeCycleEvent(event); - } - - @Override - public void fmlLifeCycleEvent(FMLServerStoppedEvent event) { - super.fmlLifeCycleEvent(event); - } + @Override + // load "Do your mod setup. Build whatever data structures you care about. Register recipes." + public void fmlLifeCycleEvent(FMLPreInitializationEvent event) { + super.fmlLifeCycleEvent(event); + if (Utils.isNavigatorInstalled()) { + registerMapLayers(); + } + + initializeTaskManager(); + } + + @Override + // load "Do your mod setup. Build whatever data structures you care about. Register recipes." + public void fmlLifeCycleEvent(FMLInitializationEvent event) { + super.fmlLifeCycleEvent(event); + MinecraftForge.EVENT_BUS.register(ClientCache.instance); + FMLCommonHandler.instance().bus().register(new HooksKey()); + } + + @Override + // postInit "Handle interaction with other mods, complete your setup based on this." + public void fmlLifeCycleEvent(FMLPostInitializationEvent event) { + super.fmlLifeCycleEvent(event); + ClientCommandHandler.instance.registerCommand(new ResetClientCacheCommand()); + } + + @Override + public void fmlLifeCycleEvent(FMLServerAboutToStartEvent event) { + super.fmlLifeCycleEvent(event); + } + + @Override + public void fmlLifeCycleEvent(FMLServerStartingEvent event) { + super.fmlLifeCycleEvent(event); + } + + @Override + public void fmlLifeCycleEvent(FMLServerStartedEvent event) { + super.fmlLifeCycleEvent(event); + } + + @Override + public void fmlLifeCycleEvent(FMLServerStoppingEvent event) { + super.fmlLifeCycleEvent(event); + } + + @Override + public void fmlLifeCycleEvent(FMLServerStoppedEvent event) { + super.fmlLifeCycleEvent(event); + } + + @Override + protected void initializeTaskManager() { + TaskManager.SERVER_INSTANCE = new TaskManager(); + TaskManager.CLIENT_INSTANCE = new TaskManager(); + } + + public void registerMapLayers() { + + NavigatorApi.registerLayerManager(OreVeinLayerManager.instance); + NavigatorApi.registerLayerManager(UndergroundFluidLayerManager.instance); + MinecraftForge.EVENT_BUS.register(this); + + if (Util.isVoxelMapInstalled()) { + MinecraftForge.EVENT_BUS.register(new VoxelMapEventHandler()); + } + } + + @SubscribeEvent + public void onVeinProspected(ProspectingNotificationEvent.OreVein event) { + Minecraft.getMinecraft().func_152344_a( + () -> OreVeinLayerManager.instance.invalidateLocation( + event.getPosition().dimensionId, + event.getPosition().chunkX, + event.getPosition().chunkZ)); + } + + @SubscribeEvent + public void onFluidProspected(ProspectingNotificationEvent.UndergroundFluid event) { + Minecraft.getMinecraft().func_152344_a( + () -> UndergroundFluidLayerManager.instance.invalidateLocation( + event.getPosition().dimensionId, + event.getPosition().chunkX, + event.getPosition().chunkZ)); + } } diff --git a/src/main/java/com/sinthoras/visualprospecting/hooks/HooksEventBus.java b/src/main/java/com/sinthoras/visualprospecting/hooks/HooksEventBus.java index 77f732b4..23ae39ac 100644 --- a/src/main/java/com/sinthoras/visualprospecting/hooks/HooksEventBus.java +++ b/src/main/java/com/sinthoras/visualprospecting/hooks/HooksEventBus.java @@ -1,22 +1,35 @@ package com.sinthoras.visualprospecting.hooks; -import com.sinthoras.visualprospecting.VP; +import static java.nio.charset.StandardCharsets.UTF_8; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.UUID; + +import net.minecraft.client.Minecraft; +import net.minecraftforge.event.world.WorldEvent; + +import org.apache.commons.io.FileUtils; + +import com.gtnewhorizon.gtnhlib.client.event.WorldDeletionEvent; +import com.sinthoras.visualprospecting.Tags; import com.sinthoras.visualprospecting.Utils; +import com.sinthoras.visualprospecting.VP; import com.sinthoras.visualprospecting.database.ClientCache; import com.sinthoras.visualprospecting.database.ServerCache; -import com.sinthoras.visualprospecting.database.WorldIdHandler; -import com.sinthoras.visualprospecting.network.WorldIdNotification; +import com.sinthoras.visualprospecting.utils.VPSaveCleanupUtils; + import cpw.mods.fml.common.eventhandler.SubscribeEvent; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraftforge.event.entity.EntityJoinWorldEvent; -import net.minecraftforge.event.world.WorldEvent; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; public class HooksEventBus { @SubscribeEvent public void onEvent(WorldEvent.Unload event) { - if(Utils.isLogicalClient()) { + if (Utils.isLogicalClient()) { ClientCache.instance.saveVeinCache(); } } @@ -26,15 +39,40 @@ public void onEvent(WorldEvent.Save event) { ServerCache.instance.saveVeinCache(); } + @SideOnly(Side.CLIENT) @SubscribeEvent - public void onEvent(EntityJoinWorldEvent event) { - if(event.world.isRemote == false) { - if (event.entity instanceof EntityPlayerMP) { - VP.network.sendTo(new WorldIdNotification(WorldIdHandler.getWorldId()), (EntityPlayerMP) event.entity); + public void onWorldDeletionEvent(WorldDeletionEvent event) { + File mcDataDir = Minecraft.getMinecraft().mcDataDir; + Path vpBasePath = mcDataDir.toPath().resolve(Tags.MODID); + String currentUsername = Minecraft.getMinecraft().getSession().getUsername(); + + String worldId = VPSaveCleanupUtils.getVisualProspectingWorldId(mcDataDir, event.worldName); + if (worldId == null) { + VP.LOG.warn("Unable to read VP worldID for world \"{}\", can't cleanup any VP data", event.worldName); + return; + } + Path vpServerPathFull = vpBasePath.resolve("server").resolve(worldId); + if (Files.isDirectory(vpServerPathFull)) { + try { + FileUtils.deleteDirectory(vpServerPathFull.toFile()); + } catch (IOException e) { + VP.LOG.warn("Failed to delete Visual Prospecting server data found at {}", vpServerPathFull); } - else if (event.entity instanceof EntityPlayer) { - ClientCache.instance.loadVeinCache(WorldIdHandler.getWorldId()); + } + + UUID userUUID = Minecraft.getMinecraft().getSession().func_148256_e().getId(); + if (userUUID == null) { + VP.LOG.debug("Unable to read userUUID for current user. Attempting to use fallback instead"); + userUUID = UUID.nameUUIDFromBytes(("OfflinePlayer:" + currentUsername).getBytes(UTF_8)); + } + Path vpClientPathFull = vpBasePath.resolve("client").resolve(userUUID.toString()).resolve(worldId); + if (Files.isDirectory(vpClientPathFull)) { + try { + FileUtils.deleteDirectory(vpClientPathFull.toFile()); + } catch (IOException e) { + VP.LOG.warn("Failed to delete Visual Prospecting client data found at {}", vpClientPathFull); } } + } } diff --git a/src/main/java/com/sinthoras/visualprospecting/hooks/HooksFML.java b/src/main/java/com/sinthoras/visualprospecting/hooks/HooksFML.java index 0ae0afb3..0e6bf55e 100644 --- a/src/main/java/com/sinthoras/visualprospecting/hooks/HooksFML.java +++ b/src/main/java/com/sinthoras/visualprospecting/hooks/HooksFML.java @@ -1,10 +1,21 @@ package com.sinthoras.visualprospecting.hooks; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; + +import com.sinthoras.visualprospecting.VP; import com.sinthoras.visualprospecting.database.ClientCache; +import com.sinthoras.visualprospecting.database.WorldIdHandler; +import com.sinthoras.visualprospecting.network.WorldIdNotification; import com.sinthoras.visualprospecting.task.TaskManager; + +import cpw.mods.fml.common.eventhandler.EventPriority; import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import cpw.mods.fml.common.gameevent.PlayerEvent; import cpw.mods.fml.common.gameevent.TickEvent; import cpw.mods.fml.common.network.FMLNetworkEvent; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; public class HooksFML { @@ -14,7 +25,27 @@ public void onEvent(FMLNetworkEvent.ClientConnectedToServerEvent event) { } @SubscribeEvent - public void onEvent(TickEvent event) { - TaskManager.instance.onTick(); + public void onEvent(FMLNetworkEvent.ClientDisconnectionFromServerEvent event) { + ClientCache.instance.saveVeinCache(); + } + + @SubscribeEvent + public void onServerTick(TickEvent.ServerTickEvent event) { + TaskManager.SERVER_INSTANCE.onTick(); + } + + @SubscribeEvent + @SideOnly(Side.CLIENT) + public void onClientTick(TickEvent.ClientTickEvent event) { + TaskManager.CLIENT_INSTANCE.onTick(); + } + + @SubscribeEvent(priority = EventPriority.LOW) + public void onEvent(PlayerEvent.PlayerLoggedInEvent event) { + if (event.player instanceof EntityPlayerMP playerMP) { + VP.network.sendTo(new WorldIdNotification(WorldIdHandler.getWorldId()), playerMP); + } else if (event.player instanceof EntityPlayer) { + ClientCache.instance.loadVeinCache(WorldIdHandler.getWorldId()); + } } } diff --git a/src/main/java/com/sinthoras/visualprospecting/hooks/HooksKey.java b/src/main/java/com/sinthoras/visualprospecting/hooks/HooksKey.java new file mode 100644 index 00000000..b885f210 --- /dev/null +++ b/src/main/java/com/sinthoras/visualprospecting/hooks/HooksKey.java @@ -0,0 +1,46 @@ +package com.sinthoras.visualprospecting.hooks; + +import net.minecraft.client.settings.KeyBinding; + +import org.lwjgl.input.Keyboard; + +import com.sinthoras.visualprospecting.integration.model.buttons.OreVeinButtonManager; +import com.sinthoras.visualprospecting.integration.model.buttons.UndergroundFluidButtonManager; + +import cpw.mods.fml.client.registry.ClientRegistry; +import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import cpw.mods.fml.common.gameevent.InputEvent; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; + +public class HooksKey { + + private final KeyBinding keyToggleOres = new KeyBinding( + "visualprospecting.key.toggleore.name", + Keyboard.KEY_NONE, + "visualprospecting.key.action.category"); + private final KeyBinding keyToggleFluids = new KeyBinding( + "visualprospecting.key.togglefluid.name", + Keyboard.KEY_NONE, + "visualprospecting.key.action.category"); + + public HooksKey() { + ClientRegistry.registerKeyBinding(keyToggleOres); + ClientRegistry.registerKeyBinding(keyToggleFluids); + } + + @SideOnly(Side.CLIENT) + @SubscribeEvent + public void key(InputEvent.KeyInputEvent event) { + checkAndToggleOverlays(); + } + + private void checkAndToggleOverlays() { + if (keyToggleOres.isPressed()) { + OreVeinButtonManager.instance.toggle(); + } + if (keyToggleFluids.isPressed()) { + UndergroundFluidButtonManager.instance.toggle(); + } + } +} diff --git a/src/main/java/com/sinthoras/visualprospecting/hooks/HooksShared.java b/src/main/java/com/sinthoras/visualprospecting/hooks/HooksShared.java index ef423d6b..3023b403 100644 --- a/src/main/java/com/sinthoras/visualprospecting/hooks/HooksShared.java +++ b/src/main/java/com/sinthoras/visualprospecting/hooks/HooksShared.java @@ -1,8 +1,19 @@ package com.sinthoras.visualprospecting.hooks; -import com.sinthoras.visualprospecting.VP; +import java.io.File; +import java.io.IOException; +import java.util.zip.DataFormatException; + +import net.minecraft.world.WorldServer; +import net.minecraftforge.common.DimensionManager; +import net.minecraftforge.common.MinecraftForge; + +import com.gtnewhorizon.gtnhlib.teams.TeamDataRegistry; import com.sinthoras.visualprospecting.Config; import com.sinthoras.visualprospecting.Tags; +import com.sinthoras.visualprospecting.VP; +import com.sinthoras.visualprospecting.commands.VPAdminCommand; +import com.sinthoras.visualprospecting.commands.VPCommand; import com.sinthoras.visualprospecting.database.ServerCache; import com.sinthoras.visualprospecting.database.WorldIdHandler; import com.sinthoras.visualprospecting.database.cachebuilder.WorldAnalysis; @@ -11,9 +22,16 @@ import com.sinthoras.visualprospecting.network.ProspectingNotification; import com.sinthoras.visualprospecting.network.ProspectingRequest; import com.sinthoras.visualprospecting.network.ProspectionSharing; +import com.sinthoras.visualprospecting.network.RequestTeamUploadMessage; +import com.sinthoras.visualprospecting.network.TeamCatchupNotification; +import com.sinthoras.visualprospecting.network.VeinDepletionMessage; import com.sinthoras.visualprospecting.network.WorldIdNotification; +import com.sinthoras.visualprospecting.task.TaskManager; +import com.sinthoras.visualprospecting.teams.TeamProspectionData; + import cpw.mods.fml.common.FMLCommonHandler; import cpw.mods.fml.common.event.FMLInitializationEvent; +import cpw.mods.fml.common.event.FMLLoadCompleteEvent; import cpw.mods.fml.common.event.FMLPostInitializationEvent; import cpw.mods.fml.common.event.FMLPreInitializationEvent; import cpw.mods.fml.common.event.FMLServerAboutToStartEvent; @@ -24,90 +42,126 @@ import cpw.mods.fml.common.network.NetworkRegistry; import cpw.mods.fml.common.registry.GameRegistry; import cpw.mods.fml.relauncher.Side; -import gregtech.api.GregTech_API; -import gregtech.api.enums.GT_Values; -import gregtech.api.enums.Materials; -import gregtech.api.enums.OrePrefixes; -import gregtech.api.util.GT_OreDictUnificator; -import net.minecraft.init.Items; -import net.minecraft.item.ItemStack; -import net.minecraft.server.MinecraftServer; -import net.minecraftforge.common.MinecraftForge; -import java.io.IOException; -import java.util.zip.DataFormatException; +public class HooksShared { + private static boolean newWorld = false; -public class HooksShared { - - // preInit "Run before anything else. Read your config, create blocks, items, - // etc, and register them with the GameRegistry." - public void fmlLifeCycleEvent(FMLPreInitializationEvent event) { - Config.syncronizeConfiguration(event.getSuggestedConfigurationFile()); - - VP.network = NetworkRegistry.INSTANCE.newSimpleChannel(Tags.MODID); - int networkId = 0; - VP.network.registerMessage(ProspectingRequest.Handler.class, ProspectingRequest.class, networkId++, Side.SERVER); - VP.network.registerMessage(ProspectingNotification.Handler.class, ProspectingNotification.class, networkId++, Side.CLIENT); - VP.network.registerMessage(WorldIdNotification.Handler.class, WorldIdNotification.class, networkId++, Side.CLIENT); - VP.network.registerMessage(ProspectionSharing.ServerHandler.class, ProspectionSharing.class, networkId++, Side.SERVER); - VP.network.registerMessage(ProspectionSharing.ClientHandler.class, ProspectionSharing.class, networkId++, Side.CLIENT); - - ProspectorsLog.instance = new ProspectorsLog(); - GameRegistry.registerItem(ProspectorsLog.instance, ProspectorsLog.instance.getUnlocalizedName()); - } - - // load "Do your mod setup. Build whatever data structures you care about. Register recipes." - public void fmlLifeCycleEvent(FMLInitializationEvent event) { - MinecraftForge.EVENT_BUS.register(new HooksEventBus()); - FMLCommonHandler.instance().bus().register(new HooksFML()); - } - - // postInit "Handle interaction with other mods, complete your setup based on this." - public void fmlLifeCycleEvent(FMLPostInitializationEvent event) { - GregTech_API.sAfterGTPostload.add(new VeinTypeCaching()); - GregTech_API.sAfterGTPostload.add(() -> GT_Values.RA.addAssemblerRecipe( - new ItemStack[] { - GT_OreDictUnificator.get(OrePrefixes.plate, Materials.Wood, 2L), - new ItemStack(Items.writable_book, 1, 0x7FFF), - new ItemStack(Items.gold_nugget, 1, 0x7FFF) - }, - Materials.Glue.getFluid(20L), - new ItemStack(ProspectorsLog.instance, 1, 0), - 128, - 8)); - } - - public void fmlLifeCycleEvent(FMLServerAboutToStartEvent event) { - - } - - // register server commands in this event handler - public void fmlLifeCycleEvent(FMLServerStartingEvent event) { - final MinecraftServer minecraftServer = event.getServer(); - WorldIdHandler.load(minecraftServer.worldServers[0]); - if(ServerCache.instance.loadVeinCache(WorldIdHandler.getWorldId()) == false || Config.recacheVeins) { - try { - WorldAnalysis world = new WorldAnalysis(minecraftServer.getEntityWorld().getSaveHandler().getWorldDirectory()); - world.cacheVeins(); - } - catch (IOException | DataFormatException e) { - VP.info("Could not load world save files to build vein cache!"); - e.printStackTrace(); - } - } - } - - public void fmlLifeCycleEvent(FMLServerStartedEvent event) { - - } - - public void fmlLifeCycleEvent(FMLServerStoppingEvent event) { - ServerCache.instance.saveVeinCache(); - ServerCache.instance.reset(); - } - - public void fmlLifeCycleEvent(FMLServerStoppedEvent event) { - - } + // preInit "Run before anything else. Read your config, create blocks, items, + // etc, and register them with the GameRegistry." + public void fmlLifeCycleEvent(FMLPreInitializationEvent event) { + Config.syncronizeConfiguration(event.getSuggestedConfigurationFile()); + + VP.network = NetworkRegistry.INSTANCE.newSimpleChannel(Tags.MODID); + int networkId = 0; + VP.network + .registerMessage(ProspectingRequest.Handler.class, ProspectingRequest.class, networkId++, Side.SERVER); + VP.network.registerMessage( + ProspectingNotification.Handler.class, + ProspectingNotification.class, + networkId++, + Side.CLIENT); + VP.network.registerMessage( + WorldIdNotification.Handler.class, + WorldIdNotification.class, + networkId++, + Side.CLIENT); + VP.network.registerMessage( + ProspectionSharing.ServerHandler.class, + ProspectionSharing.class, + networkId++, + Side.SERVER); + VP.network.registerMessage( + ProspectionSharing.ClientHandler.class, + ProspectionSharing.class, + networkId++, + Side.CLIENT); + VP.network.registerMessage( + VeinDepletionMessage.ServerHandler.class, + VeinDepletionMessage.class, + networkId++, + Side.SERVER); + VP.network.registerMessage( + VeinDepletionMessage.ClientHandler.class, + VeinDepletionMessage.class, + networkId++, + Side.CLIENT); + VP.network.registerMessage( + TeamCatchupNotification.ClientHandler.class, + TeamCatchupNotification.class, + networkId++, + Side.CLIENT); + VP.network.registerMessage( + RequestTeamUploadMessage.ClientHandler.class, + RequestTeamUploadMessage.class, + networkId++, + Side.CLIENT); + + ProspectorsLog.instance = new ProspectorsLog(); + GameRegistry.registerItem(ProspectorsLog.instance, ProspectorsLog.instance.getUnlocalizedName()); + + // Register with GTNHLib team system + TeamDataRegistry.register(TeamProspectionData.DATA_KEY, TeamProspectionData::new); + + initializeTaskManager(); + } + + // load "Do your mod setup. Build whatever data structures you care about. Register recipes." + public void fmlLifeCycleEvent(FMLInitializationEvent event) { + MinecraftForge.EVENT_BUS.register(new HooksEventBus()); + MinecraftForge.EVENT_BUS.register(ServerCache.instance); + FMLCommonHandler.instance().bus().register(new HooksFML()); + } + + // postInit "Handle interaction with other mods, complete your setup based on this." + public void fmlLifeCycleEvent(FMLPostInitializationEvent event) {} + + public void fmlLifeCycleEvent(FMLLoadCompleteEvent event) { + VeinTypeCaching.init(); + } + + public void fmlLifeCycleEvent(FMLServerAboutToStartEvent event) { + File worldDir = DimensionManager.getCurrentSaveRootDirectory(); + if (worldDir != null) { + File regionFolder = new File(worldDir, "region"); + newWorld = !regionFolder.exists(); + } + + VPCommand.register(); + VPAdminCommand.register(); + } + + // register server commands in this event handler + public void fmlLifeCycleEvent(FMLServerStartingEvent event) { + + // Get the server and load the ID handler + WorldServer world = DimensionManager.getWorld(0); + WorldIdHandler.load(world); + + // Attempt to load the vein cache. If unable or forcing a recache... + boolean loaded = ServerCache.instance.loadVeinCache(WorldIdHandler.getWorldId()); + if (!newWorld && (!loaded || Config.recacheVeins)) { + + // Reanalyze the world and reload it into memory. + try { + WorldAnalysis worldAnalysis = new WorldAnalysis(world.getSaveHandler().getWorldDirectory()); + worldAnalysis.cacheVeins(); + } catch (IOException | DataFormatException e) { + VP.LOG.error("Could not load world save files to build vein cache!", e); + } + } + } + + public void fmlLifeCycleEvent(FMLServerStartedEvent event) {} + + public void fmlLifeCycleEvent(FMLServerStoppingEvent event) { + ServerCache.instance.saveVeinCache(); + ServerCache.instance.reset(); + } + + public void fmlLifeCycleEvent(FMLServerStoppedEvent event) {} + + protected void initializeTaskManager() { + TaskManager.SERVER_INSTANCE = new TaskManager(); + } } diff --git a/src/main/java/com/sinthoras/visualprospecting/hooks/ProspectingNotificationEvent.java b/src/main/java/com/sinthoras/visualprospecting/hooks/ProspectingNotificationEvent.java index 448f05df..ec72cc40 100644 --- a/src/main/java/com/sinthoras/visualprospecting/hooks/ProspectingNotificationEvent.java +++ b/src/main/java/com/sinthoras/visualprospecting/hooks/ProspectingNotificationEvent.java @@ -6,38 +6,35 @@ import cpw.mods.fml.common.eventhandler.Event; public class ProspectingNotificationEvent extends Event { - - @Override - public boolean isCancelable() { - return false; - } - - public static class OreVein extends ProspectingNotificationEvent { - - private final OreVeinPosition position; - - public OreVein(OreVeinPosition position) { - this.position = position; - } - - public OreVeinPosition getPosition() { - return this.position; - } - - } - - public static class UndergroundFluid extends ProspectingNotificationEvent { - - private final UndergroundFluidPosition position; - - public UndergroundFluid(UndergroundFluidPosition position) { - this.position = position; - } - - public UndergroundFluidPosition getPosition() { - return this.position; - } - - } + @Override + public boolean isCancelable() { + return false; + } + + public static class OreVein extends ProspectingNotificationEvent { + + private final OreVeinPosition position; + + public OreVein(OreVeinPosition position) { + this.position = position; + } + + public OreVeinPosition getPosition() { + return this.position; + } + } + + public static class UndergroundFluid extends ProspectingNotificationEvent { + + private final UndergroundFluidPosition position; + + public UndergroundFluid(UndergroundFluidPosition position) { + this.position = position; + } + + public UndergroundFluidPosition getPosition() { + return this.position; + } + } } diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/DrawUtils.java b/src/main/java/com/sinthoras/visualprospecting/integration/DrawUtils.java deleted file mode 100644 index be7b7ffa..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/DrawUtils.java +++ /dev/null @@ -1,153 +0,0 @@ -package com.sinthoras.visualprospecting.integration; - -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiScreen; -import net.minecraft.client.renderer.OpenGlHelper; -import net.minecraft.client.renderer.Tessellator; -import net.minecraft.client.renderer.texture.TextureMap; -import net.minecraft.util.IIcon; -import net.minecraft.util.ResourceLocation; -import org.lwjgl.opengl.GL11; -import thaumcraft.api.aspects.Aspect; -import thaumcraft.client.lib.UtilsFX; - -import java.util.List; - -public class DrawUtils { - - public static void drawGradientRect(double minPixelX, double minPixelY, double maxPixelX, double maxPixelY, double z, int colorA, int colorB) { - GL11.glDisable(GL11.GL_TEXTURE_2D); - GL11.glEnable(GL11.GL_BLEND); - GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); - GL11.glDisable(GL11.GL_ALPHA_TEST); - GL11.glShadeModel(GL11.GL_SMOOTH); - Tessellator tessellator = Tessellator.instance; - tessellator.startDrawingQuads(); - - float alpha = (colorA >> 24 & 255) / 255.0f; - float red = (colorA >> 16 & 255) / 255.0f; - float green = (colorA >> 8 & 255) / 255.0f; - float blue = (colorA & 255) / 255.0f; - tessellator.setColorRGBA_F(red, green, blue, alpha); - tessellator.addVertex(maxPixelX, minPixelY, z); - tessellator.addVertex(minPixelX, minPixelY, z); - - alpha = (colorB >> 24 & 255) / 255.0f; - red = (colorB >> 16 & 255) / 255.0f; - green = (colorB >> 8 & 255) / 255.0f; - blue = (colorB & 255) / 255.0f; - tessellator.setColorRGBA_F(red, green, blue, alpha); - tessellator.addVertex(minPixelX, maxPixelY, z); - tessellator.addVertex(maxPixelX, maxPixelY, z); - - tessellator.draw(); - GL11.glShadeModel(GL11.GL_FLAT); - GL11.glDisable(GL11.GL_BLEND); - GL11.glEnable(GL11.GL_ALPHA_TEST); - GL11.glEnable(GL11.GL_TEXTURE_2D); - } - - public static void drawGradientRect(double minPixelX, double minPixelY, double maxPixelX, double maxPixelY, int colorA, int colorB) { - drawGradientRect(minPixelX, minPixelY, maxPixelX, maxPixelY, 300, colorA, colorB); - } - - public static void drawQuad(ResourceLocation texture, double x, double y, double width, double height, int color, float alpha) { - - GL11.glEnable(GL11.GL_BLEND); - OpenGlHelper.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, 1, 0); - - GL11.glEnable(GL11.GL_TEXTURE_2D); - Minecraft.getMinecraft().getTextureManager().bindTexture(texture); - - float[] c = floats(color); - GL11.glColor4f(c[0], c[1], c[2], alpha); - - Tessellator tessellator = Tessellator.instance; - tessellator.startDrawingQuads(); - tessellator.addVertexWithUV(x, y + height, 0.0, 0.0, 1.0); - tessellator.addVertexWithUV(x + width, y + height, 0.0, 1.0, 1.0); - tessellator.addVertexWithUV(x + width, y, 0.0, 1.0, 0.0); - tessellator.addVertexWithUV(x, y, 0.0, 0.0, 0.0); - tessellator.draw(); - - } - - public static void drawQuad(IIcon icon, double x, double y, double width, double height, int color, float alpha) { - - GL11.glEnable(GL11.GL_BLEND); - OpenGlHelper.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, 1, 0); - - GL11.glEnable(GL11.GL_TEXTURE_2D); - Minecraft.getMinecraft().getTextureManager().bindTexture(TextureMap.locationBlocksTexture); - - float[] c = floats(color); - GL11.glColor4f(c[0], c[1], c[2], alpha); - - Tessellator tessellator = Tessellator.instance; - tessellator.startDrawingQuads(); - tessellator.addVertexWithUV(x, y + height, 0.0, icon.getMinU(), icon.getMaxV()); - tessellator.addVertexWithUV(x + width, y + height, 0.0, icon.getMaxU(), icon.getMaxV()); - tessellator.addVertexWithUV(x + width, y, 0.0, icon.getMaxU(), icon.getMinV()); - tessellator.addVertexWithUV(x, y, 0.0, icon.getMinU(), icon.getMinV()); - tessellator.draw(); - - } - - public static void drawAspect(double centerPixelX, double centerPixelY, double pixelSize, Aspect aspect, int amount) { - final int textureSize = 16; - - GL11.glPushMatrix(); - final double scale = pixelSize / textureSize; - GL11.glScaled(scale, scale, scale); - UtilsFX.drawTag((centerPixelX - pixelSize / 2) / scale, (centerPixelY - pixelSize / 2) / scale, aspect, amount, 0, 0, GL11.GL_ONE_MINUS_SRC_ALPHA, 1.0F, false); - GL11.glDisable(GL11.GL_LIGHTING); - GL11.glPopMatrix(); - } - - public static void drawSimpleLabel(GuiScreen gui, String text, double textX, double textY, int fontColor, int bgColor, boolean centered) { - GL11.glPushMatrix(); - double dTextX = textX - (double) (int) textX; - double dTextY = textY - (double) (int) textY; - double textWidth = gui.mc.fontRenderer.getStringWidth(text); - double xOffsetL = centered ? -textWidth / 2.0 - 2 : -2; - double xOffsetR = centered ? textWidth / 2.0 + 2 : textWidth + 2; - GL11.glTranslated(dTextX, dTextY, 0.0); - drawGradientRect((int) textX + xOffsetL, (int) textY - 2, (int) textX + xOffsetR, (int) textY + gui.mc.fontRenderer.FONT_HEIGHT + 2, 0, bgColor, bgColor); - if (centered) - gui.drawCenteredString(gui.mc.fontRenderer, text, (int) textX, (int) textY, fontColor); - else - gui.drawString(gui.mc.fontRenderer, text, (int) textX, (int) textY, fontColor); - GL11.glPopMatrix(); - } - - public static void drawSimpleTooltip(GuiScreen gui, List text, double x, double y, int fontColor, int bgColor) { - if (text.isEmpty()) return; - - int maxTextWidth = 0; - for (String str : text) { - int strWidth = gui.mc.fontRenderer.getStringWidth(str); - if (strWidth > maxTextWidth) - maxTextWidth = strWidth; - } - - int boxWidth = maxTextWidth + 6; - int boxHeight = text.size() * (gui.mc.fontRenderer.FONT_HEIGHT + 2) + 6; - - double dx = x - (double) (int) x; - double dy = y - (double) (int) y; - - GL11.glPushMatrix(); - - drawGradientRect(x, y, x + boxWidth, y + boxHeight, bgColor, bgColor); - GL11.glTranslated(dx, dy, 301); - for (int i = 0; i < text.size(); i++) { - gui.drawString(gui.mc.fontRenderer, text.get(i), (int) x + 3, (int) y + 3 + i * (gui.mc.fontRenderer.FONT_HEIGHT + 2), fontColor); - } - - GL11.glPopMatrix(); - } - - public static float[] floats(int rgb) { - return new float[]{(float) (rgb >> 16 & 255) / 255.0F, (float) (rgb >> 8 & 255) / 255.0F, (float) (rgb & 255) / 255.0F}; - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/gregtech/GTScannerHandler.java b/src/main/java/com/sinthoras/visualprospecting/integration/gregtech/GTScannerHandler.java new file mode 100644 index 00000000..7f96617a --- /dev/null +++ b/src/main/java/com/sinthoras/visualprospecting/integration/gregtech/GTScannerHandler.java @@ -0,0 +1,119 @@ +package com.sinthoras.visualprospecting.integration.gregtech; + +import static com.gtnewhorizon.gtnhlib.util.numberformatting.NumberFormatUtil.formatNumber; +import static gregtech.api.util.GTUtility.ItemNBT.getProspectionFrontPage; +import static gregtech.api.util.GTUtility.ItemNBT.getProspectionGridPage; +import static gregtech.api.util.GTUtility.ItemNBT.getProspectionOilLocationPage; +import static gregtech.api.util.GTUtility.ItemNBT.getProspectionOilPosStr; +import static gregtech.api.util.GTUtility.ItemNBT.setNBT; + +import java.util.List; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.nbt.NBTTagString; +import net.minecraftforge.fluids.FluidStack; + +import com.sinthoras.visualprospecting.Tags; +import com.sinthoras.visualprospecting.database.OreVeinPosition; +import com.sinthoras.visualprospecting.database.ServerCache; + +import gregtech.api.metatileentity.MetaTileEntity; +import gregtech.api.recipe.RecipeMaps; +import gregtech.api.util.GTScannerResult; +import gregtech.api.util.GTUtility; +import gregtech.loaders.postload.ScannerHandlerLoader; + +public abstract class GTScannerHandler { + + public static void onPostLoad() { + // add at start to override default behaviour + RecipeMaps.scannerHandlers.addFirst(GTScannerHandler::analyzeProspectorData); + } + + private static @Nullable GTScannerResult analyzeProspectorData(@Nonnull MetaTileEntity aScanner, + @Nonnull ItemStack aInput, @Nullable ItemStack aSpecialSlot, @Nullable FluidStack aFluid) { + + // validate inputs + if (!ScannerHandlerLoader.isValidProspectionBook(aInput, aSpecialSlot)) return null; + // ensure it has the right flag + if (!aInput.getTagCompound().hasKey(Tags.VISUALPROSPECTING_FLAG)) return null; + + ItemStack output = GTUtility.copyAmount(1, aInput); + assert output != null; + GTUtility.ItemNBT.setBookTitle(output, "Analyzed Prospection Data"); + final NBTTagCompound compound = output.getTagCompound(); + final int dimensionId = compound.getInteger(Tags.PROSPECTION_DIMENSION_ID); + final int blockX = compound.getInteger(Tags.PROSPECTION_BLOCK_X); + final int blockY = compound.getInteger(Tags.PROSPECTION_BLOCK_Y); + final int blockZ = compound.getInteger(Tags.PROSPECTION_BLOCK_Z); + final int blockRadius = compound.getInteger(Tags.PROSPECTION_ORE_RADIUS); + final int numberOfUndergroundFluids = compound.getInteger(Tags.PROSPECTION_NUMBER_OF_UNDERGROUND_FLUID); + final String position = GTUtility.ItemNBT.getProspectionBookTitle(dimensionId, blockX, blockY, blockZ); + + // generate starting pages + final NBTTagList bookPages = new NBTTagList(); + final String frontPage = getProspectionFrontPage( + position, + formatNumber(numberOfUndergroundFluids), + formatNumber(blockRadius)).replace("\nOils: ", "\nFluids: ") + .replace("\nCheck NEI to confirm orevein type", "\nResults are synchronized to your map"); + bookPages.appendTag(new NBTTagString(frontPage)); + + // append ores + final List foundOreVeins = ServerCache.instance + .prospectOreBlockRadius(dimensionId, blockX, blockZ, blockRadius); + if (!foundOreVeins.isEmpty()) { + final int pageSize = 7; + final int numberOfPages = (foundOreVeins.size() + pageSize) / pageSize; // Equals to + // ceil((foundOreVeins.size()) + + for (int pageNumber = 0; pageNumber < numberOfPages; pageNumber++) { + final StringBuilder pageString = new StringBuilder(); + for (int i = 0; i < pageSize; i++) { + final int veinId = pageNumber * pageSize + i; + if (veinId < foundOreVeins.size()) { + final OreVeinPosition oreVein = foundOreVeins.get(veinId); + pageString.append(oreVein.getBlockX()).append(",").append(oreVein.getBlockZ()).append(" - ") + .append(oreVein.veinType.getVeinName()).append("\n"); + } + } + String pageCounter = numberOfPages > 1 ? String.format(" %d/%d", pageNumber + 1, numberOfPages) : ""; + NBTTagString pageTag = new NBTTagString(String.format("Ore Veins %s\n\n", pageCounter) + pageString); + bookPages.appendTag(pageTag); + } + } + + // append fluids + if (compound.hasKey(Tags.PROSPECTION_FLUIDS)) { + GTUtility.ItemNBT.fillBookWithList( + bookPages, + "Fluids%s\n\n", + "\n", + 9, + compound.getString(Tags.PROSPECTION_FLUIDS).split("\\|")); + + bookPages.appendTag(new NBTTagString(getProspectionGridPage().replace("Oil notes", "Fluid notes"))); + bookPages.appendTag( + new NBTTagString(getProspectionOilLocationPage(getProspectionOilPosStr(blockX, blockZ)))); + } + + // set book data + compound.setString("author", position); + compound.setTag("pages", bookPages); + setNBT(output, compound); + + // Mimic original behaviour + return new GTScannerResult( + ScannerHandlerLoader.SCAN_PROSPECTING_DATA_EUT, + ScannerHandlerLoader.SCAN_PROSPECTING_DATA_DURATION, + 1, + 0, + 0, + output); + } +} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/gregtech/UndergroundFluidsWrapper.java b/src/main/java/com/sinthoras/visualprospecting/integration/gregtech/UndergroundFluidsWrapper.java deleted file mode 100644 index 0ddfaae5..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/gregtech/UndergroundFluidsWrapper.java +++ /dev/null @@ -1,104 +0,0 @@ -package com.sinthoras.visualprospecting.integration.gregtech; - -import gregtech.GT_Mod; -import gregtech.api.objects.GT_UO_Dimension; -import gregtech.api.objects.GT_UO_Fluid; -import gregtech.api.objects.XSTR; -import gregtech.common.GT_UndergroundOil; -import net.minecraft.world.ChunkCoordIntPair; -import net.minecraft.world.World; -import net.minecraftforge.fluids.FluidRegistry; -import net.minecraftforge.fluids.FluidStack; - -import java.util.HashMap; -import java.util.Map; - -import static gregtech.api.objects.XSTR.XSTR_INSTANCE; -import static gregtech.common.GT_Proxy.*; -import static gregtech.common.GT_Proxy.GTOIL; -import static gregtech.common.GT_UndergroundOil.undergroundOil; - -public class UndergroundFluidsWrapper { - - private final static boolean isGTNHGregTechUndergroundFluids; - private final static float MODE_READ_ONLY = -1; - - static { - boolean foundMethod; - try { - GT_UndergroundOil.class.getDeclaredMethod("undergroundOil", World.class, int.class, int.class, float.class); - foundMethod = true; - } - catch (NoSuchMethodException e) { - foundMethod = false; - } - isGTNHGregTechUndergroundFluids = foundMethod; - } - - // GTNH's GregTech5-Unofficial was rewritten and requires different access - public static FluidStack prospectFluid(World world, int chunkX, int chunkZ) { - if(isGTNHGregTechUndergroundFluids) { - return undergroundOil(world, chunkX, chunkZ, MODE_READ_ONLY); - } - else { - return vanillaProspectFluid(world, chunkX, chunkZ); - } - } - - // Rewrite from GT_UndergroundOil.undergroundOil(Chunk chunk, float readOrDrainCoefficient), - // because there is no reason to require a chunk to be loaded - private static FluidStack vanillaProspectFluid(World world, int chunkX, int chunkZ) { - final ChunkCoordIntPair chunkCoordinate = new ChunkCoordIntPair(chunkX, chunkZ); - int dimensionId = world.provider.dimensionId; - GT_UO_Dimension dimension = GT_Mod.gregtechproxy.mUndergroundOil.GetDimension(dimensionId); - if (dimension == null) { - return null; - } - - Map chunkData = dimensionWiseChunkData.computeIfAbsent(dimensionId, k -> new HashMap<>(1024)); - - int[] tInts = chunkData.get(chunkCoordinate); - - if (tInts == null) { - tInts = getDefaultChunkDataOnCreation(); - } - else if (tInts[GTOIL] == 0) { - return new FluidStack(FluidRegistry.getFluid(tInts[GTOILFLUID]), 0); - } - - final XSTR tRandom = new XSTR(world.getSeed() + dimensionId * 2L + (chunkX >> 3) + 8267L * (chunkZ >> 3)); - - GT_UO_Fluid uoFluid = dimension.getRandomFluid(tRandom); - - FluidStack fluidInChunk; - - if (uoFluid == null || uoFluid.getFluid() == null) { - tInts[GTOILFLUID] = Integer.MAX_VALUE;//null fluid pointer... kind of - tInts[GTOIL] = 0; - chunkData.put(chunkCoordinate, tInts);//update hash map - return null; - } - else { - if (tInts[GTOILFLUID] == uoFluid.getFluid().getID()) {//if stored fluid matches uoFluid - fluidInChunk = new FluidStack(uoFluid.getFluid(), tInts[GTOIL]); - } - else { - fluidInChunk = new FluidStack(uoFluid.getFluid(), uoFluid.getRandomAmount(tRandom)); - fluidInChunk.amount = (int) ((float) fluidInChunk.amount * (0.75f + (XSTR_INSTANCE.nextFloat() / 2f)));//Randomly change amounts by +/- 25% - } - tInts[GTOIL] = fluidInChunk.amount; - tInts[GTOILFLUID] = fluidInChunk.getFluidID(); - } - - if (fluidInChunk.amount <= GT_UndergroundOil.DIVIDER) { - fluidInChunk.amount = 0;//return informative stack - tInts[GTOIL] = 0;//so in next access it will stop way above - } - else { - fluidInChunk.amount = fluidInChunk.amount / GT_UndergroundOil.DIVIDER;//give moderate extraction speed - } - - chunkData.put(chunkCoordinate, tInts);//update hash map - return fluidInChunk; - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/gregtech/VeinDatabase.java b/src/main/java/com/sinthoras/visualprospecting/integration/gregtech/VeinDatabase.java new file mode 100644 index 00000000..a4372506 --- /dev/null +++ b/src/main/java/com/sinthoras/visualprospecting/integration/gregtech/VeinDatabase.java @@ -0,0 +1,30 @@ +package com.sinthoras.visualprospecting.integration.gregtech; + +import java.util.Optional; + +import net.minecraft.world.ChunkCoordIntPair; + +import com.sinthoras.visualprospecting.database.OreVeinPosition; +import com.sinthoras.visualprospecting.database.ServerCache; +import com.sinthoras.visualprospecting.database.veintypes.VeinType; + +import gregtech.crossmod.visualprospecting.IDatabase; + +public class VeinDatabase implements IDatabase { + + @Override + public Optional getVeinName(int dimensionId, ChunkCoordIntPair coordinates) { + OreVeinPosition oreVein = ServerCache.instance + .getOreVein(dimensionId, coordinates.chunkXPos, coordinates.chunkZPos); + if (oreVein != null && oreVein.veinType != VeinType.NO_VEIN) { + // Unfortunately, there's not a very good way to localize this. This method is used to drive information + // in the Metrics Transmitter cover in GT5U, which operates entirely server-side. At best, we could try to + // capture which language the original cover attaching player is using, maybe. (I think this requires + // reflection, of course. Why should things be easy?) Even then, it wouldn't help in a multiplayer scenario + // with users having different locales. + return Optional.of(oreVein.veinType.getVeinName()); + } else { + return Optional.empty(); + } + } +} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/JourneyMapState.java b/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/JourneyMapState.java deleted file mode 100644 index cb60242b..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/JourneyMapState.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.sinthoras.visualprospecting.integration.journeymap; - -import com.sinthoras.visualprospecting.integration.journeymap.buttons.LayerButton; -import com.sinthoras.visualprospecting.integration.journeymap.buttons.OreVeinButton; -import com.sinthoras.visualprospecting.integration.journeymap.buttons.ThaumcraftNodeButton; -import com.sinthoras.visualprospecting.integration.journeymap.buttons.UndergroundFluidButton; -import com.sinthoras.visualprospecting.integration.journeymap.render.*; -import com.sinthoras.visualprospecting.integration.journeymap.waypoints.OreVeinWaypointManager; -import com.sinthoras.visualprospecting.integration.journeymap.waypoints.ThaumcraftNodeWaypointManager; -import com.sinthoras.visualprospecting.integration.journeymap.waypoints.WaypointManager; -import journeymap.client.render.map.GridRenderer; - -import java.util.ArrayList; -import java.util.List; - -import static com.sinthoras.visualprospecting.Utils.isTCNodeTrackerInstalled; -import static com.sinthoras.visualprospecting.integration.journeymap.Reflection.getJourneyMapGridRenderer; - -public class JourneyMapState { - - public static JourneyMapState instance = new JourneyMapState(); - - public final List buttons = new ArrayList<>(); - public final List renderers = new ArrayList<>(); - public final List waypointManagers = new ArrayList<>(); - - public JourneyMapState() { - if(isTCNodeTrackerInstalled()) { - buttons.add(ThaumcraftNodeButton.instance); - renderers.add(ThaumcraftNodeRenderer.instance); - waypointManagers.add(ThaumcraftNodeWaypointManager.instance); - } - - buttons.add(UndergroundFluidButton.instance); - renderers.add(UndergroundFluidRenderer.instance); - renderers.add(UndergroundFluidChunkRenderer.instance); - - buttons.add(OreVeinButton.instance); - renderers.add(OreVeinRenderer.instance); - waypointManagers.add(OreVeinWaypointManager.instance); - } - - public void openJourneyMapAt(int blockX, int blockZ) { - final GridRenderer gridRenderer = getJourneyMapGridRenderer(); - assert gridRenderer != null; - - gridRenderer.center(gridRenderer.getMapType(), blockX, blockZ, gridRenderer.getZoom()); - } - - public void openJourneyMapAt(int blockX, int blockZ, int zoom) { - final GridRenderer gridRenderer = getJourneyMapGridRenderer(); - assert gridRenderer != null; - - gridRenderer.center(gridRenderer.getMapType(), blockX, blockZ, zoom); - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/Reflection.java b/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/Reflection.java deleted file mode 100644 index 3d118124..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/Reflection.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.sinthoras.visualprospecting.integration.journeymap; - -import com.sinthoras.visualprospecting.VP; -import journeymap.client.render.map.GridRenderer; -import journeymap.client.ui.fullscreen.Fullscreen; - -import java.lang.reflect.Field; - -public class Reflection { - - private static Field gridRenderer; - - static { - try { - gridRenderer = Fullscreen.class.getDeclaredField("gridRenderer"); - gridRenderer.setAccessible(true); - } - catch (Exception e) { - VP.error("Failed to access private fields in JourneyMap!"); - e.printStackTrace(); - } - } - - public static GridRenderer getJourneyMapGridRenderer() { - try { - return (GridRenderer) gridRenderer.get(null); - } - catch (IllegalAccessException e) { - e.printStackTrace(); - return null; - } - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/ResetClientCacheConfirmation.java b/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/ResetClientCacheConfirmation.java deleted file mode 100644 index 4a63aad0..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/ResetClientCacheConfirmation.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.sinthoras.visualprospecting.integration.journeymap; - -import com.sinthoras.visualprospecting.database.ClientCache; -import journeymap.client.ui.UIManager; -import journeymap.client.ui.component.Button; -import journeymap.client.ui.component.ButtonList; -import journeymap.client.ui.component.JmUI; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiButton; -import net.minecraft.client.resources.I18n; -import net.minecraft.util.ChatComponentTranslation; -import net.minecraft.util.IChatComponent; - -public class ResetClientCacheConfirmation extends JmUI { - private Button confirmButton; - private Button cancelButton; - - public ResetClientCacheConfirmation() { - super(I18n.format("visualprospecting.resetprogress.title")); - } - - public void func_73866_w_() { - buttonList.clear(); - confirmButton = new Button(I18n.format("visualprospecting.resetprogress.confirm")); - cancelButton = new Button(I18n.format("visualprospecting.resetprogress.cancel")); - buttonList.add(confirmButton); - buttonList.add(cancelButton); - } - - protected void layoutButtons() { - if (buttonList.isEmpty()) { - func_73866_w_(); - } - final int x = width / 2; - final int y = height / 2; - drawCenteredString(getFontRenderer(), I18n.format("visualprospecting.resetprogress.prompt"), x, y, 0xFFFFFF); - final ButtonList row = new ButtonList(confirmButton, cancelButton); - row.layoutCenteredHorizontal(x, y + 18, true, 4); - } - - protected void func_146284_a(GuiButton guibutton) { - if(guibutton == confirmButton) { - ClientCache.instance.resetPlayerProgression(); - final IChatComponent confirmation = new ChatComponentTranslation("visualprospecting.resetprogress.confirmation"); - confirmation.getChatStyle().setItalic(true); - Minecraft.getMinecraft().thePlayer.addChatMessage(confirmation); - UIManager.getInstance().openFullscreenMap(); - } - else if(guibutton == cancelButton) { - UIManager.getInstance().openMapActions(); - } - } - - protected void func_73869_a(char c, int i) { - switch(i) { - case 1: - UIManager.getInstance().openMapActions(); - default: - } - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/buttons/LayerButton.java b/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/buttons/LayerButton.java deleted file mode 100644 index 91e9b87f..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/buttons/LayerButton.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.sinthoras.visualprospecting.integration.journeymap.buttons; - -import com.sinthoras.visualprospecting.integration.model.SupportedMods; -import com.sinthoras.visualprospecting.integration.model.buttons.*; -import journeymap.client.ui.theme.ThemeToggle; - -public class LayerButton extends com.sinthoras.visualprospecting.integration.model.buttons.LayerButton { - - private ThemeToggle button; - private final ButtonManager manager; - - private boolean isActive = false; - - public LayerButton(ButtonManager manager) { - super(manager, SupportedMods.JourneyMap); - this.manager = manager; - } - - @Override - public void updateState(boolean active) { - isActive = active; - if(button != null) { - button.setToggled(active, false); - } - } - - public void setButton(ThemeToggle button) { - this.button = button; - } - - public String getButtonTextKey() { - return manager.getButtonTextKey(); - } - - public String getIconName() { - return manager.getIconName(); - } - - public boolean isActive() { - return isActive; - } - - public void toggle() { - manager.toggle(); - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/buttons/OreVeinButton.java b/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/buttons/OreVeinButton.java deleted file mode 100644 index cea077ee..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/buttons/OreVeinButton.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.sinthoras.visualprospecting.integration.journeymap.buttons; - -import com.sinthoras.visualprospecting.integration.model.buttons.OreVeinButtonManager; - -public class OreVeinButton extends LayerButton { - - public static final OreVeinButton instance = new OreVeinButton(); - - public OreVeinButton() { - super(OreVeinButtonManager.instance); - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/buttons/ThaumcraftNodeButton.java b/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/buttons/ThaumcraftNodeButton.java deleted file mode 100644 index 3ac64db9..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/buttons/ThaumcraftNodeButton.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.sinthoras.visualprospecting.integration.journeymap.buttons; - -import com.sinthoras.visualprospecting.integration.model.buttons.ThaumcraftNodeButtonManager; - -public class ThaumcraftNodeButton extends LayerButton { - - public static final ThaumcraftNodeButton instance = new ThaumcraftNodeButton(); - - public ThaumcraftNodeButton() { - super(ThaumcraftNodeButtonManager.instance); - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/buttons/UndergroundFluidButton.java b/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/buttons/UndergroundFluidButton.java deleted file mode 100644 index edd40a15..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/buttons/UndergroundFluidButton.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.sinthoras.visualprospecting.integration.journeymap.buttons; - -import com.sinthoras.visualprospecting.integration.model.buttons.UndergroundFluidButtonManager; - -public class UndergroundFluidButton extends LayerButton { - - public static final UndergroundFluidButton instance = new UndergroundFluidButton(); - - public UndergroundFluidButton() { - super(UndergroundFluidButtonManager.instance); - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/drawsteps/ClickableDrawStep.java b/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/drawsteps/ClickableDrawStep.java deleted file mode 100644 index 675dbcf7..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/drawsteps/ClickableDrawStep.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.sinthoras.visualprospecting.integration.journeymap.drawsteps; - -import com.sinthoras.visualprospecting.integration.model.locations.IWaypointAndLocationProvider; -import journeymap.client.render.draw.DrawStep; -import net.minecraft.client.gui.FontRenderer; - -import java.util.List; - -public interface ClickableDrawStep extends DrawStep { - - List getTooltip(); - - void drawTooltip(FontRenderer fontRenderer, int mouseX, int mouseY, int displayWidth, int displayHeight); - - boolean isMouseOver(int mouseX, int mouseY); - - void onActionKeyPressed(); - - IWaypointAndLocationProvider getLocationProvider(); -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/drawsteps/OreVeinDrawStep.java b/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/drawsteps/OreVeinDrawStep.java deleted file mode 100644 index 76820cb3..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/drawsteps/OreVeinDrawStep.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.sinthoras.visualprospecting.integration.journeymap.drawsteps; - -import com.sinthoras.visualprospecting.Config; -import com.sinthoras.visualprospecting.Tags; -import com.sinthoras.visualprospecting.integration.DrawUtils; -import com.sinthoras.visualprospecting.integration.model.locations.IWaypointAndLocationProvider; -import com.sinthoras.visualprospecting.integration.model.locations.OreVeinLocation; -import journeymap.client.render.draw.DrawUtil; -import journeymap.client.render.map.GridRenderer; -import net.minecraft.client.gui.FontRenderer; -import net.minecraft.init.Blocks; -import net.minecraft.util.IIcon; -import net.minecraft.util.ResourceLocation; - -import java.awt.geom.Point2D; -import java.util.ArrayList; -import java.util.List; - -public class OreVeinDrawStep implements ClickableDrawStep { - - private static final ResourceLocation depletedTextureLocation = new ResourceLocation(Tags.MODID, "textures/depleted.png"); - - private final OreVeinLocation oreVeinLocation; - - private double iconX; - private double iconY; - private double iconSize; - - public OreVeinDrawStep(OreVeinLocation oreVeinLocation) { - this.oreVeinLocation = oreVeinLocation; - } - - @Override - public List getTooltip() { - final List tooltip = new ArrayList<>(); - if(oreVeinLocation.isDepleted()) { - tooltip.add(oreVeinLocation.getDepletedHint()); - } - if(oreVeinLocation.isActiveAsWaypoint()) { - tooltip.add(oreVeinLocation.getActiveWaypointHint()); - } - tooltip.add(oreVeinLocation.getName()); - if(oreVeinLocation.isDepleted() == false) { - tooltip.addAll(oreVeinLocation.getMaterialNames()); - } - tooltip.add(oreVeinLocation.getToggleDepletedHint()); - return tooltip; - } - - @Override - public void drawTooltip(FontRenderer fontRenderer, int mouseX, int mouseY, int displayWidth, int displayHeight) { - - } - - @Override - public boolean isMouseOver(int mouseX, int mouseY) { - return mouseX >= iconX && mouseX <= iconX + iconSize && mouseY >= iconY && mouseY <= iconY + iconSize; - } - - @Override - public void onActionKeyPressed() { - oreVeinLocation.toggleOreVein(); - } - - @Override - public IWaypointAndLocationProvider getLocationProvider() { - return oreVeinLocation; - } - - @Override - public void draw(double draggedPixelX, double draggedPixelY, GridRenderer gridRenderer, float drawScale, double fontScale, double rotation) { - iconSize = 32 * fontScale; - final double iconSizeHalf = iconSize / 2; - final Point2D.Double blockAsPixel = gridRenderer.getBlockPixelInGrid(oreVeinLocation.getBlockX(), oreVeinLocation.getBlockZ()); - final Point2D.Double pixel = new Point2D.Double(blockAsPixel.getX() + draggedPixelX, blockAsPixel.getY() + draggedPixelY); - - - if(gridRenderer.getZoom() >= Config.minZoomLevelForOreLabel && oreVeinLocation.isDepleted() == false) { - final int fontColor = oreVeinLocation.drawSearchHighlight() ? 0xFFFFFF : 0x7F7F7F; - DrawUtil.drawLabel(oreVeinLocation.getName(), pixel.getX(), pixel.getY() - iconSize, DrawUtil.HAlign.Center, DrawUtil.VAlign.Middle, 0, 180, fontColor, 255, fontScale, false, rotation); - } - - iconX = pixel.getX() - iconSizeHalf; - iconY = pixel.getY() - iconSizeHalf; - final IIcon blockIcon = Blocks.stone.getIcon(0, 0); - DrawUtils.drawQuad(blockIcon, iconX, iconY, iconSize, iconSize, 0xFFFFFF, 255); - - DrawUtils.drawQuad(oreVeinLocation.getIconFromPrimaryOre(), iconX, iconY, iconSize, iconSize, oreVeinLocation.getColor(), 255); - - if(oreVeinLocation.drawSearchHighlight() == false || oreVeinLocation.isDepleted()) { - DrawUtil.drawRectangle(iconX, iconY, iconSize, iconSize, 0x000000, 150); - if(oreVeinLocation.isDepleted()) { - DrawUtils.drawQuad(depletedTextureLocation, iconX, iconY, iconSize, iconSize, 0xFFFFFF, 255); - } - } - - if(oreVeinLocation.isActiveAsWaypoint()) { - final double thickness = iconSize / 8; - final int borderAlpha = 204; - final int color = 0xFFD700; - DrawUtil.drawRectangle(iconX - thickness, iconY - thickness, iconSize + thickness, thickness, color, borderAlpha); - DrawUtil.drawRectangle(iconX + iconSize, iconY - thickness, thickness, iconSize + thickness, color, borderAlpha); - DrawUtil.drawRectangle(iconX, iconY + iconSize, iconSize + thickness, thickness, color, borderAlpha); - DrawUtil.drawRectangle(iconX - thickness, iconY, thickness, iconSize + thickness, color, borderAlpha); - } - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/drawsteps/ThaumcraftNodeDrawStep.java b/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/drawsteps/ThaumcraftNodeDrawStep.java deleted file mode 100644 index a28eb395..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/drawsteps/ThaumcraftNodeDrawStep.java +++ /dev/null @@ -1,170 +0,0 @@ -package com.sinthoras.visualprospecting.integration.journeymap.drawsteps; - -import com.sinthoras.visualprospecting.Tags; -import com.sinthoras.visualprospecting.integration.DrawUtils; -import com.sinthoras.visualprospecting.integration.model.layers.ThaumcraftNodeLayerManager; -import com.sinthoras.visualprospecting.integration.model.locations.IWaypointAndLocationProvider; -import com.sinthoras.visualprospecting.integration.model.locations.ThaumcraftNodeLocation; -import journeymap.client.render.map.GridRenderer; -import net.minecraft.client.gui.FontRenderer; -import net.minecraft.client.renderer.RenderHelper; -import net.minecraft.util.ResourceLocation; -import org.lwjgl.opengl.GL11; -import org.lwjgl.opengl.GL12; -import thaumcraft.api.aspects.Aspect; -import thaumcraft.client.lib.UtilsFX; - -import java.awt.geom.Point2D; -import java.util.List; - -public class ThaumcraftNodeDrawStep implements ClickableDrawStep { - - private static final ResourceLocation markedTextureLocation = new ResourceLocation(Tags.MODID, "textures/node_marked.png"); - private static final ResourceLocation unmarkedTextureLocation = new ResourceLocation(Tags.MODID, "textures/node_unmarked.png"); - - private final ThaumcraftNodeLocation thaumcraftNodeLocation; - - private double centerPixelX = 0; - private double centerPixelY = 0; - private double clickableRadiusPixelSquared = 0; - - public ThaumcraftNodeDrawStep(ThaumcraftNodeLocation thaumcraftNodeLocation) { - this.thaumcraftNodeLocation = thaumcraftNodeLocation; - } - - @Override - public void draw(double draggedPixelX, double draggedPixelY, GridRenderer gridRenderer, float drawScale, double fontScale, double rotation) { - final double borderSize = 44 * fontScale; - final double borderSizeHalf = borderSize / 2; - final Point2D.Double blockAsPixel = gridRenderer.getBlockPixelInGrid(thaumcraftNodeLocation.getBlockX(), thaumcraftNodeLocation.getBlockZ()); - final Point2D.Double pixel = new Point2D.Double(blockAsPixel.getX() + draggedPixelX, blockAsPixel.getY() + draggedPixelY); - centerPixelX = pixel.getX(); - centerPixelY = pixel.getY(); - clickableRadiusPixelSquared = borderSizeHalf * borderSizeHalf; - - final int alpha = 204; - DrawUtils.drawQuad(thaumcraftNodeLocation.isActiveAsWaypoint() ? markedTextureLocation : unmarkedTextureLocation, pixel.getX() - borderSizeHalf, pixel.getY() - borderSizeHalf, borderSize, borderSize, 0xFFFFFF, alpha); - - final int aspectPixelDiameter = 32; - DrawUtils.drawAspect(pixel.getX(), pixel.getY(), aspectPixelDiameter, thaumcraftNodeLocation.getStrongestAspect(), 0); - } - - public List getTooltip() { - return null; - } - - public boolean isMouseOver(int mouseX, int mouseY) { - final double deltaX = mouseX - centerPixelX; - final double deltaY = mouseY - centerPixelY; - return deltaX * deltaX + deltaY * deltaY <= clickableRadiusPixelSquared; - } - - public void onActionKeyPressed() { - ThaumcraftNodeLayerManager.instance.deleteNode(thaumcraftNodeLocation); - } - - @Override - public IWaypointAndLocationProvider getLocationProvider() { - return thaumcraftNodeLocation; - } - - public void drawTooltip(FontRenderer fontRenderer, int mouseX, int mouseY, int displayWidth, int displayHeight) { - final boolean isWaypoint = thaumcraftNodeLocation.isActiveAsWaypoint(); - final String activeWaypointHint = thaumcraftNodeLocation.getActiveWaypointHint(); - final String title = thaumcraftNodeLocation.getTitle(); - final String nodeDescription = thaumcraftNodeLocation.getDescription(); - final String deleteHint = thaumcraftNodeLocation.getDeleteHint(); - - int maxTextWidth = Math.max(Math.max(fontRenderer.getStringWidth(title), fontRenderer.getStringWidth(nodeDescription)), fontRenderer.getStringWidth(deleteHint)); - if(isWaypoint) { - maxTextWidth = Math.max(maxTextWidth, fontRenderer.getStringWidth(activeWaypointHint)); - } - if(fontRenderer.getBidiFlag()) { - maxTextWidth = (int) Math.ceil(maxTextWidth * 1.25f); - } - - final int aspectRows = (thaumcraftNodeLocation.getAspects().size() + 4) / 5; // Equivalent to Math.ceil(size / 5) - final int aspectColumns = Math.min(thaumcraftNodeLocation.getAspects().size(), 5); - - int pixelX = mouseX + 12; - int pixelY = mouseY - 12; - final int tooltipHeight = (isWaypoint ? 44 : 32) + aspectRows * 16; - final int tooltipWidth = Math.max(aspectColumns * 16, maxTextWidth); - if(pixelX + tooltipWidth > displayWidth) { - pixelX -= 28 + tooltipWidth; - } - if(pixelY + tooltipHeight + 6 > displayHeight) { - pixelY = displayHeight - tooltipHeight - 6; - } - - GL11.glDisable(GL12.GL_RESCALE_NORMAL); - RenderHelper.disableStandardItemLighting(); - GL11.glDisable(GL11.GL_LIGHTING); - GL11.glDisable(GL11.GL_DEPTH_TEST); - - // Draw background - final int backgroundColor = 0xF0100010; - DrawUtils.drawGradientRect(pixelX - 3, pixelY - 4, pixelX + tooltipWidth + 3, pixelY - 3, backgroundColor, backgroundColor); - DrawUtils.drawGradientRect(pixelX - 3, pixelY + tooltipHeight + 3, pixelX + tooltipWidth + 3, pixelY + tooltipHeight + 4, backgroundColor, backgroundColor); - DrawUtils.drawGradientRect(pixelX - 3, pixelY - 3, pixelX + tooltipWidth + 3, pixelY + tooltipHeight + 3, backgroundColor, backgroundColor); - DrawUtils.drawGradientRect(pixelX - 4, pixelY - 3, pixelX - 3, pixelY + tooltipHeight + 3, backgroundColor, backgroundColor); - DrawUtils.drawGradientRect(pixelX + tooltipWidth + 3, pixelY - 3, pixelX + tooltipWidth + 4, pixelY + tooltipHeight + 3, backgroundColor, backgroundColor); - - int verdunGreen = 0x505000FF; - int borderColor = 0x5028007F; - DrawUtils.drawGradientRect(pixelX - 3, pixelY - 3 + 1, pixelX - 3 + 1, pixelY + tooltipHeight + 3 - 1, verdunGreen, borderColor); - DrawUtils.drawGradientRect(pixelX + tooltipWidth + 2, pixelY - 3 + 1, pixelX + tooltipWidth + 3, pixelY + tooltipHeight + 3 - 1, verdunGreen, borderColor); - DrawUtils.drawGradientRect(pixelX - 3, pixelY - 3, pixelX + tooltipWidth + 3, pixelY - 3 + 1, verdunGreen, verdunGreen); - DrawUtils.drawGradientRect(pixelX - 3, pixelY + tooltipHeight + 2, pixelX + tooltipWidth + 3, pixelY + tooltipHeight + 3, borderColor, borderColor); - - // Draw text - int offset = 0; - if(fontRenderer.getBidiFlag()) { - if(isWaypoint) { - final int asWaypointWidth = (int) Math.ceil(fontRenderer.getStringWidth(activeWaypointHint) * 1.1f); - fontRenderer.drawString(title, pixelX + tooltipWidth - asWaypointWidth, pixelY, 0xFFFFFFFF); - offset += 12; - } - final int titleWidth = (int) Math.ceil(fontRenderer.getStringWidth(title) * 1.1f); - fontRenderer.drawString(title, pixelX + tooltipWidth - titleWidth, pixelY + offset, 0xFFFFFFFF); - offset += 12; - final int nodeDescriptonWidth = (int) Math.ceil(fontRenderer.getStringWidth(nodeDescription) * 1.1f); - fontRenderer.drawString(nodeDescription, pixelX + nodeDescriptonWidth - titleWidth, pixelY + offset, 0xFFFFFFFF); - - final int deleteHintWidth = (int) Math.ceil(fontRenderer.getStringWidth(deleteHint) * 1.1f); - fontRenderer.drawString(deleteHint, pixelX + tooltipWidth - deleteHintWidth, pixelY + aspectRows * 16 + offset + 12, 0xFFFFFFFF); - } - else { - if(isWaypoint) { - fontRenderer.drawString(activeWaypointHint, pixelX, pixelY, 0xFFFFFFFF); - offset += 12; - } - fontRenderer.drawString(title, pixelX, pixelY + offset, 0xFFFFFFFF); - offset += 12; - fontRenderer.drawString(nodeDescription, pixelX, pixelY + offset, 0xFFFFFFFF); - - fontRenderer.drawString(deleteHint, pixelX, pixelY + aspectRows * 16 + offset + 12, 0xFFFFFFFF); - } - - // Draw aspects - int aspectX = 0; - int aspectY = 0; - - - for(Aspect aspect : thaumcraftNodeLocation.getAspects().getAspectsSortedAmount()) { - GL11.glPushMatrix(); - UtilsFX.drawTag(pixelX + aspectX * 16, pixelY + aspectY * 16 + offset + 10, aspect, thaumcraftNodeLocation.getAspects().getAmount(aspect), 0, 0.01, 1, 1, false); - GL11.glPopMatrix(); - ++aspectX; - if(aspectX >= 5) { - aspectX = 0; - ++aspectY; - } - } - - GL11.glEnable(GL11.GL_LIGHTING); - GL11.glEnable(GL11.GL_DEPTH_TEST); - RenderHelper.enableStandardItemLighting(); - GL11.glEnable(GL12.GL_RESCALE_NORMAL); - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/drawsteps/UndergroundFluidChunkDrawStep.java b/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/drawsteps/UndergroundFluidChunkDrawStep.java deleted file mode 100644 index 24a21e9a..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/drawsteps/UndergroundFluidChunkDrawStep.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.sinthoras.visualprospecting.integration.journeymap.drawsteps; - -import com.sinthoras.visualprospecting.Config; -import com.sinthoras.visualprospecting.VP; -import com.sinthoras.visualprospecting.integration.model.locations.UndergroundFluidChunkLocation; -import journeymap.client.render.draw.DrawStep; -import journeymap.client.render.draw.DrawUtil; -import journeymap.client.render.map.GridRenderer; - -import java.awt.geom.Point2D; - -public class UndergroundFluidChunkDrawStep implements DrawStep { - - private final UndergroundFluidChunkLocation undergroundFluidChunkLocation; - - public UndergroundFluidChunkDrawStep(UndergroundFluidChunkLocation undergroundFluidChunkLocation) { - this.undergroundFluidChunkLocation = undergroundFluidChunkLocation; - } - - @Override - public void draw(double draggedPixelX, double draggedPixelY, GridRenderer gridRenderer, float drawScale, double fontScale, double rotation) { - final int zoom = gridRenderer.getZoom(); - if (undergroundFluidChunkLocation.getFluidAmount() > 0 && zoom >= Config.minZoomLevelForUndergroundFluidDetails) { - double blockSize = Math.pow(2, zoom); - final Point2D.Double blockAsPixel = gridRenderer.getBlockPixelInGrid(undergroundFluidChunkLocation.getBlockX(), undergroundFluidChunkLocation.getBlockZ()); - final Point2D.Double pixel = new Point2D.Double(blockAsPixel.getX() + draggedPixelX, blockAsPixel.getY() + draggedPixelY); - float alpha = ((float)(undergroundFluidChunkLocation.getFluidAmount() - undergroundFluidChunkLocation.getMinAmountInField())) - / (undergroundFluidChunkLocation.getMaxAmountInField() - undergroundFluidChunkLocation.getMinAmountInField() + 1); - alpha *= alpha * 204; - DrawUtil.drawRectangle(pixel.getX(), pixel.getY(), - VP.chunkWidth * blockSize, VP.chunkDepth * blockSize, - undergroundFluidChunkLocation.getFluid().getColor(), (int)alpha); - - if(undergroundFluidChunkLocation.getFluidAmount() >= undergroundFluidChunkLocation.getMaxAmountInField()) { - final int borderColor = 0xFFD700; - final int borderAlpha = 204; - DrawUtil.drawRectangle(pixel.getX(), pixel.getY(), 15 * blockSize, blockSize, borderColor, borderAlpha); - DrawUtil.drawRectangle(pixel.getX() + 15 * blockSize, pixel.getY(), blockSize, 15 * blockSize, borderColor, borderAlpha); - DrawUtil.drawRectangle(pixel.getX() + 1 * blockSize, pixel.getY() + 15 * blockSize, 15 * blockSize, blockSize, borderColor, borderAlpha); - DrawUtil.drawRectangle(pixel.getX(), pixel.getY() + 1 * blockSize, blockSize, 15 * blockSize, borderColor, borderAlpha); - } - - DrawUtil.drawLabel(undergroundFluidChunkLocation.getFluidAmountFormatted(), - pixel.getX() + 13 * blockSize, pixel.getY() + 13 * blockSize, - DrawUtil.HAlign.Left, DrawUtil.VAlign.Above, - 0, 180, 0x00FFFFFF, 255, fontScale, false, rotation); - } - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/drawsteps/UndergroundFluidDrawStep.java b/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/drawsteps/UndergroundFluidDrawStep.java deleted file mode 100644 index 32d83356..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/drawsteps/UndergroundFluidDrawStep.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.sinthoras.visualprospecting.integration.journeymap.drawsteps; - -import com.sinthoras.visualprospecting.VP; -import com.sinthoras.visualprospecting.integration.model.locations.UndergroundFluidLocation; -import journeymap.client.render.draw.DrawStep; -import journeymap.client.render.draw.DrawUtil; -import journeymap.client.render.map.GridRenderer; - -import java.awt.geom.Point2D; - -public class UndergroundFluidDrawStep implements DrawStep { - - private final UndergroundFluidLocation undergroundFluidLocation; - - public UndergroundFluidDrawStep(UndergroundFluidLocation undergroundFluidLocation) { - this.undergroundFluidLocation = undergroundFluidLocation; - } - - @Override - public void draw(double draggedPixelX, double draggedPixelY, GridRenderer gridRenderer, float drawScale, double fontScale, double rotation) { - final int maxAmountInField = undergroundFluidLocation.getMaxProduction(); - if (maxAmountInField > 0) { - final double blockSize = Math.pow(2, gridRenderer.getZoom()); - final Point2D.Double blockAsPixel = gridRenderer.getBlockPixelInGrid(undergroundFluidLocation.getBlockX(), undergroundFluidLocation.getBlockZ()); - final Point2D.Double pixel = new Point2D.Double(blockAsPixel.getX() + draggedPixelX, blockAsPixel.getY() + draggedPixelY); - - final int borderColor = undergroundFluidLocation.getFluid().getColor(); - final int borderAlpha = 204; - DrawUtil.drawRectangle(pixel.getX(), pixel.getY(), VP.undergroundFluidSizeChunkX * VP.chunkWidth * blockSize, 2 * blockSize, borderColor, borderAlpha); - DrawUtil.drawRectangle(pixel.getX() + VP.undergroundFluidSizeChunkX * VP.chunkWidth * blockSize, pixel.getY(), 2 * blockSize, VP.undergroundFluidSizeChunkZ * VP.chunkDepth * blockSize, borderColor, borderAlpha); - DrawUtil.drawRectangle(pixel.getX() + 2 * blockSize, pixel.getY() + VP.undergroundFluidSizeChunkZ * VP.chunkDepth * blockSize, VP.undergroundFluidSizeChunkX * VP.chunkWidth * blockSize, 2 * blockSize, borderColor, borderAlpha); - DrawUtil.drawRectangle(pixel.getX(), pixel.getY() + 2 * blockSize, 2 * blockSize, VP.undergroundFluidSizeChunkZ * VP.chunkDepth * blockSize, borderColor, borderAlpha); - - final String label = undergroundFluidLocation.getMinProduction() + "L - " + maxAmountInField + "L " + undergroundFluidLocation.getFluid().getLocalizedName(); - DrawUtil.drawLabel(label, pixel.getX() + VP.chunkWidth * blockSize, pixel.getY(), DrawUtil.HAlign.Right, DrawUtil.VAlign.Below, 0, 180, 0x00FFFFFF, 255, fontScale, false, rotation); - } - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/render/LayerRenderer.java b/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/render/LayerRenderer.java deleted file mode 100644 index bb53299a..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/render/LayerRenderer.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.sinthoras.visualprospecting.integration.journeymap.render; - -import com.sinthoras.visualprospecting.integration.model.SupportedMods; -import com.sinthoras.visualprospecting.integration.model.layers.LayerManager; -import com.sinthoras.visualprospecting.integration.model.locations.ILocationProvider; -import journeymap.client.render.draw.DrawStep; - -import java.util.ArrayList; -import java.util.List; - -public abstract class LayerRenderer extends com.sinthoras.visualprospecting.integration.model.layers.LayerRenderer { - - private final LayerManager manager; - - protected List drawSteps = new ArrayList<>(); - - public LayerRenderer(LayerManager manager) { - super(manager, SupportedMods.JourneyMap); - this.manager = manager; - } - - public boolean isLayerActive() { - return manager.isLayerActive(); - } - - public List getDrawStepsCachedForInteraction() { - return drawSteps; - } - - public List getDrawStepsCachedForRendering() { - return drawSteps; - } - - @Override - public void updateVisibleElements(List visibleElements) { - drawSteps = (List) mapLocationProviderToDrawStep(visibleElements); - } - - protected abstract List mapLocationProviderToDrawStep(List visibleElements); -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/render/OreVeinRenderer.java b/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/render/OreVeinRenderer.java deleted file mode 100644 index 2e107bc5..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/render/OreVeinRenderer.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.sinthoras.visualprospecting.integration.journeymap.render; - -import com.sinthoras.visualprospecting.integration.journeymap.drawsteps.ClickableDrawStep; -import com.sinthoras.visualprospecting.integration.journeymap.drawsteps.OreVeinDrawStep; -import com.sinthoras.visualprospecting.integration.model.layers.OreVeinLayerManager; -import com.sinthoras.visualprospecting.integration.model.locations.ILocationProvider; -import com.sinthoras.visualprospecting.integration.model.locations.OreVeinLocation; - -import java.util.ArrayList; -import java.util.List; - -public class OreVeinRenderer extends WaypointProviderLayerRenderer { - - public static final OreVeinRenderer instance = new OreVeinRenderer(); - - public OreVeinRenderer() { - super(OreVeinLayerManager.instance); - } - - @Override - public List mapLocationProviderToDrawStep(List visibleElements) { - final List drawSteps = new ArrayList<>(); - visibleElements.stream() - .map(element -> (OreVeinLocation) element) - .forEach(location -> drawSteps.add(new OreVeinDrawStep(location))); - return drawSteps; - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/render/ThaumcraftNodeRenderer.java b/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/render/ThaumcraftNodeRenderer.java deleted file mode 100644 index b6891d89..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/render/ThaumcraftNodeRenderer.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.sinthoras.visualprospecting.integration.journeymap.render; - -import com.sinthoras.visualprospecting.integration.journeymap.drawsteps.ClickableDrawStep; -import com.sinthoras.visualprospecting.integration.journeymap.drawsteps.ThaumcraftNodeDrawStep; -import com.sinthoras.visualprospecting.integration.model.layers.ThaumcraftNodeLayerManager; -import com.sinthoras.visualprospecting.integration.model.locations.ILocationProvider; -import com.sinthoras.visualprospecting.integration.model.locations.ThaumcraftNodeLocation; - -import java.util.ArrayList; -import java.util.List; - -public class ThaumcraftNodeRenderer extends WaypointProviderLayerRenderer { - - public static ThaumcraftNodeRenderer instance = new ThaumcraftNodeRenderer(); - - public ThaumcraftNodeRenderer() { - super(ThaumcraftNodeLayerManager.instance); - } - - @Override - public List mapLocationProviderToDrawStep(List visibleElements) { - final List drawSteps = new ArrayList<>(); - visibleElements.stream() - .map(element -> (ThaumcraftNodeLocation) element) - .forEach(location -> drawSteps.add(new ThaumcraftNodeDrawStep(location))); - return drawSteps; - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/render/UndergroundFluidChunkRenderer.java b/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/render/UndergroundFluidChunkRenderer.java deleted file mode 100644 index 9acf255e..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/render/UndergroundFluidChunkRenderer.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.sinthoras.visualprospecting.integration.journeymap.render; - -import com.sinthoras.visualprospecting.integration.journeymap.drawsteps.UndergroundFluidChunkDrawStep; -import com.sinthoras.visualprospecting.integration.model.layers.UndergroundFluidChunkLayerManager; -import com.sinthoras.visualprospecting.integration.model.locations.ILocationProvider; -import com.sinthoras.visualprospecting.integration.model.locations.UndergroundFluidChunkLocation; -import journeymap.client.render.draw.DrawStep; - -import java.util.ArrayList; -import java.util.List; - -public class UndergroundFluidChunkRenderer extends LayerRenderer { - - public static final UndergroundFluidChunkRenderer instance = new UndergroundFluidChunkRenderer(); - - public UndergroundFluidChunkRenderer() { - super(UndergroundFluidChunkLayerManager.instance); - } - - @Override - public List mapLocationProviderToDrawStep(List visibleElements) { - final List drawSteps = new ArrayList<>(); - visibleElements.stream() - .map(element -> (UndergroundFluidChunkLocation) element) - .forEach(location -> drawSteps.add(new UndergroundFluidChunkDrawStep(location))); - return drawSteps; - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/render/UndergroundFluidRenderer.java b/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/render/UndergroundFluidRenderer.java deleted file mode 100644 index ae62ec92..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/render/UndergroundFluidRenderer.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.sinthoras.visualprospecting.integration.journeymap.render; - -import com.sinthoras.visualprospecting.integration.journeymap.drawsteps.UndergroundFluidDrawStep; -import com.sinthoras.visualprospecting.integration.model.layers.UndergroundFluidLayerManager; -import com.sinthoras.visualprospecting.integration.model.locations.ILocationProvider; -import com.sinthoras.visualprospecting.integration.model.locations.UndergroundFluidLocation; -import journeymap.client.render.draw.DrawStep; - -import java.util.ArrayList; -import java.util.List; - -public class UndergroundFluidRenderer extends LayerRenderer { - - public static final UndergroundFluidRenderer instance = new UndergroundFluidRenderer(); - - public UndergroundFluidRenderer() { - super(UndergroundFluidLayerManager.instance); - } - - @Override - public List mapLocationProviderToDrawStep(List visibleElements) { - final List drawSteps = new ArrayList<>(); - visibleElements.stream() - .map(element -> (UndergroundFluidLocation) element) - .forEach(location -> drawSteps.add(new UndergroundFluidDrawStep(location))); - return drawSteps; - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/render/WaypointProviderLayerRenderer.java b/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/render/WaypointProviderLayerRenderer.java deleted file mode 100644 index afce9311..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/render/WaypointProviderLayerRenderer.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.sinthoras.visualprospecting.integration.journeymap.render; - -import com.sinthoras.visualprospecting.integration.journeymap.drawsteps.ClickableDrawStep; -import com.sinthoras.visualprospecting.integration.model.layers.WaypointProviderManager; -import com.sinthoras.visualprospecting.integration.model.locations.ILocationProvider; -import net.minecraft.client.gui.FontRenderer; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -public abstract class WaypointProviderLayerRenderer extends LayerRenderer { - - private final WaypointProviderManager manager; - - private List drawSteps = new ArrayList<>(); - private List drawStepsReversed = new ArrayList<>(); - - private ClickableDrawStep hoveredDrawStep = null; - - public WaypointProviderLayerRenderer(WaypointProviderManager manager) { - super(manager); - this.manager = manager; - } - - @Override - public List getDrawStepsCachedForInteraction() { - return drawSteps; - } - - @Override - public List getDrawStepsCachedForRendering() { - return drawStepsReversed; - } - - @Override - public void updateVisibleElements(List visibleElements) { - drawSteps = (List) mapLocationProviderToDrawStep(visibleElements); - drawStepsReversed = new ArrayList<>(drawSteps); - Collections.reverse(drawStepsReversed); - } - - protected abstract List mapLocationProviderToDrawStep(List visibleElements); - - public void onMouseMove(int mouseX, int mouseY) { - hoveredDrawStep = null; - for (ClickableDrawStep drawStep : getDrawStepsCachedForInteraction()) { - if (drawStep.isMouseOver(mouseX, mouseY)) { - hoveredDrawStep = drawStep; - return; - } - } - } - - public boolean onMouseAction(boolean isDoubleClick) { - if(hoveredDrawStep != null) { - if(isDoubleClick) { - if(hoveredDrawStep.getLocationProvider().isActiveAsWaypoint()) { - manager.clearActiveWaypoint(); - } - else { - manager.setActiveWaypoint(hoveredDrawStep.getLocationProvider().toWaypoint()); - } - } - return true; - } - return false; - } - - public List getTextTooltip() { - if(hoveredDrawStep != null) { - return hoveredDrawStep.getTooltip(); - } - return null; - } - - public void drawCustomTooltip(FontRenderer fontRenderer, int mouseX, int mouseY, int displayWidth, int displayHeight) { - if(hoveredDrawStep != null) { - hoveredDrawStep.drawTooltip(fontRenderer, mouseX, mouseY, displayWidth, displayHeight); - } - } - - public void onActionKeyPressed() { - if(manager.isLayerActive() && hoveredDrawStep != null) { - hoveredDrawStep.onActionKeyPressed(); - manager.forceRefresh(); - } - } - - public boolean isLayerActive() { - return manager.isLayerActive(); - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/waypoints/OreVeinWaypointManager.java b/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/waypoints/OreVeinWaypointManager.java deleted file mode 100644 index 2c0ad2a2..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/waypoints/OreVeinWaypointManager.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.sinthoras.visualprospecting.integration.journeymap.waypoints; - -import com.sinthoras.visualprospecting.integration.model.layers.OreVeinLayerManager; - -public class OreVeinWaypointManager extends WaypointManager { - - public static final OreVeinWaypointManager instance = new OreVeinWaypointManager(); - - public OreVeinWaypointManager() { - super(OreVeinLayerManager.instance); - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/waypoints/ThaumcraftNodeWaypointManager.java b/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/waypoints/ThaumcraftNodeWaypointManager.java deleted file mode 100644 index c32a6f02..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/waypoints/ThaumcraftNodeWaypointManager.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.sinthoras.visualprospecting.integration.journeymap.waypoints; - -import com.sinthoras.visualprospecting.integration.model.layers.ThaumcraftNodeLayerManager; - -public class ThaumcraftNodeWaypointManager extends WaypointManager { - - public static final ThaumcraftNodeWaypointManager instance = new ThaumcraftNodeWaypointManager(); - - public ThaumcraftNodeWaypointManager() { - super(ThaumcraftNodeLayerManager.instance); - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/waypoints/WaypointManager.java b/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/waypoints/WaypointManager.java deleted file mode 100644 index 30f11513..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/journeymap/waypoints/WaypointManager.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.sinthoras.visualprospecting.integration.journeymap.waypoints; - -import com.sinthoras.visualprospecting.integration.model.SupportedMods; -import com.sinthoras.visualprospecting.integration.model.layers.WaypointProviderManager; -import com.sinthoras.visualprospecting.integration.model.waypoints.Waypoint; - -import java.awt.*; - -public class WaypointManager extends com.sinthoras.visualprospecting.integration.model.waypoints.WaypointManager { - - private journeymap.client.model.Waypoint jmWaypoint; - - public WaypointManager(WaypointProviderManager layerManager) { - super(layerManager, SupportedMods.JourneyMap); - } - - @Override - public void clearActiveWaypoint() { - jmWaypoint = null; - } - - public boolean hasWaypoint() { - return jmWaypoint != null; - } - - public journeymap.client.model.Waypoint getJmWaypoint() { - return jmWaypoint; - } - - @Override - public void updateActiveWaypoint(Waypoint waypoint) { - if(hasWaypoint() == false - || waypoint.blockX != jmWaypoint.getX() - || waypoint.blockY != jmWaypoint.getY() - || waypoint.blockZ != jmWaypoint.getZ() - || jmWaypoint.getDimensions().contains(waypoint.dimensionId) == false) { - jmWaypoint = new journeymap.client.model.Waypoint( - waypoint.label, - waypoint.blockX, waypoint.blockY, waypoint.blockZ, - new Color(waypoint.color), - journeymap.client.model.Waypoint.Type.Normal, - waypoint.dimensionId); - } - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/model/MapState.java b/src/main/java/com/sinthoras/visualprospecting/integration/model/MapState.java deleted file mode 100644 index a08fdd9c..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/model/MapState.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.sinthoras.visualprospecting.integration.model; - -import com.sinthoras.visualprospecting.integration.model.buttons.*; -import com.sinthoras.visualprospecting.integration.model.layers.*; -import com.sinthoras.visualprospecting.integration.tcnodetracker.NTNodeTrackerWaypointManager; - -import java.util.ArrayList; -import java.util.List; - -import static com.sinthoras.visualprospecting.Utils.isTCNodeTrackerInstalled; - -public class MapState { - public static final MapState instance = new MapState(); - - public final List buttons = new ArrayList<>(); - public final List layers = new ArrayList<>(); - - public MapState() { - if(isTCNodeTrackerInstalled()) { - buttons.add(ThaumcraftNodeButtonManager.instance); - layers.add(ThaumcraftNodeLayerManager.instance); - new NTNodeTrackerWaypointManager(); - } - - buttons.add(UndergroundFluidButtonManager.instance); - layers.add(UndergroundFluidLayerManager.instance); - layers.add(UndergroundFluidChunkLayerManager.instance); - - buttons.add(OreVeinButtonManager.instance); - layers.add(OreVeinLayerManager.instance); - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/model/SupportedMods.java b/src/main/java/com/sinthoras/visualprospecting/integration/model/SupportedMods.java deleted file mode 100644 index 551b6719..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/model/SupportedMods.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.sinthoras.visualprospecting.integration.model; - -public enum SupportedMods { - TCNodeTracker, - JourneyMap, - XaeroWorldMap, - XaeroMiniMap; - - // TODO: Open Map at here -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/model/buttons/ButtonManager.java b/src/main/java/com/sinthoras/visualprospecting/integration/model/buttons/ButtonManager.java deleted file mode 100644 index 9541e3d2..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/model/buttons/ButtonManager.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.sinthoras.visualprospecting.integration.model.buttons; - -import com.sinthoras.visualprospecting.integration.model.MapState; -import com.sinthoras.visualprospecting.integration.model.SupportedMods; - -import java.util.EnumMap; -import java.util.Map; - -public class ButtonManager { - - private final String buttonTextKey; - private final String iconName; - - private Map buttons = new EnumMap<>(SupportedMods.class); - private boolean isActive = false; - - /* - Provide textures in assets/journeymap/icon/theme/Vault/icon/.png - and assets/journeymap/icon/theme/Victorian/icon/.png for JourneyMap - and provide a texture in assets/xaeroworldmap/textures/.png for XaeroWorldMap. - */ - public ButtonManager(String buttonTextKey, String iconName) { - this.buttonTextKey = buttonTextKey; - this.iconName = iconName; - } - - public void registerButton(SupportedMods map, LayerButton layerButton) { - buttons.put(map, layerButton); - } - - public void updateState(boolean active) { - buttons.values().forEach(button -> button.updateState(active)); - } - - public boolean containsButton(LayerButton button) { - return buttons.containsValue(button); - } - - public String getButtonTextKey() { - return buttonTextKey; - } - - public String getIconName() { - return iconName; - } - - public boolean isActive() { - return isActive; - } - - public void activate() { - MapState.instance.buttons.forEach(ButtonManager::deactivate); - isActive = true; - updateState(true); - } - - public void deactivate() { - isActive = false; - updateState(false); - } - - public void toggle() { - if(isActive) { - deactivate(); - } - else { - activate(); - } - } -} \ No newline at end of file diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/model/buttons/LayerButton.java b/src/main/java/com/sinthoras/visualprospecting/integration/model/buttons/LayerButton.java deleted file mode 100644 index 0845cf0d..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/model/buttons/LayerButton.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.sinthoras.visualprospecting.integration.model.buttons; - -import com.sinthoras.visualprospecting.integration.model.SupportedMods; - -public abstract class LayerButton { - - public LayerButton(ButtonManager manager, SupportedMods map) { - manager.registerButton(map, this); - // Grab lang key and texture information from manager in extended constructor - } - - public abstract void updateState(boolean active); -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/model/buttons/OreVeinButtonManager.java b/src/main/java/com/sinthoras/visualprospecting/integration/model/buttons/OreVeinButtonManager.java index 5ab7d819..537ad4f8 100644 --- a/src/main/java/com/sinthoras/visualprospecting/integration/model/buttons/OreVeinButtonManager.java +++ b/src/main/java/com/sinthoras/visualprospecting/integration/model/buttons/OreVeinButtonManager.java @@ -1,10 +1,23 @@ package com.sinthoras.visualprospecting.integration.model.buttons; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.StatCollector; + +import com.gtnewhorizons.navigator.api.model.SupportedMods; +import com.gtnewhorizons.navigator.api.model.buttons.ButtonManager; +import com.sinthoras.visualprospecting.Tags; + public class OreVeinButtonManager extends ButtonManager { public static final OreVeinButtonManager instance = new OreVeinButtonManager(); - public OreVeinButtonManager() { - super("visualprospecting.button.orevein", "oreveins"); + @Override + public ResourceLocation getIcon(SupportedMods mod, String theme) { + return new ResourceLocation(Tags.MODID, "textures/icons/oreveins.png"); + } + + @Override + public String getButtonText() { + return StatCollector.translateToLocal("visualprospecting.button.orevein"); } } diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/model/buttons/ThaumcraftNodeButtonManager.java b/src/main/java/com/sinthoras/visualprospecting/integration/model/buttons/ThaumcraftNodeButtonManager.java deleted file mode 100644 index b2da7182..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/model/buttons/ThaumcraftNodeButtonManager.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.sinthoras.visualprospecting.integration.model.buttons; - -public class ThaumcraftNodeButtonManager extends ButtonManager { - - public static final ThaumcraftNodeButtonManager instance = new ThaumcraftNodeButtonManager(); - - public ThaumcraftNodeButtonManager() { - super("visualprospecting.button.nodes", "nodes"); - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/model/buttons/UndergroundFluidButtonManager.java b/src/main/java/com/sinthoras/visualprospecting/integration/model/buttons/UndergroundFluidButtonManager.java index 7c7836d5..5a566c2c 100644 --- a/src/main/java/com/sinthoras/visualprospecting/integration/model/buttons/UndergroundFluidButtonManager.java +++ b/src/main/java/com/sinthoras/visualprospecting/integration/model/buttons/UndergroundFluidButtonManager.java @@ -1,10 +1,23 @@ package com.sinthoras.visualprospecting.integration.model.buttons; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.StatCollector; + +import com.gtnewhorizons.navigator.api.model.SupportedMods; +import com.gtnewhorizons.navigator.api.model.buttons.ButtonManager; +import com.sinthoras.visualprospecting.Tags; + public class UndergroundFluidButtonManager extends ButtonManager { public static final UndergroundFluidButtonManager instance = new UndergroundFluidButtonManager(); - public UndergroundFluidButtonManager() { - super("visualprospecting.button.undergroundfluid", "undergroundfluid"); + @Override + public ResourceLocation getIcon(SupportedMods mod, String theme) { + return new ResourceLocation(Tags.MODID, "textures/icons/undergroundfluid.png"); + } + + @Override + public String getButtonText() { + return StatCollector.translateToLocal("visualprospecting.button.undergroundfluid"); } } diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/model/layers/LayerManager.java b/src/main/java/com/sinthoras/visualprospecting/integration/model/layers/LayerManager.java deleted file mode 100644 index fb16726e..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/model/layers/LayerManager.java +++ /dev/null @@ -1,109 +0,0 @@ -package com.sinthoras.visualprospecting.integration.model.layers; - -import com.sinthoras.visualprospecting.integration.model.SupportedMods; -import com.sinthoras.visualprospecting.integration.model.buttons.ButtonManager; -import com.sinthoras.visualprospecting.integration.model.buttons.LayerButton; -import com.sinthoras.visualprospecting.integration.model.locations.ILocationProvider; - -import java.util.ArrayList; -import java.util.EnumMap; -import java.util.List; -import java.util.Map; - -public abstract class LayerManager { - - private final ButtonManager buttonManager; - - private boolean isLayerActive = false; - protected boolean forceRefresh = false; - private List visibleElements = new ArrayList<>(); - protected Map layerRenderer = new EnumMap<>(SupportedMods.class); - private int miniMapWidth = 0; - private int miniMapHeight = 0; - private int fullscreenMapWidth = 0; - private int fullscreenMapHeight = 0; - - public LayerManager(ButtonManager buttonManager) { - this.buttonManager = buttonManager; - } - - public void onButtonClicked(LayerButton button) { - if(buttonManager.containsButton(button)) { - toggleLayer(); - } - } - - public boolean isLayerActive() { - return buttonManager.isActive(); - } - - public void activateLayer() { - buttonManager.activate(); - } - - public void deactivateLayer() { - buttonManager.deactivate(); - } - - public void toggleLayer() { - buttonManager.toggle(); - } - - public void forceRefresh() { - forceRefresh = true; - } - - public void onOpenMap() { - - } - - protected abstract List generateVisibleElements(int minBlockX, int minBlockZ, int maxBlockX, int maxBlockZ); - - protected boolean needsRegenerateVisibleElements(int minBlockX, int minBlockZ, int maxBlockX, int maxBlockZ) { - return true; - } - - public void recacheMiniMap(int centerBlockX, int centerBlockZ, int blockRadius) { - recacheMiniMap(centerBlockX, centerBlockZ, blockRadius, blockRadius); - } - - public void recacheMiniMap(int centerBlockX, int centerBlockZ, int blockWidth, int blockHeight) { - miniMapWidth = blockWidth; - miniMapHeight = blockHeight; - recacheVisibleElements(centerBlockX, centerBlockZ); - } - - public void recacheFullscreenMap(int centerBlockX, int centerBlockZ, int blockWidth, int blockHeight) { - fullscreenMapWidth = blockWidth; - fullscreenMapHeight = blockHeight; - recacheVisibleElements(centerBlockX, centerBlockZ); - } - - private void recacheVisibleElements(int centerBlockX, int centerBlockZ) { - final int radiusBlockX = (Math.max(miniMapWidth, fullscreenMapWidth) + 1) >> 1; - final int radiusBlockZ = (Math.max(miniMapHeight, fullscreenMapHeight) + 1) >> 1; - - final int minBlockX = centerBlockX - radiusBlockX; - final int minBlockZ = centerBlockZ - radiusBlockZ; - final int maxBlockX = centerBlockX + radiusBlockX; - final int maxBlockZ = centerBlockZ + radiusBlockZ; - - checkAndUpdateElements(minBlockX, minBlockZ, maxBlockX, maxBlockZ); - } - - protected void checkAndUpdateElements(int minBlockX, int minBlockZ, int maxBlockX, int maxBlockZ) { - if(forceRefresh || needsRegenerateVisibleElements(minBlockX, minBlockZ, maxBlockX, maxBlockZ)) { - visibleElements = generateVisibleElements(minBlockX, minBlockZ, maxBlockX, maxBlockZ); - layerRenderer.values().forEach(layer -> layer.updateVisibleElements(visibleElements)); - forceRefresh = false; - } - } - - public void registerLayerRenderer(SupportedMods map, LayerRenderer renderer) { - layerRenderer.put(map, renderer); - } - - public LayerRenderer getLayerRenderer(SupportedMods map) { - return layerRenderer.get(map); - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/model/layers/LayerRenderer.java b/src/main/java/com/sinthoras/visualprospecting/integration/model/layers/LayerRenderer.java deleted file mode 100644 index 83baa8be..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/model/layers/LayerRenderer.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.sinthoras.visualprospecting.integration.model.layers; - -import com.sinthoras.visualprospecting.integration.model.SupportedMods; -import com.sinthoras.visualprospecting.integration.model.locations.ILocationProvider; - -import java.util.List; - -public abstract class LayerRenderer { - - public LayerRenderer(LayerManager manager, SupportedMods map) { - manager.registerLayerRenderer(map, this); - } - - public abstract void updateVisibleElements(List visibleElements); -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/model/layers/OreVeinLayerManager.java b/src/main/java/com/sinthoras/visualprospecting/integration/model/layers/OreVeinLayerManager.java index fc044f12..762002c3 100644 --- a/src/main/java/com/sinthoras/visualprospecting/integration/model/layers/OreVeinLayerManager.java +++ b/src/main/java/com/sinthoras/visualprospecting/integration/model/layers/OreVeinLayerManager.java @@ -1,72 +1,81 @@ package com.sinthoras.visualprospecting.integration.model.layers; +import java.util.ArrayList; +import java.util.Collection; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import com.gtnewhorizons.navigator.api.journeymap.waypoints.JMWaypointManager; +import com.gtnewhorizons.navigator.api.model.SupportedMods; +import com.gtnewhorizons.navigator.api.model.layers.InteractableLayerManager; +import com.gtnewhorizons.navigator.api.model.layers.LayerRenderer; +import com.gtnewhorizons.navigator.api.model.layers.UniversalInteractableRenderer; +import com.gtnewhorizons.navigator.api.model.locations.ILocationProvider; +import com.gtnewhorizons.navigator.api.model.waypoints.WaypointManager; +import com.gtnewhorizons.navigator.api.xaero.waypoints.XaeroWaypointManager; import com.sinthoras.visualprospecting.Utils; import com.sinthoras.visualprospecting.database.ClientCache; import com.sinthoras.visualprospecting.database.OreVeinPosition; import com.sinthoras.visualprospecting.database.veintypes.VeinType; import com.sinthoras.visualprospecting.database.veintypes.VeinTypeCaching; import com.sinthoras.visualprospecting.integration.model.buttons.OreVeinButtonManager; -import com.sinthoras.visualprospecting.integration.model.locations.IWaypointAndLocationProvider; import com.sinthoras.visualprospecting.integration.model.locations.OreVeinLocation; -import net.minecraft.client.Minecraft; +import com.sinthoras.visualprospecting.integration.model.render.OreVeinMapMarker; +import com.sinthoras.visualprospecting.integration.model.render.OreVeinRenderStep; -import java.util.ArrayList; -import java.util.List; - -public class OreVeinLayerManager extends WaypointProviderManager { +public class OreVeinLayerManager extends InteractableLayerManager { public static final OreVeinLayerManager instance = new OreVeinLayerManager(); - private int oldMinOreChunkX = 0; - private int oldMaxOreChunkX = 0; - private int oldMinOreChunkZ = 0; - private int oldMaxOreChunkZ = 0; - public OreVeinLayerManager() { super(OreVeinButtonManager.instance); + setHasSearchField(true); } @Override public void onOpenMap() { - VeinTypeCaching.recalculateNEISearch(); + VeinTypeCaching.recalculateSearch(Utils.getNEISearchPattern(), Utils.getNEISearchItemFilter()); } + @Nullable @Override - protected boolean needsRegenerateVisibleElements(int minBlockX, int minBlockZ, int maxBlockX, int maxBlockZ) { - final int minOreChunkX = Utils.mapToCenterOreChunkCoord(Utils.coordBlockToChunk(minBlockX)); - final int minOreChunkZ = Utils.mapToCenterOreChunkCoord(Utils.coordBlockToChunk(minBlockZ)); - final int maxOreChunkX = Utils.mapToCenterOreChunkCoord(Utils.coordBlockToChunk(maxBlockX)); - final int maxOreChunkZ = Utils.mapToCenterOreChunkCoord(Utils.coordBlockToChunk(maxBlockZ)); - - if (minOreChunkX != oldMinOreChunkX || maxOreChunkX != oldMaxOreChunkX || minOreChunkZ != oldMinOreChunkZ || maxOreChunkZ != oldMaxOreChunkZ) { - oldMinOreChunkX = minOreChunkX; - oldMaxOreChunkX = maxOreChunkX; - oldMinOreChunkZ = minOreChunkZ; - oldMaxOreChunkZ = maxOreChunkZ; - return true; - } - return false; + protected LayerRenderer addLayerRenderer(InteractableLayerManager manager, SupportedMods mod) { + return new UniversalInteractableRenderer(manager) + .withRenderStep(location -> new OreVeinRenderStep((OreVeinLocation) location)) + .withMapMarker(location -> OreVeinMapMarker.create((OreVeinLocation) location)); } + @Nullable @Override - protected List generateVisibleElements(int minBlockX, int minBlockZ, int maxBlockX, int maxBlockZ) { - final int minOreChunkX = Utils.mapToCenterOreChunkCoord(Utils.coordBlockToChunk(minBlockX)); - final int minOreChunkZ = Utils.mapToCenterOreChunkCoord(Utils.coordBlockToChunk(minBlockZ)); - final int maxOreChunkX = Utils.mapToCenterOreChunkCoord(Utils.coordBlockToChunk(maxBlockX)); - final int maxOreChunkZ = Utils.mapToCenterOreChunkCoord(Utils.coordBlockToChunk(maxBlockZ)); - final int playerDimensionId = Minecraft.getMinecraft().thePlayer.dimension; - - ArrayList oreChunkLocations = new ArrayList<>(); + protected WaypointManager addWaypointManager(InteractableLayerManager manager, SupportedMods mod) { + return switch (mod) { + case JourneyMap -> new JMWaypointManager(manager); + case XaeroWorldMap -> new XaeroWaypointManager(manager, "!"); + default -> null; + }; + } - for (int chunkX = minOreChunkX; chunkX <= maxOreChunkX; chunkX = Utils.mapToCenterOreChunkCoord(chunkX + 3)) { - for (int chunkZ = minOreChunkZ; chunkZ <= maxOreChunkZ; chunkZ = Utils.mapToCenterOreChunkCoord(chunkZ + 3)) { - final OreVeinPosition oreVeinPosition = ClientCache.instance.getOreVein(playerDimensionId, chunkX, chunkZ); - if (oreVeinPosition.veinType != VeinType.NO_VEIN) { - oreChunkLocations.add(new OreVeinLocation(oreVeinPosition)); - } + @Override + protected Collection generateVisibleLocations(int minBlockX, int minBlockZ, + int maxBlockX, int maxBlockZ, int dimension) { + Collection locations = new ArrayList<>(); + for (OreVeinPosition vein : ClientCache.instance.getAllOreVeins()) { + int blockX = vein.getBlockX(); + int blockZ = vein.getBlockZ(); + if (vein.veinType != VeinType.NO_VEIN && vein.dimensionId == dimension + && blockX >= minBlockX + && blockX <= maxBlockX + && blockZ >= minBlockZ + && blockZ <= maxBlockZ) { + locations.add(new OreVeinLocation(vein)); } } + return locations; + } - return oreChunkLocations; + @Override + public void onSearch(@NotNull String searchString) { + VeinTypeCaching.recalculateSearch(Utils.getSearchPattern(searchString), Utils.getItemFilter(searchString)); } } diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/model/layers/ThaumcraftNodeLayerManager.java b/src/main/java/com/sinthoras/visualprospecting/integration/model/layers/ThaumcraftNodeLayerManager.java deleted file mode 100644 index 3af95cc2..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/model/layers/ThaumcraftNodeLayerManager.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.sinthoras.visualprospecting.integration.model.layers; - -import com.dyonovan.tcnodetracker.TCNodeTracker; -import com.dyonovan.tcnodetracker.lib.NodeList; -import com.sinthoras.visualprospecting.integration.model.buttons.ThaumcraftNodeButtonManager; -import com.sinthoras.visualprospecting.integration.model.locations.IWaypointAndLocationProvider; -import com.sinthoras.visualprospecting.integration.model.locations.ThaumcraftNodeLocation; -import net.minecraft.client.Minecraft; - -import java.util.ArrayList; -import java.util.List; - -public class ThaumcraftNodeLayerManager extends WaypointProviderManager { - - public static final ThaumcraftNodeLayerManager instance = new ThaumcraftNodeLayerManager(); - - private int oldMinBlockX = 0; - private int oldMinBlockZ = 0; - private int oldMaxBlockX = 0; - private int oldMaxBlockZ = 0; - - public ThaumcraftNodeLayerManager() { - super(ThaumcraftNodeButtonManager.instance); - } - - @Override - protected boolean needsRegenerateVisibleElements(int minBlockX, int minBlockZ, int maxBlockX, int maxBlockZ) { - if(minBlockX != oldMinBlockX || minBlockZ != oldMinBlockZ || maxBlockX != oldMaxBlockX || maxBlockZ != oldMaxBlockZ) { - oldMinBlockX = minBlockX; - oldMinBlockZ = minBlockZ; - oldMaxBlockX = maxBlockX; - oldMaxBlockZ = maxBlockZ; - return true; - } - return false; - } - - @Override - protected List generateVisibleElements(int minBlockX, int minBlockZ, int maxBlockX, int maxBlockZ) { - final int playerDimensionId = Minecraft.getMinecraft().thePlayer.dimension; - - ArrayList thaumcraftNodeLocations = new ArrayList<>(); - - for (NodeList node : TCNodeTracker.nodelist) { - if(node.dim == playerDimensionId - && node.x >= minBlockX && node.x <= maxBlockX - && node.z >= minBlockZ && node.z <= maxBlockZ) { - thaumcraftNodeLocations.add(new ThaumcraftNodeLocation(node)); - } - } - - return thaumcraftNodeLocations; - } - - public void deleteNode(ThaumcraftNodeLocation thaumcraftNodeLocation) { - TCNodeTracker.nodelist.removeIf(thaumcraftNodeLocation::belongsToNode); - if(thaumcraftNodeLocation.isActiveAsWaypoint()) { - clearActiveWaypoint(); - } - forceRefresh(); - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/model/layers/UndergroundFluidChunkLayerManager.java b/src/main/java/com/sinthoras/visualprospecting/integration/model/layers/UndergroundFluidChunkLayerManager.java deleted file mode 100644 index 0581b75d..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/model/layers/UndergroundFluidChunkLayerManager.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.sinthoras.visualprospecting.integration.model.layers; - -import com.sinthoras.visualprospecting.Utils; -import com.sinthoras.visualprospecting.VP; -import com.sinthoras.visualprospecting.database.ClientCache; -import com.sinthoras.visualprospecting.database.UndergroundFluidPosition; -import com.sinthoras.visualprospecting.integration.model.buttons.UndergroundFluidButtonManager; -import com.sinthoras.visualprospecting.integration.model.locations.ILocationProvider; -import com.sinthoras.visualprospecting.integration.model.locations.UndergroundFluidChunkLocation; -import net.minecraft.client.Minecraft; - -import java.util.ArrayList; -import java.util.List; - -public class UndergroundFluidChunkLayerManager extends LayerManager { - - public static final UndergroundFluidChunkLayerManager instance = new UndergroundFluidChunkLayerManager(); - - private int oldMinUndergroundFluidX = 0; - private int oldMaxUndergroundFluidX = 0; - private int oldMinUndergroundFluidZ = 0; - private int oldMaxUndergroundFluidZ = 0; - - public UndergroundFluidChunkLayerManager() { - super(UndergroundFluidButtonManager.instance); - } - - @Override - protected boolean needsRegenerateVisibleElements(int minBlockX, int minBlockZ, int maxBlockX, int maxBlockZ) { - final int minUndergroundFluidX = Utils.mapToCornerUndergroundFluidChunkCoord(Utils.coordBlockToChunk(minBlockX)); - final int minUndergroundFluidZ = Utils.mapToCornerUndergroundFluidChunkCoord(Utils.coordBlockToChunk(minBlockZ)); - final int maxUndergroundFluidX = Utils.mapToCornerUndergroundFluidChunkCoord(Utils.coordBlockToChunk(maxBlockX)); - final int maxUndergroundFluidZ = Utils.mapToCornerUndergroundFluidChunkCoord(Utils.coordBlockToChunk(maxBlockZ)); - if (minUndergroundFluidX != oldMinUndergroundFluidX || maxUndergroundFluidX != oldMaxUndergroundFluidX || minUndergroundFluidZ != oldMinUndergroundFluidZ || maxUndergroundFluidZ != oldMaxUndergroundFluidZ) { - oldMinUndergroundFluidX = minUndergroundFluidX; - oldMaxUndergroundFluidX = maxUndergroundFluidX; - oldMinUndergroundFluidZ = minUndergroundFluidZ; - oldMaxUndergroundFluidZ = maxUndergroundFluidZ; - return true; - } - return false; - } - - @Override - protected List generateVisibleElements(int minBlockX, int minBlockZ, int maxBlockX, int maxBlockZ) { - final int minUndergroundFluidX = Utils.mapToCornerUndergroundFluidChunkCoord(Utils.coordBlockToChunk(minBlockX)); - final int minUndergroundFluidZ = Utils.mapToCornerUndergroundFluidChunkCoord(Utils.coordBlockToChunk(minBlockZ)); - final int maxUndergroundFluidX = Utils.mapToCornerUndergroundFluidChunkCoord(Utils.coordBlockToChunk(maxBlockX)); - final int maxUndergroundFluidZ = Utils.mapToCornerUndergroundFluidChunkCoord(Utils.coordBlockToChunk(maxBlockZ)); - final int playerDimensionId = Minecraft.getMinecraft().thePlayer.dimension; - - ArrayList undergroundFluidPositions = new ArrayList<>(); - - for (int chunkX = minUndergroundFluidX; chunkX <= maxUndergroundFluidX; chunkX += VP.undergroundFluidSizeChunkX) { - for (int chunkZ = minUndergroundFluidZ; chunkZ <= maxUndergroundFluidZ; chunkZ += VP.undergroundFluidSizeChunkZ) { - final UndergroundFluidPosition undergroundFluid = ClientCache.instance.getUndergroundFluid(playerDimensionId, chunkX, chunkZ); - if (undergroundFluid.isProspected()) { - final int minAmountInField = undergroundFluid.getMinProduction(); - final int maxAmountInField = undergroundFluid.getMaxProduction(); - for (int offsetChunkX = 0; offsetChunkX < VP.undergroundFluidSizeChunkX; offsetChunkX++) { - for (int offsetChunkZ = 0; offsetChunkZ < VP.undergroundFluidSizeChunkZ; offsetChunkZ++) { - undergroundFluidPositions.add(new UndergroundFluidChunkLocation(chunkX + offsetChunkX, chunkZ + offsetChunkZ, playerDimensionId, undergroundFluid.fluid, undergroundFluid.chunks[offsetChunkX][offsetChunkZ], minAmountInField, maxAmountInField)); - } - } - } - } - } - - return undergroundFluidPositions; - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/model/layers/UndergroundFluidLayerManager.java b/src/main/java/com/sinthoras/visualprospecting/integration/model/layers/UndergroundFluidLayerManager.java index dee182af..7669e17a 100644 --- a/src/main/java/com/sinthoras/visualprospecting/integration/model/layers/UndergroundFluidLayerManager.java +++ b/src/main/java/com/sinthoras/visualprospecting/integration/model/layers/UndergroundFluidLayerManager.java @@ -1,65 +1,127 @@ package com.sinthoras.visualprospecting.integration.model.layers; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidRegistry; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import com.gtnewhorizons.navigator.api.model.SupportedMods; +import com.gtnewhorizons.navigator.api.model.layers.LayerManager; +import com.gtnewhorizons.navigator.api.model.layers.LayerRenderer; +import com.gtnewhorizons.navigator.api.model.layers.UniversalLayerRenderer; +import com.gtnewhorizons.navigator.api.model.locations.ILocationProvider; +import com.gtnewhorizons.navigator.api.util.Util; import com.sinthoras.visualprospecting.Utils; -import com.sinthoras.visualprospecting.VP; import com.sinthoras.visualprospecting.database.ClientCache; import com.sinthoras.visualprospecting.database.UndergroundFluidPosition; import com.sinthoras.visualprospecting.integration.model.buttons.UndergroundFluidButtonManager; -import com.sinthoras.visualprospecting.integration.model.locations.ILocationProvider; import com.sinthoras.visualprospecting.integration.model.locations.UndergroundFluidLocation; -import net.minecraft.client.Minecraft; +import com.sinthoras.visualprospecting.integration.model.render.UndergroundFluidImageOverlay; +import com.sinthoras.visualprospecting.integration.model.render.UndergroundFluidRenderStep; -import java.util.ArrayList; -import java.util.List; +import gregtech.api.enums.UndergroundFluidNames; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import it.unimi.dsi.fastutil.objects.ObjectSet; public class UndergroundFluidLayerManager extends LayerManager { public static final UndergroundFluidLayerManager instance = new UndergroundFluidLayerManager(); - - private int oldMinUndergroundFluidX = 0; - private int oldMaxUndergroundFluidX = 0; - private int oldMinUndergroundFluidZ = 0; - private int oldMaxUndergroundFluidZ = 0; + private static final ObjectSet highlightedFluids = new ObjectOpenHashSet<>(); public UndergroundFluidLayerManager() { super(UndergroundFluidButtonManager.instance); + setHasSearchField(true); } + @Nullable @Override - protected boolean needsRegenerateVisibleElements(int minBlockX, int minBlockZ, int maxBlockX, int maxBlockZ) { - final int minUndergroundFluidX = Utils.mapToCornerUndergroundFluidChunkCoord(Utils.coordBlockToChunk(minBlockX)); - final int minUndergroundFluidZ = Utils.mapToCornerUndergroundFluidChunkCoord(Utils.coordBlockToChunk(minBlockZ)); - final int maxUndergroundFluidX = Utils.mapToCornerUndergroundFluidChunkCoord(Utils.coordBlockToChunk(maxBlockX)); - final int maxUndergroundFluidZ = Utils.mapToCornerUndergroundFluidChunkCoord(Utils.coordBlockToChunk(maxBlockZ)); - if (minUndergroundFluidX != oldMinUndergroundFluidX || maxUndergroundFluidX != oldMaxUndergroundFluidX || minUndergroundFluidZ != oldMinUndergroundFluidZ || maxUndergroundFluidZ != oldMaxUndergroundFluidZ) { - oldMinUndergroundFluidX = minUndergroundFluidX; - oldMaxUndergroundFluidX = maxUndergroundFluidX; - oldMinUndergroundFluidZ = minUndergroundFluidZ; - oldMaxUndergroundFluidZ = maxUndergroundFluidZ; - return true; + protected LayerRenderer addLayerRenderer(LayerManager manager, SupportedMods mod) { + UniversalLayerRenderer renderer = new UniversalLayerRenderer(manager) + .withRenderStep(location -> new UndergroundFluidRenderStep((UndergroundFluidLocation) location)); + if (Util.isJourneyMapV6Installed()) { + renderer.withJourneyMapV6Overlays( + location -> UndergroundFluidImageOverlay.create((UndergroundFluidLocation) location)); } - return false; + return renderer; } @Override - protected List generateVisibleElements(int minBlockX, int minBlockZ, int maxBlockX, int maxBlockZ) { - final int minUndergroundFluidX = Utils.mapToCornerUndergroundFluidChunkCoord(Utils.coordBlockToChunk(minBlockX)); - final int minUndergroundFluidZ = Utils.mapToCornerUndergroundFluidChunkCoord(Utils.coordBlockToChunk(minBlockZ)); - final int maxUndergroundFluidX = Utils.mapToCornerUndergroundFluidChunkCoord(Utils.coordBlockToChunk(maxBlockX)); - final int maxUndergroundFluidZ = Utils.mapToCornerUndergroundFluidChunkCoord(Utils.coordBlockToChunk(maxBlockZ)); - final int playerDimensionId = Minecraft.getMinecraft().thePlayer.dimension; - - ArrayList undergroundFluidLocations = new ArrayList<>(); - - for (int chunkX = minUndergroundFluidX; chunkX <= maxUndergroundFluidX; chunkX += VP.undergroundFluidSizeChunkX) { - for (int chunkZ = minUndergroundFluidZ; chunkZ <= maxUndergroundFluidZ; chunkZ += VP.undergroundFluidSizeChunkZ) { - final UndergroundFluidPosition undergroundFluid = ClientCache.instance.getUndergroundFluid(playerDimensionId, chunkX, chunkZ); - if (undergroundFluid.isProspected()) { - undergroundFluidLocations.add(new UndergroundFluidLocation(undergroundFluid)); + public void onOpenMap() { + highlightedFluids.clear(); + computeSearch(Utils.getNEISearchPattern()); + } + + private static List getFluidNames(Fluid fluid) { + List names = new ArrayList<>(); + names.add(fluid.getLocalizedName()); + names.add(fluid.getUnlocalizedName()); + names.add(fluid.getName()); + return names; + } + + @Override + public void updateElement(ILocationProvider location) { + UndergroundFluidLocation fluidLocation = (UndergroundFluidLocation) location; + fluidLocation.setActive(true); + if (isSearchActive()) { + boolean highlighted = highlightedFluids.contains(fluidLocation.getFluid()) + && fluidLocation.getMaxProduction() > 0; + fluidLocation.setActive(highlighted); + } + } + + @Override + protected List generateVisibleLocations(int minBlockX, int minBlockZ, int maxBlockX, + int maxBlockZ, int dimension) { + List locations = new ArrayList<>(); + for (UndergroundFluidPosition fluid : ClientCache.instance.getAllUndergroundFluids()) { + int blockX = fluid.getBlockX(); + int blockZ = fluid.getBlockZ(); + if (fluid.isProspected() && fluid.dimensionId == dimension + && blockX >= minBlockX + && blockX <= maxBlockX + && blockZ >= minBlockZ + && blockZ <= maxBlockZ) { + locations.add(new UndergroundFluidLocation(fluid)); + } + } + return locations; + } + + private void computeSearch(@Nullable Pattern filterPattern) { + if (filterPattern != null) { + for (UndergroundFluidNames fluidName : UndergroundFluidNames.values()) { + Fluid fluid = FluidRegistry.getFluid(fluidName.name); + if (fluid == null) continue; + for (String name : getFluidNames(fluid)) { + if (name != null && filterPattern.matcher(name.toLowerCase()).find()) { + highlightedFluids.add(fluid); + } } } } + } + + @Override + public void onSearch(@NotNull String searchString) { + if (searchString.isEmpty()) { + highlightedFluids.clear(); + } else { + computeSearch(Utils.getSearchPattern(searchString)); + } + } + + @Override + public int getElementSize() { + return 8; + } - return undergroundFluidLocations; + public boolean isSearchActive() { + return !highlightedFluids.isEmpty(); } } diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/model/layers/WaypointProviderManager.java b/src/main/java/com/sinthoras/visualprospecting/integration/model/layers/WaypointProviderManager.java deleted file mode 100644 index 94f55daf..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/model/layers/WaypointProviderManager.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.sinthoras.visualprospecting.integration.model.layers; - -import com.sinthoras.visualprospecting.integration.model.SupportedMods; -import com.sinthoras.visualprospecting.integration.model.waypoints.Waypoint; -import com.sinthoras.visualprospecting.integration.model.buttons.ButtonManager; -import com.sinthoras.visualprospecting.integration.model.locations.IWaypointAndLocationProvider; -import com.sinthoras.visualprospecting.integration.model.waypoints.WaypointManager; - -import java.util.ArrayList; -import java.util.EnumMap; -import java.util.List; -import java.util.Map; - -public abstract class WaypointProviderManager extends LayerManager { - - private List visibleElements = new ArrayList<>(); - private Map waypointManagers = new EnumMap<>(SupportedMods.class); - - protected Waypoint activeWaypoint = null; - - public WaypointProviderManager(ButtonManager buttonManager) { - super(buttonManager); - } - - public void setActiveWaypoint(Waypoint waypoint) { - activeWaypoint = waypoint; - visibleElements.forEach(element -> element.onWaypointUpdated(waypoint)); - waypointManagers.values().forEach(translator -> translator.updateActiveWaypoint(waypoint)); - } - - public void clearActiveWaypoint() { - activeWaypoint = null; - visibleElements.forEach(IWaypointAndLocationProvider::onWaypointCleared); - waypointManagers.values().forEach(WaypointManager::clearActiveWaypoint); - } - - public boolean hasActiveWaypoint() { - return activeWaypoint != null; - } - - public void registerWaypointManager(SupportedMods map, WaypointManager waypointManager) { - waypointManagers.put(map, waypointManager); - } - - public WaypointManager getWaypointManager(SupportedMods map) { - return waypointManagers.get(map); - } - - protected abstract List generateVisibleElements(int minBlockX, int minBlockZ, int maxBlockX, int maxBlockZ); - - @Override - protected void checkAndUpdateElements(int minBlockX, int minBlockZ, int maxBlockX, int maxBlockZ) { - if(forceRefresh || needsRegenerateVisibleElements(minBlockX, minBlockZ, maxBlockX, maxBlockZ)) { - visibleElements = generateVisibleElements(minBlockX, minBlockZ, maxBlockX, maxBlockZ); - - if(hasActiveWaypoint()) { - for (IWaypointAndLocationProvider element : visibleElements) { - element.onWaypointUpdated(activeWaypoint); - } - } - - layerRenderer.values().forEach(layer -> layer.updateVisibleElements(visibleElements)); - forceRefresh = false; - } - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/model/locations/ILocationProvider.java b/src/main/java/com/sinthoras/visualprospecting/integration/model/locations/ILocationProvider.java deleted file mode 100644 index d069df51..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/model/locations/ILocationProvider.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.sinthoras.visualprospecting.integration.model.locations; - -public interface ILocationProvider { - - int getDimensionId(); - - double getBlockX(); - - double getBlockZ(); -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/model/locations/IWaypointAndLocationProvider.java b/src/main/java/com/sinthoras/visualprospecting/integration/model/locations/IWaypointAndLocationProvider.java deleted file mode 100644 index e8a2d998..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/model/locations/IWaypointAndLocationProvider.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.sinthoras.visualprospecting.integration.model.locations; - -import com.sinthoras.visualprospecting.integration.model.waypoints.Waypoint; - -public interface IWaypointAndLocationProvider extends ILocationProvider { - - Waypoint toWaypoint(); - - boolean isActiveAsWaypoint(); - - void onWaypointCleared(); - - void onWaypointUpdated(Waypoint waypoint); -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/model/locations/OreVeinLocation.java b/src/main/java/com/sinthoras/visualprospecting/integration/model/locations/OreVeinLocation.java index 0c290555..54587f01 100644 --- a/src/main/java/com/sinthoras/visualprospecting/integration/model/locations/OreVeinLocation.java +++ b/src/main/java/com/sinthoras/visualprospecting/integration/model/locations/OreVeinLocation.java @@ -1,39 +1,60 @@ package com.sinthoras.visualprospecting.integration.model.locations; -import com.sinthoras.visualprospecting.VP; -import com.sinthoras.visualprospecting.database.ClientCache; -import com.sinthoras.visualprospecting.database.OreVeinPosition; -import com.sinthoras.visualprospecting.integration.model.waypoints.Waypoint; +import java.util.List; +import java.util.stream.Collectors; + import net.minecraft.client.resources.I18n; import net.minecraft.util.EnumChatFormatting; -import net.minecraft.util.IIcon; + import org.lwjgl.input.Keyboard; -import java.util.List; -import java.util.stream.Collectors; +import com.gtnewhorizons.navigator.api.NavigatorApi; +import com.gtnewhorizons.navigator.api.model.locations.IWaypointAndLocationProvider; +import com.gtnewhorizons.navigator.api.model.waypoints.Waypoint; +import com.sinthoras.visualprospecting.database.ClientCache; +import com.sinthoras.visualprospecting.database.OreVeinPosition; + +import gregtech.api.enums.OrePrefixes; +import gregtech.api.enums.TextureSet; +import gregtech.api.interfaces.IIconContainer; +import gregtech.api.interfaces.IOreMaterial; +import gregtech.api.util.LightingHelper; public class OreVeinLocation implements IWaypointAndLocationProvider { private static final String depletedHint = EnumChatFormatting.RED + I18n.format("visualprospecting.depleted"); - private static final String activeWaypointHint = EnumChatFormatting.GOLD + I18n.format("visualprospecting.iswaypoint"); - private static final String toggleDepletedHint = EnumChatFormatting.DARK_GRAY + I18n.format("visualprospecting.node.deletehint", Keyboard.getKeyName(VP.keyAction.getKeyCode())); + private static final String activeWaypointHint = EnumChatFormatting.GOLD + + I18n.format("visualprospecting.iswaypoint"); + private static final String toggleDepletedHint = EnumChatFormatting.DARK_GRAY + I18n + .format("visualprospecting.node.deletehint", Keyboard.getKeyName(NavigatorApi.ACTION_KEY.getKeyCode())); private final OreVeinPosition oreVeinPosition; private final String name; private final List materialNames; + private final IIconContainer iconContainer; + private final int color; private boolean isActiveAsWaypoint; public OreVeinLocation(OreVeinPosition oreVeinPosition) { this.oreVeinPosition = oreVeinPosition; - name = EnumChatFormatting.WHITE + I18n.format(oreVeinPosition.veinType.name); - materialNames = oreVeinPosition.veinType.getOreMaterialNames().stream().map(materialName -> EnumChatFormatting.GRAY + materialName).collect(Collectors.toList()); + name = EnumChatFormatting.WHITE + oreVeinPosition.veinType.getVeinName(); + materialNames = oreVeinPosition.veinType.getOreMaterialNames().stream() + .map(materialName -> EnumChatFormatting.GRAY + materialName).collect(Collectors.toList()); + final IOreMaterial primaryOre = oreVeinPosition.veinType.primaryOre; + final TextureSet textureSet = primaryOre.getTextureSet(); + iconContainer = textureSet.mTextures[OrePrefixes.ore.getTextureIndex()]; + color = LightingHelper.getColor(primaryOre.getRGBA()); } @Override public Waypoint toWaypoint() { - return new Waypoint(oreVeinPosition.getBlockX(), 65, oreVeinPosition.getBlockZ(), oreVeinPosition.dimensionId, - I18n.format("visualprospecting.tracked", I18n.format(oreVeinPosition.veinType.name)), + return new Waypoint( + oreVeinPosition.getBlockX(), + 65, + oreVeinPosition.getBlockZ(), + oreVeinPosition.dimensionId, + I18n.format("visualprospecting.tracked", oreVeinPosition.veinType.getVeinName()), getColor()); } @@ -102,10 +123,14 @@ public boolean drawSearchHighlight() { } public int getColor() { - return oreVeinPosition.veinType.oreMaterialProvider.getColor(); + return color; + } + + public IIconContainer getIconFromPrimaryOre() { + return iconContainer; } - public IIcon getIconFromPrimaryOre() { - return oreVeinPosition.veinType.oreMaterialProvider.getIcon(); + public IOreMaterial getRepresentativeOre() { + return oreVeinPosition.veinType.representativeOre; } } diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/model/locations/ThaumcraftNodeLocation.java b/src/main/java/com/sinthoras/visualprospecting/integration/model/locations/ThaumcraftNodeLocation.java deleted file mode 100644 index c4476dc1..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/model/locations/ThaumcraftNodeLocation.java +++ /dev/null @@ -1,134 +0,0 @@ -package com.sinthoras.visualprospecting.integration.model.locations; - -import com.dyonovan.tcnodetracker.lib.NodeList; -import com.sinthoras.visualprospecting.VP; -import com.sinthoras.visualprospecting.integration.model.waypoints.Waypoint; -import net.minecraft.client.resources.I18n; -import net.minecraft.util.EnumChatFormatting; -import org.lwjgl.input.Keyboard; -import thaumcraft.api.aspects.Aspect; -import thaumcraft.api.aspects.AspectList; -import thaumcraft.api.nodes.NodeType; -import thaumcraft.common.config.ConfigBlocks; -import thaumcraft.common.tiles.TileNode; - -public class ThaumcraftNodeLocation implements IWaypointAndLocationProvider { - - private static final String deleteHint = EnumChatFormatting.DARK_GRAY + I18n.format("visualprospecting.node.deletehint", Keyboard.getKeyName(VP.keyAction.getKeyCode()));; - private static final String activeWaypointHint = EnumChatFormatting.GOLD + I18n.format("visualprospecting.iswaypoint"); - private static final String title = EnumChatFormatting.BOLD + I18n.format("tile.blockAiry.0.name"); - - private final NodeList node; - private final TileNode nodeTile; - private final String description; - - private boolean isActiveAsWaypoint; - - public ThaumcraftNodeLocation(NodeList node) { - this.node = node; - - nodeTile = new TileNode(); - final AspectList aspectList = new AspectList(); - for(String aspectTag : node.aspect.keySet()) { - aspectList.add(Aspect.getAspect(aspectTag), node.aspect.get(aspectTag)); - } - nodeTile.setAspects(aspectList); - switch(node.type) { - case "NORMAL": - nodeTile.setNodeType(NodeType.NORMAL); - break; - case "UNSTABLE": - nodeTile.setNodeType(NodeType.UNSTABLE); - break; - case "DARK": - nodeTile.setNodeType(NodeType.DARK); - break; - case "TAINTED": - nodeTile.setNodeType(NodeType.TAINTED); - break; - case "PURE": - nodeTile.setNodeType(NodeType.PURE); - break; - case "HUNGRY": - nodeTile.setNodeType(NodeType.HUNGRY); - break; - } - nodeTile.blockType = ConfigBlocks.blockAiry; - nodeTile.blockMetadata = 0; - - description = node.mod.equals("BLANK") ? EnumChatFormatting.GRAY + I18n.format("nodetype." + node.type + ".name") - : EnumChatFormatting.GRAY + I18n.format("nodetype." + node.type + ".name") + ", " + I18n.format("nodemod." + node.mod + ".name"); - } - - @Override - public double getBlockX() { - return node.x + 0.5; - } - - public double getBlockY() { - return node.y + 0.5; - } - - @Override - public double getBlockZ() { - return node.z + 0.5; - } - - @Override - public int getDimensionId() { - return node.dim; - } - - @Override - public Waypoint toWaypoint() { - return new Waypoint(node.x, node.y, node.z, node.dim, - I18n.format("visualprospecting.tracked", I18n.format("tile.blockAiry.0.name")), - nodeTile.targetColor.getRGB()); - } - - @Override - public boolean isActiveAsWaypoint() { - return isActiveAsWaypoint; - } - - @Override - public void onWaypointCleared() { - isActiveAsWaypoint = false; - } - - @Override - public void onWaypointUpdated(Waypoint waypoint) { - isActiveAsWaypoint = waypoint.dimensionId == node.dim - && waypoint.blockX == node.x - && waypoint.blockY == node.y - && waypoint.blockZ== node.z; - } - - public boolean belongsToNode(NodeList other) { - return node.x == other.x && node.y == other.y && node.z == other.z && node.dim == other.dim; - } - - public Aspect getStrongestAspect() { - return nodeTile.getAspects().getAspectsSortedAmount()[0]; - } - - public AspectList getAspects() { - return nodeTile.getAspects(); - } - - public String getDescription() { - return description; - } - - public String getDeleteHint() { - return deleteHint; - } - - public String getActiveWaypointHint() { - return activeWaypointHint; - } - - public String getTitle() { - return title; - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/model/locations/UndergroundFluidChunkLocation.java b/src/main/java/com/sinthoras/visualprospecting/integration/model/locations/UndergroundFluidChunkLocation.java deleted file mode 100644 index 41769a50..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/model/locations/UndergroundFluidChunkLocation.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.sinthoras.visualprospecting.integration.model.locations; - -import com.sinthoras.visualprospecting.Utils; -import net.minecraftforge.fluids.Fluid; - -public class UndergroundFluidChunkLocation implements ILocationProvider { - - private final int blockX; - private final int blockZ; - private final int dimensionId; - private final Fluid fluid; - private final int fluidAmount; - private final int maxAmountInField; - private final int minAmountInField; - - public UndergroundFluidChunkLocation(int chunkX, int chunkZ, int dimensionId, Fluid fluid, int fluidAmount, int minAmountInField, int maxAmountInField) { - blockX = Utils.coordChunkToBlock(chunkX); - blockZ = Utils.coordChunkToBlock(chunkZ); - this.dimensionId = dimensionId; - this.fluid = fluid; - this.fluidAmount = fluidAmount; - this.maxAmountInField = maxAmountInField; - this.minAmountInField = minAmountInField; - } - - public double getBlockX() { - return blockX + 0.5; - } - - public double getBlockZ() { - return blockZ + 0.5; - } - - public int getDimensionId() { - return dimensionId; - } - - public String getFluidAmountFormatted() { - if(fluidAmount >= 1000) { - return (fluidAmount / 1000) + "kL"; - } - return fluidAmount + "L"; - } - - public int getFluidAmount() { - return fluidAmount; - } - - public Fluid getFluid() { - return fluid; - } - - public int getMaxAmountInField() { - return maxAmountInField; - } - - public int getMinAmountInField() { - return minAmountInField; - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/model/locations/UndergroundFluidLocation.java b/src/main/java/com/sinthoras/visualprospecting/integration/model/locations/UndergroundFluidLocation.java index cf4bfbb1..1a2068cc 100644 --- a/src/main/java/com/sinthoras/visualprospecting/integration/model/locations/UndergroundFluidLocation.java +++ b/src/main/java/com/sinthoras/visualprospecting/integration/model/locations/UndergroundFluidLocation.java @@ -1,18 +1,46 @@ package com.sinthoras.visualprospecting.integration.model.locations; -import com.sinthoras.visualprospecting.database.UndergroundFluidPosition; import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidRegistry; + +import com.gtnewhorizons.navigator.api.model.locations.ILocationProvider; +import com.sinthoras.visualprospecting.database.UndergroundFluidPosition; + +import gregtech.api.enums.UndergroundFluidNames; +import it.unimi.dsi.fastutil.objects.Reference2IntMap; +import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; public class UndergroundFluidLocation implements ILocationProvider { + private static final int NO_OVERRIDE = Integer.MIN_VALUE; + private static Reference2IntMap renderColorOverrides; + private final UndergroundFluidPosition undergroundFluidPosition; - private final int minProduction; - private final int maxProduction; + private final int color; + private boolean active; public UndergroundFluidLocation(UndergroundFluidPosition undergroundFluidPosition) { this.undergroundFluidPosition = undergroundFluidPosition; - minProduction = undergroundFluidPosition.getMinProduction(); - maxProduction = undergroundFluidPosition.getMaxProduction(); + this.color = resolveColor(undergroundFluidPosition.fluid); + } + + public static int resolveColor(Fluid fluid) { + if (fluid == null) return 0xFFFFFF; + if (renderColorOverrides == null) { + Reference2IntOpenHashMap map = new Reference2IntOpenHashMap<>(); + map.defaultReturnValue(NO_OVERRIDE); + for (UndergroundFluidNames entry : UndergroundFluidNames.values()) { + if (entry.renderColor == null) continue; + Fluid f = FluidRegistry.getFluid(entry.name); + if (f == null) continue; + int packed = ((entry.renderColor[0] & 0xFF) << 16) | ((entry.renderColor[1] & 0xFF) << 8) + | (entry.renderColor[2] & 0xFF); + map.put(f, packed); + } + renderColorOverrides = map; + } + int override = renderColorOverrides.getInt(fluid); + return override != NO_OVERRIDE ? override : fluid.getColor(); } @Override @@ -31,14 +59,30 @@ public double getBlockZ() { } public int getMinProduction() { - return minProduction; + return undergroundFluidPosition.getMinProduction(); } public int getMaxProduction() { - return maxProduction; + return undergroundFluidPosition.getMaxProduction(); + } + + public int[][] getChunks() { + return undergroundFluidPosition.chunks; } public Fluid getFluid() { return undergroundFluidPosition.fluid; } + + public int getColor() { + return color; + } + + public void setActive(boolean active) { + this.active = active; + } + + public boolean isActive() { + return active; + } } diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/model/render/DimensionStoneBackground.java b/src/main/java/com/sinthoras/visualprospecting/integration/model/render/DimensionStoneBackground.java new file mode 100644 index 00000000..9c157621 --- /dev/null +++ b/src/main/java/com/sinthoras/visualprospecting/integration/model/render/DimensionStoneBackground.java @@ -0,0 +1,48 @@ +package com.sinthoras.visualprospecting.integration.model.render; + +import java.util.List; + +import net.minecraft.init.Blocks; +import net.minecraft.util.IIcon; + +import com.sinthoras.visualprospecting.database.DimensionNameResolver; + +import gregtech.api.enums.StoneType; +import gtneioreplugin.util.DimensionHelper; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; + +/** + * Picks the stone-type background icon for an ore-vein marker so it matches the vein's host rock (netherrack in the + * Nether, end stone in the End, ...). + *

+ * Unmapped dimensions fall back to vanilla stone. + */ +public final class DimensionStoneBackground { + + private static final Int2ObjectOpenHashMap STONE_BY_DIM_ID = new Int2ObjectOpenHashMap<>(); + + private DimensionStoneBackground() {} + + /** Returns the stone background icon for the vein's dimension, or vanilla stone when unknown. */ + public static IIcon getBackgroundIcon(int dimensionId) { + StoneType stoneType = STONE_BY_DIM_ID.get(dimensionId); + if (stoneType == null) { + final String dimName = DimensionNameResolver.resolve(dimensionId); + stoneType = resolveStoneType(dimName); + if (!dimName.isEmpty()) { + STONE_BY_DIM_ID.put(dimensionId, stoneType); + } + } + + final IIcon icon = stoneType.getIcon(1); + return icon != null ? icon : Blocks.stone.getIcon(0, 0); + } + + private static StoneType resolveStoneType(String dimName) { + if (!DimensionHelper.INTERNAL_TO_FULL.containsKey(dimName)) { + return StoneType.Stone; + } + final List stoneTypes = DimensionHelper.getStoneTypes(dimName); + return stoneTypes.isEmpty() ? StoneType.Stone : stoneTypes.get(0); + } +} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/model/render/OreVeinMapMarker.java b/src/main/java/com/sinthoras/visualprospecting/integration/model/render/OreVeinMapMarker.java new file mode 100644 index 00000000..0fb10d4c --- /dev/null +++ b/src/main/java/com/sinthoras/visualprospecting/integration/model/render/OreVeinMapMarker.java @@ -0,0 +1,164 @@ +package com.sinthoras.visualprospecting.integration.model.render; + +import java.awt.AlphaComposite; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.InputStream; + +import javax.annotation.Nullable; +import javax.imageio.ImageIO; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.renderer.texture.TextureMap; +import net.minecraft.util.IIcon; +import net.minecraft.util.ResourceLocation; + +import com.gtnewhorizons.navigator.api.model.markers.MapMarker; +import com.sinthoras.visualprospecting.Config; +import com.sinthoras.visualprospecting.Tags; +import com.sinthoras.visualprospecting.VP; +import com.sinthoras.visualprospecting.integration.model.locations.OreVeinLocation; + +import gregtech.api.interfaces.IIconContainer; +import gregtech.api.util.client.ResourceUtils; + +public final class OreVeinMapMarker { + + private static final ResourceLocation DEPLETED = new ResourceLocation(Tags.MODID, "textures/depleted.png"); + private static final ResourceLocation MARKED = new ResourceLocation(Tags.MODID, "textures/node_marked.png"); + private static final int ICON_SIZE = 32; + private static BufferedImage depletedImage; + private static BufferedImage markedImage; + + private OreVeinMapMarker() {} + + public static @Nullable MapMarker create(OreVeinLocation location) { + BufferedImage icon = createIcon(location); + if (icon == null) return null; + + MapMarker marker = new MapMarker(icon).setDisplaySize(ICON_SIZE, ICON_SIZE).setDisplayZoomScale(1, 2, 3, 5); + if (!location.isDepleted()) { + marker.setLabel(location.getName()).setLabelColor(location.drawSearchHighlight() ? 0xFFFFFF : 0x7F7F7F) + .setLabelScale(1.2F).setLabelZoomScale(1, 2.5, Math.min(Config.minZoomLevelForOreLabel, 4), 5) + .setLabelOffsetY(28).setLabelBackgroundOpacity(0.35F) + .setLabelMinZoom(Config.minZoomLevelForOreLabel) + .setLabelOnMinimap(Config.showOreLabelsOnJourneyMap6Minimap); + } + return marker; + } + + private static @Nullable BufferedImage createIcon(OreVeinLocation location) { + BufferedImage background = spriteImage( + DimensionStoneBackground.getBackgroundIcon(location.getDimensionId()), + 0xFFFFFF); + IIconContainer ore = location.getIconFromPrimaryOre(); + BufferedImage oreImage = spriteImage(ore.getIcon(), location.getColor()); + if (background == null || oreImage == null) return null; + + BufferedImage image = new BufferedImage(ICON_SIZE, ICON_SIZE, BufferedImage.TYPE_INT_ARGB); + Graphics2D graphics = image.createGraphics(); + try { + graphics.setRenderingHint( + RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); + draw(graphics, background); + draw(graphics, oreImage); + BufferedImage overlay = spriteImage(ore.getOverlayIcon(), 0xFFFFFF); + if (overlay != null) draw(graphics, overlay); + if (!location.drawSearchHighlight() || location.isDepleted()) { + graphics.setComposite(AlphaComposite.SrcOver); + graphics.setColor(new Color(0, 0, 0, 150)); + graphics.fillRect(0, 0, ICON_SIZE, ICON_SIZE); + } + if (location.isDepleted()) draw(graphics, getDepletedImage()); + if (location.isActiveAsWaypoint()) draw(graphics, getMarkedImage()); + } finally { + graphics.dispose(); + } + return image; + } + + private static void draw(Graphics2D graphics, @Nullable BufferedImage image) { + if (image != null) graphics.drawImage(image, 0, 0, ICON_SIZE, ICON_SIZE, null); + } + + private static @Nullable BufferedImage spriteImage(@Nullable IIcon icon, int tint) { + if (icon == null) return null; + BufferedImage source = loadSprite(icon); + if (source == null) source = atlasSpriteImage(icon); + if (source == null) return null; + + int red = tint >> 16 & 0xFF; + int green = tint >> 8 & 0xFF; + int blue = tint & 0xFF; + int width = source.getWidth(); + int height = source.getHeight(); + int[] pixels = source.getRGB(0, 0, width, height, null, 0, width); + for (int i = 0; i < pixels.length; i++) { + int pixel = pixels[i]; + pixels[i] = pixel & 0xFF000000 | (pixel >> 16 & 0xFF) * red / 255 << 16 + | (pixel >> 8 & 0xFF) * green / 255 << 8 + | (pixel & 0xFF) * blue / 255; + } + + BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + image.setRGB(0, 0, width, height, pixels, 0, width); + return image; + } + + private static @Nullable BufferedImage loadSprite(IIcon icon) { + ResourceLocation location = ResourceUtils.getCompleteBlockTextureResourceLocation(icon.getIconName()); + try (InputStream stream = Minecraft.getMinecraft().getResourceManager().getResource(location) + .getInputStream()) { + BufferedImage image = ImageIO.read(stream); + if (image == null) return null; + int frameHeight = Math.min(image.getWidth(), image.getHeight()); + return image.getSubimage(0, 0, image.getWidth(), frameHeight); + } catch (IOException ignored) { + return null; + } + } + + private static @Nullable BufferedImage atlasSpriteImage(IIcon icon) { + if (!(icon instanceof TextureAtlasSprite sprite) || sprite.getFrameCount() == 0) return null; + + int sourceWidth = sprite.getIconWidth(); + int sourceHeight = sprite.getIconHeight(); + int[][] frame = sprite.getFrameTextureData(0); + if (frame.length == 0 || frame[0] == null || frame[0].length < sourceWidth * sourceHeight) return null; + + // The atlas location is irrelevant here; reuse MapMarker's normalized sprite region to exclude padding. + MapMarker spriteRegion = new MapMarker(TextureMap.locationBlocksTexture, sprite); + int width = spriteRegion.getTextureWidth(); + int height = spriteRegion.getTextureHeight(); + int offsetX = spriteRegion.getTextureX() - sprite.getOriginX(); + int offsetY = spriteRegion.getTextureY() - sprite.getOriginY(); + BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + image.setRGB(0, 0, width, height, frame[0], offsetY * sourceWidth + offsetX, sourceWidth); + return image; + } + + private static @Nullable BufferedImage getDepletedImage() { + if (depletedImage == null) depletedImage = load(DEPLETED); + return depletedImage; + } + + private static @Nullable BufferedImage getMarkedImage() { + if (markedImage == null) markedImage = load(MARKED); + return markedImage; + } + + private static @Nullable BufferedImage load(ResourceLocation location) { + try (InputStream stream = Minecraft.getMinecraft().getResourceManager().getResource(location) + .getInputStream()) { + return ImageIO.read(stream); + } catch (IOException e) { + VP.LOG.error("Could not load map marker image " + location, e); + return null; + } + } +} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/model/render/OreVeinRenderStep.java b/src/main/java/com/sinthoras/visualprospecting/integration/model/render/OreVeinRenderStep.java new file mode 100644 index 00000000..56b940e2 --- /dev/null +++ b/src/main/java/com/sinthoras/visualprospecting/integration/model/render/OreVeinRenderStep.java @@ -0,0 +1,149 @@ +package com.sinthoras.visualprospecting.integration.model.render; + +import java.util.List; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.audio.PositionedSoundRecord; +import net.minecraft.item.ItemStack; +import net.minecraft.util.IIcon; +import net.minecraft.util.ResourceLocation; + +import com.gtnewhorizons.navigator.api.model.steps.UniversalInteractableStep; +import com.gtnewhorizons.navigator.api.util.DrawUtils; +import com.sinthoras.visualprospecting.Config; +import com.sinthoras.visualprospecting.Tags; +import com.sinthoras.visualprospecting.integration.model.locations.OreVeinLocation; + +import codechicken.nei.api.ShortcutInputHandler; +import gregtech.api.interfaces.IIconContainer; +import gregtech.api.interfaces.IOreMaterial; +import gregtech.common.ores.OreInfo; +import gregtech.common.ores.OreManager; + +public class OreVeinRenderStep extends UniversalInteractableStep { + + private static final ResourceLocation clickSound = new ResourceLocation("gui.button.press"); + private static final ResourceLocation depletedTextureLocation = new ResourceLocation( + Tags.MODID, + "textures/depleted.png"); + private static final int fontHeight = Minecraft.getMinecraft().fontRenderer.FONT_HEIGHT; + + public OreVeinRenderStep(OreVeinLocation location) { + super(location); + setFontScale(1.2f); + setMinScale(1); + } + + @Override + public void preRender(double topX, double topY, float drawScale, double zoom) { + double iconSize = isXaero ? 10 * zoom : 32 * drawScale * getZoomScale(1, 2, 3, 5); + setSize(iconSize); + setOffset(-iconSize / 2); + } + + @Override + public void draw(double topX, double topY, float drawScale, double zoom) { + // Xaero culling relies on Navigator's visible cache; add centered bounds if that cache grows costly. + if (!isXaero) { + final Minecraft mc = Minecraft.getMinecraft(); + final double scale = 1.0; + final double screenW = mc.displayWidth * scale; + final double screenH = mc.displayHeight * scale; + final double topMargin = (fontHeight + 5) * getLabelScale() * scale; + if (topX + width < 0 || topX > screenW || topY + height < -topMargin || topY > screenH) { + return; + } + } + + if (zoom >= Config.minZoomLevelForOreLabel && !location.isDepleted()) { + final double labelScale = getLabelScale(); + final int fontColor = location.drawSearchHighlight() ? 0xFFFFFF : 0x7F7F7F; + DrawUtils.drawLabel( + location.getName(), + topX + width / 2, + topY - (fontHeight + 5) * (isXaero ? 1 : labelScale), + fontColor, + 0, + true, + labelScale); + } + + final IIcon blockIcon = DimensionStoneBackground.getBackgroundIcon(location.getDimensionId()); + DrawUtils.drawQuad(blockIcon, topX, topY, width, height, 0xFFFFFF, 255); + + final IIconContainer oreIconContainer = location.getIconFromPrimaryOre(); + final IIcon oreIcon = oreIconContainer.getIcon(); + DrawUtils.drawQuad(oreIcon, topX, topY, width, height, location.getColor(), 255); + final IIcon oreIconOverlay = oreIconContainer.getOverlayIcon(); + if (oreIconOverlay != null) DrawUtils.drawQuad(oreIconOverlay, topX, topY, width, height, 0xFFFFFF, 255); + + if (!location.drawSearchHighlight() || location.isDepleted()) { + DrawUtils.drawRect(topX, topY, width, height, 0x000000, 150); + if (location.isDepleted()) { + DrawUtils.drawQuad(depletedTextureLocation, topX, topY, width, height, 0xFFFFFF, 255); + } + } + + if (location.isActiveAsWaypoint()) { + final double thickness = width / 8; + DrawUtils.drawHollowRect(topX, topY, width, height, 0xFFD700, 204, thickness); + } + } + + @Override + public void getTooltip(List list) { + if (location.isDepleted()) { + list.add(location.getDepletedHint()); + } + if (location.isActiveAsWaypoint()) { + list.add(location.getActiveWaypointHint()); + } + list.add(location.getName()); + if (!location.isDepleted()) { + list.addAll(location.getMaterialNames()); + } + list.add(location.getToggleDepletedHint()); + } + + @Override + public boolean isMouseOver(int mouseX, int mouseY) { + return mouseX >= getX() && mouseX <= getX() + width && mouseY >= getY() && mouseY <= getY() + height; + } + + @Override + public void onActionKeyPressed() { + location.toggleOreVein(); + } + + @Override + public boolean onKeyPressed(int keyCode) { + + if (super.onKeyPressed(keyCode)) { + return true; + } + + final ItemStack itemStack; + + try (OreInfo info = OreInfo.getNewInfo()) { + info.material = location.getRepresentativeOre(); + + itemStack = OreManager.getStack(info, 1); + } + + if (ShortcutInputHandler.handleKeyEvent(itemStack)) { + playClickSound(); + return true; + } + + return false; + } + + private void playClickSound() { + Minecraft.getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.func_147674_a(clickSound, 1.0F)); + } + + private double getLabelScale() { + return isXaero ? getFontScale() + : getFontScale() * getZoomScale(1.2, 3, Math.min(Config.minZoomLevelForOreLabel, 4), 5); + } +} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/model/render/UndergroundFluidImageOverlay.java b/src/main/java/com/sinthoras/visualprospecting/integration/model/render/UndergroundFluidImageOverlay.java new file mode 100644 index 00000000..3946be2a --- /dev/null +++ b/src/main/java/com/sinthoras/visualprospecting/integration/model/render/UndergroundFluidImageOverlay.java @@ -0,0 +1,78 @@ +package com.sinthoras.visualprospecting.integration.model.render; + +import java.awt.image.BufferedImage; +import java.util.Collection; +import java.util.Collections; + +import com.sinthoras.visualprospecting.Tags; +import com.sinthoras.visualprospecting.VP; +import com.sinthoras.visualprospecting.integration.model.locations.UndergroundFluidLocation; + +import journeymap.api.v2.client.display.ImageOverlay; +import journeymap.api.v2.client.model.MapImage; +import journeymap.api.v2.common.Context; +import journeymap.api.v2.common.util.BlockPos; + +public final class UndergroundFluidImageOverlay { + + private static final int CELL_PIXELS = 8; + + private UndergroundFluidImageOverlay() {} + + public static Collection create(UndergroundFluidLocation location) { + BufferedImage image = new BufferedImage( + VP.undergroundFluidSizeChunkX * CELL_PIXELS, + VP.undergroundFluidSizeChunkZ * CELL_PIXELS, + BufferedImage.TYPE_INT_ARGB); + if (location.isActive()) { + int min = location.getMinProduction(); + int max = location.getMaxProduction(); + float range = max - min + 1; + for (int x = 0; x < VP.undergroundFluidSizeChunkX; x++) { + for (int z = 0; z < VP.undergroundFluidSizeChunkZ; z++) { + int amount = location.getChunks()[x][z]; + if (amount <= 0) continue; + int alpha = range > 1 ? (int) ((amount - min) / range * 255) : 10; + fillCell(image, x, z, alpha << 24 | location.getColor()); + } + } + } + drawBorder( + image, + (location.getMaxProduction() > 0 ? location.getColor() : 0xFFFFFF) + | (location.isActive() && location.getMaxProduction() > 0 ? 0xFF : 74) << 24); + + int x = (int) Math.floor(location.getBlockX()); + int z = (int) Math.floor(location.getBlockZ()); + int width = VP.undergroundFluidSizeChunkX * VP.chunkWidth; + int height = VP.undergroundFluidSizeChunkZ * VP.chunkWidth; + MapImage mapImage = new MapImage(image).setBlur(false); + ImageOverlay overlay = new ImageOverlay( + Tags.MODID, + new BlockPos(x, 64, z), + new BlockPos(x + width, 64, z + height), + mapImage); + overlay.setDimension(location.getDimensionId()).setOverlayGroupName(UndergroundFluidLocation.class.getName()) + .setActiveUIs(Context.UI.Minimap); + return Collections.singletonList(overlay); + } + + private static void fillCell(BufferedImage image, int cellX, int cellZ, int argb) { + for (int x = cellX * CELL_PIXELS; x < (cellX + 1) * CELL_PIXELS; x++) { + for (int z = cellZ * CELL_PIXELS; z < (cellZ + 1) * CELL_PIXELS; z++) { + image.setRGB(x, z, argb); + } + } + } + + private static void drawBorder(BufferedImage image, int argb) { + for (int x = 0; x < image.getWidth(); x++) { + image.setRGB(x, 0, argb); + image.setRGB(x, image.getHeight() - 1, argb); + } + for (int z = 0; z < image.getHeight(); z++) { + image.setRGB(0, z, argb); + image.setRGB(image.getWidth() - 1, z, argb); + } + } +} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/model/render/UndergroundFluidRenderStep.java b/src/main/java/com/sinthoras/visualprospecting/integration/model/render/UndergroundFluidRenderStep.java new file mode 100644 index 00000000..2229037c --- /dev/null +++ b/src/main/java/com/sinthoras/visualprospecting/integration/model/render/UndergroundFluidRenderStep.java @@ -0,0 +1,167 @@ +package com.sinthoras.visualprospecting.integration.model.render; + +import java.text.MessageFormat; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.resources.I18n; + +import com.gtnewhorizons.navigator.api.model.steps.UniversalRenderStep; +import com.gtnewhorizons.navigator.api.util.DrawUtils; +import com.sinthoras.visualprospecting.Config; +import com.sinthoras.visualprospecting.VP; +import com.sinthoras.visualprospecting.integration.model.layers.UndergroundFluidLayerManager; +import com.sinthoras.visualprospecting.integration.model.locations.UndergroundFluidLocation; + +public class UndergroundFluidRenderStep extends UniversalRenderStep { + + public UndergroundFluidRenderStep(UndergroundFluidLocation location) { + super(location); + setSize(VP.undergroundFluidSizeChunkX * VP.chunkWidth); + setFontScale(0.5f); + } + + @Override + public void preRender(double topX, double topY, float drawScale, double zoom) { + setOffset(isJourneyMap ? -blockSize / 2 : 0); + } + + @Override + public void draw(double topX, double topY, float drawScale, double zoom) { + final Minecraft mc = Minecraft.getMinecraft(); + final double regionW = VP.undergroundFluidSizeChunkX * VP.chunkWidth * blockSize; + final double regionH = VP.undergroundFluidSizeChunkZ * VP.chunkWidth * blockSize; + // Xaero culling relies on Navigator's visible cache; add centered bounds if that cache grows costly. + if (!isXaero + && (topX + regionW < 0 || topY + regionH < 0 || topX > mc.displayWidth || topY > mc.displayHeight)) { + return; + } + + renderChunks(topX, topY); + setSize(VP.undergroundFluidSizeChunkX * VP.chunkWidth); + final int maxAmountInField = location.getMaxProduction(); + if (maxAmountInField > 0) { + int alpha = location.isActive() ? 255 : 74; + DrawUtils + .drawHollowRect(topX, topY, getAdjustedWidth(), getAdjustedHeight(), location.getColor(), alpha, 2); + } else { + DrawUtils.drawHollowRect(topX, topY, getAdjustedWidth(), getAdjustedHeight(), 0xFFFFFF, 74, 2); + } + + if (!isMinimap() && getZoomStep() >= Config.minZoomLevelForUndergroundFluidDetails - 3) { + String title = I18n.format("visualprospecting.empty"); + String values = null; + if (maxAmountInField > 0) { + title = location.getFluid().getLocalizedName(); + values = MessageFormat.format( + "{0}-{1} L/Op", + formatAmount(location.getMinProduction()), + formatAmount(maxAmountInField)); + } + + int textColor = 0xFFFFFFFF; + if (UndergroundFluidLayerManager.instance.isSearchActive()) { + textColor = location.isActive() ? 0xFFFF00 : 0x444444; + } + + final double labelScale = getMainLabelScale(); + DrawUtils.drawLabel( + title, + topX + getAdjustedWidth() / 2, + topY + 1.5, + textColor, + 0xB4000000, + true, + labelScale); + if (values != null) { + DrawUtils.drawLabel( + values, + topX + getAdjustedWidth() / 2, + topY + 1.5 + (mc.fontRenderer.FONT_HEIGHT + 2) * labelScale, + textColor, + 0xB4000000, + true, + labelScale); + } + } + } + + private void renderChunks(double x, double y) { + final double zoomStep = getZoomStep(); + if (zoomStep < Config.minZoomLevelForUndergroundFluidDetails - 1 || location.getMaxProduction() <= 0 + || !location.isActive()) { + return; + } + final boolean drawLabels = zoomStep >= Config.minZoomLevelForUndergroundFluidDetails; + + final int minProduction = location.getMinProduction(); + final int maxProduction = location.getMaxProduction(); + final int fluidColor = location.getColor(); + final int[][] chunks = location.getChunks(); + final float productionRange = maxProduction - minProduction + 1; + final boolean highlightPeak = maxProduction >= 10; + + final Minecraft mc = Minecraft.getMinecraft(); + final double cellLabelScale = getCellLabelScale(); + final double cellLabelOffsetY = isXaero ? 0 + : (mc.fontRenderer.FONT_HEIGHT + 2) * cellLabelScale + * getZoomScale( + 1, + 0, + Config.minZoomLevelForUndergroundFluidDetails, + Config.minZoomLevelForUndergroundFluidDetails + 1); + final double screenW = mc.displayWidth; + final double screenH = mc.displayHeight; + setSize(VP.chunkWidth); + final double cellW = getAdjustedWidth(); + final double cellH = getAdjustedHeight(); + for (int chunkX = 0; chunkX < VP.undergroundFluidSizeChunkX; chunkX++) { + final double cellX = x + chunkX * cellW; + if (!isXaero && (cellX + cellW < 0 || cellX > screenW)) continue; + for (int chunkZ = 0; chunkZ < VP.undergroundFluidSizeChunkZ; chunkZ++) { + final double cellY = y + chunkZ * cellH; + if (!isXaero && (cellY + cellH < 0 || cellY > screenH)) continue; + int amount = chunks[chunkX][chunkZ]; + if (amount <= 0) continue; + int alpha = productionRange > 1 ? (int) ((amount - minProduction) / productionRange * 255) : 10; + DrawUtils.drawRect(cellX, cellY, cellW, cellH, fluidColor, alpha); + + if (highlightPeak && amount >= maxProduction) { + DrawUtils.drawHollowRect(cellX, cellY, cellW, cellH, 0xFFD700, 204, 1.5); + } + + if (drawLabels) { + DrawUtils.drawLabel( + MessageFormat.format("{0} L/Op", formatAmount(amount)), + cellX + cellW / 2, + cellY + cellH / 2 + cellLabelOffsetY, + 0xFFFFFFFF, + 0xB4000000, + true, + cellLabelScale); + } + } + } + } + + private String formatAmount(int amount) { + if (amount < 1000) { + return String.valueOf(amount); + } + final int roundedTenths = (amount + 50) / 100; + if (roundedTenths % 10 == 0) { + return (roundedTenths / 10) + "k"; + } + return (roundedTenths / 10) + "." + (roundedTenths % 10) + "k"; + } + + private double getCellLabelScale() { + return isXaero ? getFontScale() * 0.7 + : getFontScale() * getZoomScale(1.2, 3, Math.min(Config.minZoomLevelForUndergroundFluidDetails, 4), 5); + } + + private double getMainLabelScale() { + return isXaero ? getFontScale() + : getFontScale() + * getZoomScale(1, 4.5, Math.min(Config.minZoomLevelForUndergroundFluidDetails - 3, 4), 5); + } +} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/model/waypoints/Waypoint.java b/src/main/java/com/sinthoras/visualprospecting/integration/model/waypoints/Waypoint.java deleted file mode 100644 index 2d728403..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/model/waypoints/Waypoint.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.sinthoras.visualprospecting.integration.model.waypoints; - -public class Waypoint { - public final int blockX; - public final int blockY; - public final int blockZ; - public final int dimensionId; - public final String label; - public final int color; - - public Waypoint(int blockX, int blockY, int blockZ, int dimensionId, String label, int color) { - this.blockX = blockX; - this.blockY = blockY; - this.blockZ = blockZ; - this.dimensionId = dimensionId; - this.label = label; - this.color = color; - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/model/waypoints/WaypointManager.java b/src/main/java/com/sinthoras/visualprospecting/integration/model/waypoints/WaypointManager.java deleted file mode 100644 index 1e6643f7..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/model/waypoints/WaypointManager.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.sinthoras.visualprospecting.integration.model.waypoints; - -import com.sinthoras.visualprospecting.integration.model.SupportedMods; -import com.sinthoras.visualprospecting.integration.model.layers.WaypointProviderManager; - -public abstract class WaypointManager { - - public WaypointManager(WaypointProviderManager layerManager, SupportedMods map) { - layerManager.registerWaypointManager(map, this); - } - - public abstract void clearActiveWaypoint(); - - public abstract void updateActiveWaypoint(Waypoint waypoint); -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/tcnodetracker/NTNodeTrackerWaypointManager.java b/src/main/java/com/sinthoras/visualprospecting/integration/tcnodetracker/NTNodeTrackerWaypointManager.java deleted file mode 100644 index 39c076b6..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/tcnodetracker/NTNodeTrackerWaypointManager.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.sinthoras.visualprospecting.integration.tcnodetracker; - -import com.dyonovan.tcnodetracker.TCNodeTracker; -import com.sinthoras.visualprospecting.integration.model.SupportedMods; -import com.sinthoras.visualprospecting.integration.model.layers.ThaumcraftNodeLayerManager; -import com.sinthoras.visualprospecting.integration.model.waypoints.Waypoint; - -public class NTNodeTrackerWaypointManager extends com.sinthoras.visualprospecting.integration.model.waypoints.WaypointManager { - - public NTNodeTrackerWaypointManager() { - super(ThaumcraftNodeLayerManager.instance, SupportedMods.TCNodeTracker); - } - - @Override - public void clearActiveWaypoint() { - TCNodeTracker.yMarker = -1; - } - - @Override - public void updateActiveWaypoint(Waypoint waypoint) { - TCNodeTracker.xMarker = waypoint.blockX; - TCNodeTracker.yMarker = waypoint.blockY; - TCNodeTracker.zMarker = waypoint.blockZ; - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/voxelmap/IWaypointManagerReflection.java b/src/main/java/com/sinthoras/visualprospecting/integration/voxelmap/IWaypointManagerReflection.java deleted file mode 100644 index 68d915a0..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/voxelmap/IWaypointManagerReflection.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.sinthoras.visualprospecting.integration.voxelmap; - -import java.lang.reflect.Method; - -import com.sinthoras.visualprospecting.VP; -import com.thevoxelbox.voxelmap.interfaces.IWaypointManager; - -public class IWaypointManagerReflection { - - private static Method getCurrentSubworldDescriptor; - - public static String getCurrentSubworldDescriptor(IWaypointManager obj, boolean arg) { - try { - return (String) getCurrentSubworldDescriptor.invoke(obj, arg); - } catch (Exception e) { - VP.error("Could not invoke IWaypointManager#if. If it failed due to a NullPointerException, look for an error message starting with \"Getting the method IWaypointManager#if failed\" further up."); - e.printStackTrace(); - } - return ""; - } - - static { - try { - getCurrentSubworldDescriptor = IWaypointManager.class.getMethod("if", boolean.class); - } catch (Exception e) { - VP.error("Getting the method IWaypointManager#if failed, any calls to IWaypointManagerReflection#getCurrentSubworldDescriptor will return an empty String."); - e.printStackTrace(); - } - } - -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/voxelmap/VoxelMapEventHandler.java b/src/main/java/com/sinthoras/visualprospecting/integration/voxelmap/VoxelMapEventHandler.java index da609e09..993ffff3 100644 --- a/src/main/java/com/sinthoras/visualprospecting/integration/voxelmap/VoxelMapEventHandler.java +++ b/src/main/java/com/sinthoras/visualprospecting/integration/voxelmap/VoxelMapEventHandler.java @@ -2,85 +2,78 @@ import java.util.TreeSet; +import net.minecraft.client.Minecraft; +import net.minecraft.client.entity.EntityClientPlayerMP; +import net.minecraft.item.ItemStack; + +import com.gtnewhorizons.navigator.api.voxelmap.VoxelMapWaypointManager; import com.sinthoras.visualprospecting.Config; import com.sinthoras.visualprospecting.Utils; import com.sinthoras.visualprospecting.database.OreVeinPosition; import com.sinthoras.visualprospecting.database.UndergroundFluidPosition; import com.sinthoras.visualprospecting.hooks.ProspectingNotificationEvent; -import com.thevoxelbox.voxelmap.interfaces.AbstractVoxelMap; -import com.thevoxelbox.voxelmap.interfaces.IWaypointManager; -import com.thevoxelbox.voxelmap.util.Waypoint; +import com.sinthoras.visualprospecting.integration.model.locations.UndergroundFluidLocation; import cpw.mods.fml.common.eventhandler.SubscribeEvent; -import gregtech.api.GregTech_API; -import net.minecraft.client.Minecraft; -import net.minecraft.client.entity.EntityClientPlayerMP; -import net.minecraft.item.ItemStack; -import net.minecraft.util.StatCollector; public class VoxelMapEventHandler { - - @SubscribeEvent - public void onVeinProspected(ProspectingNotificationEvent.OreVein event) { - if(event.isCanceled()) { - return; - } - - OreVeinPosition pos = event.getPosition(); - IWaypointManager waypointManager = AbstractVoxelMap.getInstance().getWaypointManager(); - short[] color = GregTech_API.sGeneratedMaterials[pos.veinType.primaryOreMeta].getRGBA(); - TreeSet dim = new TreeSet<>(); - dim.add(pos.dimensionId); - - waypointManager.addWaypoint(new Waypoint( - StatCollector.translateToLocal(pos.veinType.name), // name - pos.getBlockX(), // X - pos.getBlockZ(), // Z - getY(), // Y - Config.enableVoxelMapWaypointsByDefault, // enabled - (float) color[0] / 255.0f, // red - (float) color[1] / 255.0f, // green - (float) color[2] / 255.0f, // blue - "Pickaxe", // icon - IWaypointManagerReflection.getCurrentSubworldDescriptor(waypointManager, false), // world - dim)); // dimension - } - - @SubscribeEvent - public void onFluidProspected(ProspectingNotificationEvent.UndergroundFluid event) { - if(event.isCanceled()) { - return; - } - - UndergroundFluidPosition pos = event.getPosition(); - IWaypointManager waypointManager = AbstractVoxelMap.getInstance().getWaypointManager(); - int x = Utils.coordChunkToBlock(pos.chunkX); - int z = Utils.coordChunkToBlock(pos.chunkZ); - int color = pos.fluid.getColor(); - TreeSet dim = new TreeSet<>(); - dim.add(pos.dimensionId); - - waypointManager.addWaypoint(new Waypoint( - pos.fluid.getLocalizedName(), // name - x, // X - z, // Z - Minecraft.getMinecraft().theWorld.getHeightValue(x, z), // Y - Config.enableVoxelMapWaypointsByDefault, // enabled - (float) (color >> 16 & 0xFF) / 255.0f, // red - (float) (color >> 8 & 0xFF) / 255.0f, // green - (float) (color & 0xFF) / 255.0f, // blue - "Science", // icon - IWaypointManagerReflection.getCurrentSubworldDescriptor(waypointManager, false), // world - dim)); // dimension - } - - private static int getY() { - EntityClientPlayerMP player = Minecraft.getMinecraft().thePlayer; - ItemStack heldItem = player.getHeldItem(); - if(heldItem == null || !heldItem.getUnlocalizedName().contains("gt.detrav.metatool.01")) { - return (int) player.posY; - } - return 65; - } + @SubscribeEvent + public void onVeinProspected(ProspectingNotificationEvent.OreVein event) { + if (event.isCanceled()) { + return; + } + + OreVeinPosition pos = event.getPosition(); + short[] color = pos.veinType.primaryOre.getRGBA(); + TreeSet dim = new TreeSet<>(); + dim.add(pos.dimensionId); + VoxelMapWaypointManager.addVoxelMapWaypoint( + pos.veinType.getVeinName(), // name + pos.getBlockX(), // X + pos.getBlockZ(), // Z + getY(), // Y + Config.enableVoxelMapWaypointsByDefault, // enabled + (float) color[0] / 255.0f, // red + (float) color[1] / 255.0f, // green + (float) color[2] / 255.0f, // blue + "Pickaxe", // icon + dim); + } + + @SuppressWarnings("deprecation") + @SubscribeEvent + public void onFluidProspected(ProspectingNotificationEvent.UndergroundFluid event) { + if (event.isCanceled()) { + return; + } + + UndergroundFluidPosition pos = event.getPosition(); + int x = Utils.coordChunkToBlock(pos.chunkX); + int z = Utils.coordChunkToBlock(pos.chunkZ); + int color = UndergroundFluidLocation.resolveColor(pos.fluid); + TreeSet dim = new TreeSet<>(); + dim.add(pos.dimensionId); + + VoxelMapWaypointManager.addVoxelMapWaypoint( + pos.fluid.getLocalizedName(), // name + x, // X + z, // Z + Minecraft.getMinecraft().theWorld.getHeightValue(x, z), // Y + Config.enableVoxelMapWaypointsByDefault, // enabled + (float) (color >> 16 & 0xFF) / 255.0f, // red + (float) (color >> 8 & 0xFF) / 255.0f, // green + (float) (color & 0xFF) / 255.0f, // blue + "Science", // icon + dim); // dimension + } + + private static int getY() { + EntityClientPlayerMP player = Minecraft.getMinecraft().thePlayer; + ItemStack heldItem = player.getHeldItem(); + if (heldItem == null || !heldItem.getUnlocalizedName().contains("gt.detrav.metatool.01")) { + return (int) player.posY; + } + return 65; + } } diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/xaerominimap/XaeroMiniMapState.java b/src/main/java/com/sinthoras/visualprospecting/integration/xaerominimap/XaeroMiniMapState.java deleted file mode 100644 index 34332a99..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/xaerominimap/XaeroMiniMapState.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.sinthoras.visualprospecting.integration.xaerominimap; - -import com.sinthoras.visualprospecting.integration.xaerominimap.waypoints.OreVeinWaypointManager; -import com.sinthoras.visualprospecting.integration.xaerominimap.waypoints.ThaumcraftNodeWaypointManager; -import com.sinthoras.visualprospecting.integration.xaerominimap.waypoints.WaypointManager; - -import java.util.ArrayList; -import java.util.List; - -import static com.sinthoras.visualprospecting.Utils.isTCNodeTrackerInstalled; - -public class XaeroMiniMapState { - - public static XaeroMiniMapState instance = new XaeroMiniMapState(); - - public final List waypointManagers = new ArrayList<>(); - - public XaeroMiniMapState() { - waypointManagers.add(OreVeinWaypointManager.instance); - - if (isTCNodeTrackerInstalled()) { - waypointManagers.add(ThaumcraftNodeWaypointManager.instance); - } - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/xaerominimap/waypoints/OreVeinWaypointManager.java b/src/main/java/com/sinthoras/visualprospecting/integration/xaerominimap/waypoints/OreVeinWaypointManager.java deleted file mode 100644 index 441d6dc2..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/xaerominimap/waypoints/OreVeinWaypointManager.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.sinthoras.visualprospecting.integration.xaerominimap.waypoints; - -import com.sinthoras.visualprospecting.integration.model.layers.OreVeinLayerManager; -import com.sinthoras.visualprospecting.integration.model.waypoints.Waypoint; - -public class OreVeinWaypointManager extends WaypointManager { - - public static OreVeinWaypointManager instance = new OreVeinWaypointManager(); - - public OreVeinWaypointManager() { - super(OreVeinLayerManager.instance, WaypointType.ORE_VEINS_WAYPOINT); - } - - @Override - protected String getSymbol(Waypoint waypoint) { - return "!"; - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/xaerominimap/waypoints/ThaumcraftNodeWaypointManager.java b/src/main/java/com/sinthoras/visualprospecting/integration/xaerominimap/waypoints/ThaumcraftNodeWaypointManager.java deleted file mode 100644 index 2c30aa97..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/xaerominimap/waypoints/ThaumcraftNodeWaypointManager.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.sinthoras.visualprospecting.integration.xaerominimap.waypoints; - -import com.sinthoras.visualprospecting.integration.model.layers.ThaumcraftNodeLayerManager; -import com.sinthoras.visualprospecting.integration.model.waypoints.Waypoint; - -public class ThaumcraftNodeWaypointManager extends WaypointManager { - - public static ThaumcraftNodeWaypointManager instance = new ThaumcraftNodeWaypointManager(); - - public ThaumcraftNodeWaypointManager() { - super(ThaumcraftNodeLayerManager.instance, WaypointType.TC_NODES_WAYPOINT); - } - - @Override - protected String getSymbol(Waypoint waypoint) { - return "@"; - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/xaerominimap/waypoints/WaypointManager.java b/src/main/java/com/sinthoras/visualprospecting/integration/xaerominimap/waypoints/WaypointManager.java deleted file mode 100644 index 337051df..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/xaerominimap/waypoints/WaypointManager.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.sinthoras.visualprospecting.integration.xaerominimap.waypoints; - -import com.sinthoras.visualprospecting.Tags; -import com.sinthoras.visualprospecting.integration.model.SupportedMods; -import com.sinthoras.visualprospecting.integration.model.layers.WaypointProviderManager; -import com.sinthoras.visualprospecting.integration.model.waypoints.Waypoint; -import xaero.common.minimap.waypoints.WaypointsManager; - -import java.util.Hashtable; - -public class WaypointManager extends com.sinthoras.visualprospecting.integration.model.waypoints.WaypointManager { - - private static final Hashtable xWaypointTable = WaypointsManager.getCustomWaypoints(Tags.MODID); - - private WaypointWithDimension xWaypoint; - private final WaypointType waypointType; - - public WaypointManager(WaypointProviderManager layerManager, WaypointType waypointType) { - super(layerManager, SupportedMods.XaeroMiniMap); - this.waypointType = waypointType; - } - - @Override - public void clearActiveWaypoint() { - xWaypoint = null; - xWaypointTable.remove(waypointType.ordinal()); - } - - public boolean hasWaypoint() { - return xWaypoint != null; - } - - public WaypointWithDimension getXWaypoint() { - return xWaypoint; - } - - @Override - public void updateActiveWaypoint(Waypoint waypoint) { - if (!hasWaypoint() || waypoint.blockX != xWaypoint.getX() || waypoint.blockY != xWaypoint.getY() || - waypoint.blockZ != xWaypoint.getZ() || waypoint.dimensionId != xWaypoint.getDimID()) { - xWaypoint = new WaypointWithDimension(waypoint.blockX, waypoint.blockY, waypoint.blockZ, waypoint.label, getSymbol(waypoint), 15, waypoint.dimensionId); - xWaypointTable.put(waypointType.ordinal(), xWaypoint); - } - } - - protected String getSymbol(Waypoint waypoint) { - return waypoint.label.substring(0, 1); - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/xaerominimap/waypoints/WaypointType.java b/src/main/java/com/sinthoras/visualprospecting/integration/xaerominimap/waypoints/WaypointType.java deleted file mode 100644 index 7a9e69f5..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/xaerominimap/waypoints/WaypointType.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.sinthoras.visualprospecting.integration.xaerominimap.waypoints; - -public enum WaypointType { - ORE_VEINS_WAYPOINT, - TC_NODES_WAYPOINT -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/xaerominimap/waypoints/WaypointWithDimension.java b/src/main/java/com/sinthoras/visualprospecting/integration/xaerominimap/waypoints/WaypointWithDimension.java deleted file mode 100644 index d24c0e37..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/xaerominimap/waypoints/WaypointWithDimension.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.sinthoras.visualprospecting.integration.xaerominimap.waypoints; - -import xaero.common.minimap.waypoints.Waypoint; - -public class WaypointWithDimension extends Waypoint { - - private final int dimID; - private int currentDim; - - public WaypointWithDimension(int x, int y, int z, String name, String symbol, int color, int dimID) { - super(x, y, z, name, symbol, color); - this.dimID = dimID; - this.currentDim = dimID; - } - - public void notifyDimension(int newDimID) { - currentDim = newDimID; - } - - public int getDimID() { - return dimID; - } - - @Override - public boolean isDisabled() { - return super.isDisabled() || currentDim != dimID; - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/XaeroWorldMapState.java b/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/XaeroWorldMapState.java deleted file mode 100644 index 0683b4e5..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/XaeroWorldMapState.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.sinthoras.visualprospecting.integration.xaeroworldmap; - -import com.sinthoras.visualprospecting.integration.xaeroworldmap.buttons.LayerButton; -import com.sinthoras.visualprospecting.integration.xaeroworldmap.buttons.OreVeinButton; -import com.sinthoras.visualprospecting.integration.xaeroworldmap.buttons.ThaumcraftNodeButton; -import com.sinthoras.visualprospecting.integration.xaeroworldmap.buttons.UndergroundFluidButton; -import com.sinthoras.visualprospecting.integration.xaeroworldmap.renderers.*; -import com.sinthoras.visualprospecting.integration.xaerominimap.waypoints.OreVeinWaypointManager; -import com.sinthoras.visualprospecting.integration.xaerominimap.waypoints.ThaumcraftNodeWaypointManager; -import com.sinthoras.visualprospecting.integration.xaerominimap.waypoints.WaypointManager; - -import java.util.ArrayList; -import java.util.List; - -import static com.sinthoras.visualprospecting.Utils.isTCNodeTrackerInstalled; - -public class XaeroWorldMapState { - public static XaeroWorldMapState instance = new XaeroWorldMapState(); - - public final List buttons = new ArrayList<>(); - public final List renderers = new ArrayList<>(); - - public XaeroWorldMapState() { - buttons.add(OreVeinButton.instance); - renderers.add(OreVeinRenderer.instance); - - buttons.add(UndergroundFluidButton.instance); - renderers.add(UndergroundFluidChunkRenderer.instance); - renderers.add(UndergroundFluidRenderer.instance); - - if (isTCNodeTrackerInstalled()) { - buttons.add(ThaumcraftNodeButton.instance); - renderers.add(ThaumcraftNodeRenderer.instance); - } - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/buttons/LayerButton.java b/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/buttons/LayerButton.java deleted file mode 100644 index ac760ef8..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/buttons/LayerButton.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.sinthoras.visualprospecting.integration.xaeroworldmap.buttons; - -import com.sinthoras.visualprospecting.integration.model.SupportedMods; -import com.sinthoras.visualprospecting.integration.model.buttons.ButtonManager; -import net.minecraft.util.ResourceLocation; - -public class LayerButton extends com.sinthoras.visualprospecting.integration.model.buttons.LayerButton { - - private final ButtonManager manager; - private SizedGuiTexturedButton button; - - public final ResourceLocation textureLocation; - - public LayerButton(ButtonManager manager) { - super(manager, SupportedMods.XaeroWorldMap); - this.manager = manager; - textureLocation = new ResourceLocation("xaeroworldmap", "textures/" + getIconName() + ".png"); - } - - @Override - public void updateState(boolean active) { - if (button != null) { - button.setActive(active); - } - } - - public void setButton(SizedGuiTexturedButton button) { - this.button = button; - updateState(manager.isActive()); - } - - public String getButtonTextKey() { - return manager.getButtonTextKey(); - } - - public String getIconName() { - return manager.getIconName(); - } - - public void toggle() { - manager.toggle(); - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/buttons/OreVeinButton.java b/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/buttons/OreVeinButton.java deleted file mode 100644 index 952a3c17..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/buttons/OreVeinButton.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.sinthoras.visualprospecting.integration.xaeroworldmap.buttons; - -import com.sinthoras.visualprospecting.integration.model.buttons.OreVeinButtonManager; - -public class OreVeinButton extends LayerButton { - - public static final OreVeinButton instance = new OreVeinButton(); - - public OreVeinButton() { - super(OreVeinButtonManager.instance); - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/buttons/SizedGuiTexturedButton.java b/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/buttons/SizedGuiTexturedButton.java deleted file mode 100644 index f29b5ad2..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/buttons/SizedGuiTexturedButton.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.sinthoras.visualprospecting.integration.xaeroworldmap.buttons; - -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.Gui; -import net.minecraft.client.gui.GuiButton; -import net.minecraft.util.ResourceLocation; -import org.lwjgl.opengl.GL11; -import xaero.map.gui.CursorBox; -import xaero.map.gui.GuiTexturedButton; - -import java.util.function.Consumer; - -public class SizedGuiTexturedButton extends GuiTexturedButton { - - private static final int BUTTON_WIDTH = 20; - private static final int BUTTON_HEIGHT = 20; - private static final int ICON_WIDTH = 16; - private static final int ICON_HEIGHT = 16; - - protected boolean active; - - public SizedGuiTexturedButton(int x, int y, ResourceLocation texture, Consumer action, CursorBox tooltip) { - super(x, y, BUTTON_WIDTH, BUTTON_HEIGHT, 0, 0, ICON_WIDTH, ICON_HEIGHT, texture, action, tooltip); - active = false; - } - - @Override - public void drawButton(Minecraft minecraft, int mouseX, int mouseY) { - minecraft.getTextureManager().bindTexture(texture); - int iconX = xPosition + width / 2 - textureW / 2; - int iconY = yPosition + height / 2 - textureH / 2; - if (enabled) { - if (active) { - if (isMouseOver(mouseX, mouseY)) { - iconY--; - GL11.glColor4f(0.5F, 0.9F, 0.5F, 1.0F); - } - else { - GL11.glColor4f(0.5882F, 0.9882F, 0.5882F, 1.0F); - } - } - else { - if (isMouseOver(mouseX, mouseY)) { - iconY--; - GL11.glColor4f(0.9F, 0.9F, 0.9F, 1.0F); - } else { - GL11.glColor4f(0.9882F, 0.9882F, 0.9882F, 1.0F); - } - } - } else { - GL11.glColor4f(0.25F, 0.25F, 0.25F, 1.0F); - } - - Gui.func_146110_a(iconX, iconY, textureX, textureY, textureW, textureH, textureW, textureH); - } - - public boolean isMouseOver(int mouseX, int mouseY) { - return mouseX >= xPosition && mouseY >= yPosition && mouseX < xPosition + width && mouseY < yPosition + height; - } - - public void setActive(boolean active) { - this.active = active; - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/buttons/ThaumcraftNodeButton.java b/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/buttons/ThaumcraftNodeButton.java deleted file mode 100644 index ff065052..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/buttons/ThaumcraftNodeButton.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.sinthoras.visualprospecting.integration.xaeroworldmap.buttons; - -import com.sinthoras.visualprospecting.integration.model.buttons.ThaumcraftNodeButtonManager; - -public class ThaumcraftNodeButton extends LayerButton { - - public static final ThaumcraftNodeButton instance = new ThaumcraftNodeButton(); - - public ThaumcraftNodeButton() { - super(ThaumcraftNodeButtonManager.instance); - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/buttons/UndergroundFluidButton.java b/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/buttons/UndergroundFluidButton.java deleted file mode 100644 index a3155643..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/buttons/UndergroundFluidButton.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.sinthoras.visualprospecting.integration.xaeroworldmap.buttons; - -import com.sinthoras.visualprospecting.integration.model.buttons.UndergroundFluidButtonManager; - -public class UndergroundFluidButton extends LayerButton { - - public static final UndergroundFluidButton instance = new UndergroundFluidButton(); - - public UndergroundFluidButton() { - super(UndergroundFluidButtonManager.instance); - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/renderers/InteractableLayerRenderer.java b/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/renderers/InteractableLayerRenderer.java deleted file mode 100644 index 93e0b8af..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/renderers/InteractableLayerRenderer.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.sinthoras.visualprospecting.integration.xaeroworldmap.renderers; - -import com.sinthoras.visualprospecting.integration.model.layers.WaypointProviderManager; -import com.sinthoras.visualprospecting.integration.model.locations.ILocationProvider; -import com.sinthoras.visualprospecting.integration.xaeroworldmap.rendersteps.InteractableRenderStep; -import com.sinthoras.visualprospecting.integration.xaeroworldmap.rendersteps.RenderStep; -import net.minecraft.client.gui.GuiScreen; - -import java.util.List; - -public abstract class InteractableLayerRenderer extends LayerRenderer { - private double mouseXForRender; - private double mouseYForRender; - protected WaypointProviderManager manager; - protected InteractableRenderStep hovered; - - public InteractableLayerRenderer(WaypointProviderManager manager) { - super(manager); - this.manager = manager; - hovered = null; - } - - @Override - protected abstract List generateRenderSteps(List visibleElements); - - public void updateHovered(double mouseX, double mouseY, double cameraX, double cameraZ, double scale) { - mouseXForRender = mouseX - cameraX; - mouseYForRender = mouseY - cameraZ; - for (RenderStep step : renderStepsReversed) { - if (step instanceof InteractableRenderStep && ((InteractableRenderStep) step).isMouseOver(mouseXForRender, mouseYForRender, scale)) { - hovered = (InteractableRenderStep) step; - return; - } - } - hovered = null; - } - - public void drawTooltip(GuiScreen gui, double scale, int scaleAdj) { - if (hovered != null) { - hovered.drawTooltip(gui, mouseXForRender, mouseYForRender, scale, scaleAdj); - } - } - - public void doActionKeyPress() { - if (manager.isLayerActive() && hovered != null) { - hovered.onActionButton(); - manager.forceRefresh(); - } - } - - public void doDoubleClick() { - if (hovered != null) { - if (hovered.getLocationProvider().isActiveAsWaypoint()) { - manager.clearActiveWaypoint(); - } else { - manager.setActiveWaypoint(hovered.getLocationProvider().toWaypoint()); - } - } - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/renderers/LayerRenderer.java b/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/renderers/LayerRenderer.java deleted file mode 100644 index ea7e4d77..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/renderers/LayerRenderer.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.sinthoras.visualprospecting.integration.xaeroworldmap.renderers; - -import com.sinthoras.visualprospecting.integration.model.SupportedMods; -import com.sinthoras.visualprospecting.integration.model.layers.LayerManager; -import com.sinthoras.visualprospecting.integration.model.locations.ILocationProvider; -import com.sinthoras.visualprospecting.integration.xaeroworldmap.rendersteps.RenderStep; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -public abstract class LayerRenderer extends com.sinthoras.visualprospecting.integration.model.layers.LayerRenderer { - - private final LayerManager manager; - protected List renderSteps = new ArrayList<>(); - protected List renderStepsReversed = new ArrayList<>(); - - public LayerRenderer(LayerManager manager) { - super(manager, SupportedMods.XaeroWorldMap); - this.manager = manager; - } - - public List getRenderSteps() { - return renderSteps; - } - - public List getRenderStepsReversed() { - return renderStepsReversed; - } - - public boolean isLayerActive() { - return manager.isLayerActive(); - } - - @Override - public void updateVisibleElements(List visibleElements) { - //noinspection unchecked - renderSteps = (List) generateRenderSteps(visibleElements); - renderStepsReversed = new ArrayList<>(renderSteps); - Collections.reverse(renderStepsReversed); - } - - protected abstract List generateRenderSteps(List visibleElements); -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/renderers/OreVeinRenderer.java b/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/renderers/OreVeinRenderer.java deleted file mode 100644 index 798cfbcb..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/renderers/OreVeinRenderer.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.sinthoras.visualprospecting.integration.xaeroworldmap.renderers; - -import com.sinthoras.visualprospecting.integration.model.layers.OreVeinLayerManager; -import com.sinthoras.visualprospecting.integration.model.locations.ILocationProvider; -import com.sinthoras.visualprospecting.integration.model.locations.OreVeinLocation; -import com.sinthoras.visualprospecting.integration.xaeroworldmap.rendersteps.OreVeinRenderStep; - -import java.util.ArrayList; -import java.util.List; - -public class OreVeinRenderer extends InteractableLayerRenderer { - public static OreVeinRenderer instance = new OreVeinRenderer(); - - public OreVeinRenderer() { - super(OreVeinLayerManager.instance); - } - - @Override - protected List generateRenderSteps(List visibleElements) { - final List renderSteps = new ArrayList<>(); - visibleElements.stream() - .map(element -> (OreVeinLocation) element) - .forEach(location -> renderSteps.add(new OreVeinRenderStep(location))); - return renderSteps; - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/renderers/ThaumcraftNodeRenderer.java b/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/renderers/ThaumcraftNodeRenderer.java deleted file mode 100644 index 2336923c..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/renderers/ThaumcraftNodeRenderer.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.sinthoras.visualprospecting.integration.xaeroworldmap.renderers; - -import com.sinthoras.visualprospecting.integration.model.layers.ThaumcraftNodeLayerManager; -import com.sinthoras.visualprospecting.integration.model.locations.ILocationProvider; -import com.sinthoras.visualprospecting.integration.model.locations.ThaumcraftNodeLocation; -import com.sinthoras.visualprospecting.integration.xaeroworldmap.rendersteps.ThaumcraftNodeRenderStep; - -import java.util.ArrayList; -import java.util.List; - -public class ThaumcraftNodeRenderer extends InteractableLayerRenderer { - public static ThaumcraftNodeRenderer instance = new ThaumcraftNodeRenderer(); - - public ThaumcraftNodeRenderer() { - super(ThaumcraftNodeLayerManager.instance); - } - - @Override - protected List generateRenderSteps(List visibleElements) { - final List renderSteps = new ArrayList<>(); - visibleElements.stream() - .map(element -> (ThaumcraftNodeLocation) element) - .forEach(location -> renderSteps.add(new ThaumcraftNodeRenderStep(location))); - return renderSteps; - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/renderers/UndergroundFluidChunkRenderer.java b/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/renderers/UndergroundFluidChunkRenderer.java deleted file mode 100644 index f3ebe260..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/renderers/UndergroundFluidChunkRenderer.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.sinthoras.visualprospecting.integration.xaeroworldmap.renderers; - -import com.sinthoras.visualprospecting.integration.model.layers.UndergroundFluidChunkLayerManager; -import com.sinthoras.visualprospecting.integration.model.locations.ILocationProvider; -import com.sinthoras.visualprospecting.integration.model.locations.UndergroundFluidChunkLocation; -import com.sinthoras.visualprospecting.integration.xaeroworldmap.rendersteps.UndergroundFluidChunkRenderStep; - -import java.util.ArrayList; -import java.util.List; - -public class UndergroundFluidChunkRenderer extends LayerRenderer { - public static UndergroundFluidChunkRenderer instance = new UndergroundFluidChunkRenderer(); - - public UndergroundFluidChunkRenderer() { - super(UndergroundFluidChunkLayerManager.instance); - } - - @Override - protected List generateRenderSteps(List visibleElements) { - final List renderSteps = new ArrayList<>(); - visibleElements.stream() - .map(element -> (UndergroundFluidChunkLocation) element) - .forEach(location -> renderSteps.add(new UndergroundFluidChunkRenderStep(location))); - return renderSteps; - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/renderers/UndergroundFluidRenderer.java b/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/renderers/UndergroundFluidRenderer.java deleted file mode 100644 index fe63816c..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/renderers/UndergroundFluidRenderer.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.sinthoras.visualprospecting.integration.xaeroworldmap.renderers; - -import com.sinthoras.visualprospecting.integration.model.layers.UndergroundFluidLayerManager; -import com.sinthoras.visualprospecting.integration.model.locations.ILocationProvider; -import com.sinthoras.visualprospecting.integration.model.locations.UndergroundFluidLocation; -import com.sinthoras.visualprospecting.integration.xaeroworldmap.rendersteps.UndergroundFluidRenderStep; - -import java.util.ArrayList; -import java.util.List; - -public class UndergroundFluidRenderer extends LayerRenderer { - public static UndergroundFluidRenderer instance = new UndergroundFluidRenderer(); - - public UndergroundFluidRenderer() { - super(UndergroundFluidLayerManager.instance); - } - - @Override - protected List generateRenderSteps(List visibleElements) { - final List renderSteps = new ArrayList<>(); - visibleElements.stream() - .map(element -> (UndergroundFluidLocation) element) - .forEach(location -> renderSteps.add(new UndergroundFluidRenderStep(location))); - return renderSteps; - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/rendersteps/InteractableRenderStep.java b/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/rendersteps/InteractableRenderStep.java deleted file mode 100644 index 93660d1c..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/rendersteps/InteractableRenderStep.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.sinthoras.visualprospecting.integration.xaeroworldmap.rendersteps; - -import com.sinthoras.visualprospecting.integration.model.locations.IWaypointAndLocationProvider; -import net.minecraft.client.gui.GuiScreen; - -public interface InteractableRenderStep extends RenderStep { - - boolean isMouseOver(double mouseX, double mouseY, double scale); - - void drawTooltip(GuiScreen gui, double mouseX, double mouseY, double scale, int scaleAdj); - - void onActionButton(); - - IWaypointAndLocationProvider getLocationProvider(); - -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/rendersteps/OreVeinRenderStep.java b/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/rendersteps/OreVeinRenderStep.java deleted file mode 100644 index 690350df..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/rendersteps/OreVeinRenderStep.java +++ /dev/null @@ -1,115 +0,0 @@ -package com.sinthoras.visualprospecting.integration.xaeroworldmap.rendersteps; - -import com.sinthoras.visualprospecting.Config; -import com.sinthoras.visualprospecting.Tags; -import com.sinthoras.visualprospecting.Utils; -import com.sinthoras.visualprospecting.integration.DrawUtils; -import com.sinthoras.visualprospecting.integration.model.locations.IWaypointAndLocationProvider; -import com.sinthoras.visualprospecting.integration.model.locations.OreVeinLocation; -import net.minecraft.client.gui.GuiScreen; -import net.minecraft.client.resources.I18n; -import net.minecraft.init.Blocks; -import net.minecraft.util.IIcon; -import net.minecraft.util.ResourceLocation; -import org.lwjgl.opengl.GL11; - -import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.List; - -public class OreVeinRenderStep implements InteractableRenderStep { - - private final OreVeinLocation oreVeinLocation; - private final ResourceLocation depletedTextureLocation = new ResourceLocation(Tags.MODID, "textures/depleted.png"); - private final IIcon blockStoneIcon = Blocks.stone.getIcon(0, 0); - private final double iconSize = 32; - private double iconX; - private double iconY; - - public OreVeinRenderStep(OreVeinLocation veinPosition) { - oreVeinLocation = veinPosition; - } - - @Override - public void draw(@Nullable GuiScreen gui, double cameraX, double cameraZ, double scale) { - final double iconSizeHalf = iconSize / 2; - final double scaleForGui = Math.max(1, scale); - iconX = (oreVeinLocation.getBlockX() - 0.5 - cameraX) * scaleForGui - iconSizeHalf; - iconY = (oreVeinLocation.getBlockZ() - 0.5 - cameraZ) * scaleForGui - iconSizeHalf; - - GL11.glPushMatrix(); - GL11.glTranslated(oreVeinLocation.getBlockX() - 0.5 - cameraX, oreVeinLocation.getBlockZ() - 0.5 - cameraZ, 0); - GL11.glScaled(1 / scaleForGui, 1 / scaleForGui, 1); - DrawUtils.drawQuad(blockStoneIcon, -iconSizeHalf, -iconSizeHalf, iconSize, iconSize, 0xFFFFFF, 255); - - DrawUtils.drawQuad(oreVeinLocation.getIconFromPrimaryOre(), -iconSizeHalf, -iconSizeHalf, iconSize, iconSize, oreVeinLocation.getColor(), 255); - - if (!oreVeinLocation.drawSearchHighlight() || oreVeinLocation.isDepleted()) { - DrawUtils.drawGradientRect(-iconSizeHalf, -iconSizeHalf, iconSizeHalf, iconSizeHalf, 0, 0x96000000, 0x96000000); - if (oreVeinLocation.isDepleted()) { - DrawUtils.drawQuad(depletedTextureLocation, -iconSizeHalf, -iconSizeHalf, iconSize, iconSize, 0xFFFFFF, 255); - } - } - - if (gui != null && scale >= Utils.journeyMapScaleToLinear(Config.minZoomLevelForOreLabel) && !oreVeinLocation.isDepleted()) { - final int fontColor = oreVeinLocation.drawSearchHighlight() ? 0xFFFFFFFF : 0xFF7F7F7F; - String text = I18n.format(oreVeinLocation.getName()); - DrawUtils.drawSimpleLabel(gui, text, 0, -iconSizeHalf - gui.mc.fontRenderer.FONT_HEIGHT - 5, fontColor, 0xB4000000, true); - } - - if (oreVeinLocation.isActiveAsWaypoint()) { - final double thickness = iconSize / 8; - final int color = 0xFFFFD700; - DrawUtils.drawGradientRect(-iconSizeHalf - thickness, -iconSizeHalf - thickness, iconSizeHalf, -iconSizeHalf, 0, color, color); - DrawUtils.drawGradientRect(iconSizeHalf, -iconSizeHalf - thickness, iconSizeHalf + thickness, iconSizeHalf, 0, color, color); - DrawUtils.drawGradientRect(-iconSizeHalf, iconSizeHalf, iconSizeHalf + thickness, iconSizeHalf + thickness, 0, color, color); - DrawUtils.drawGradientRect(-iconSizeHalf - thickness, -iconSizeHalf, -iconSizeHalf, iconSizeHalf + thickness, 0, color, color); - } - - GL11.glPopMatrix(); - } - - @Override - public boolean isMouseOver(double mouseX, double mouseY, double scale) { - final double scaleForGui = Math.max(1, scale); - mouseX = mouseX * scaleForGui; - mouseY = mouseY * scaleForGui; - return mouseX >= iconX && mouseY >= iconY && mouseX <= iconX + iconSize && mouseY <= iconY + iconSize; - } - - @Override - public void drawTooltip(GuiScreen gui, double mouseX, double mouseY, double scale, int scaleAdj) { - //correct for gl matrix differences - mouseX = mouseX * scale + (gui.mc.displayWidth >> 1); - mouseY = mouseY * scale + (gui.mc.displayHeight >> 1); - - final List tooltip = new ArrayList<>(); - if (oreVeinLocation.isDepleted()) { - tooltip.add(oreVeinLocation.getDepletedHint()); - } - if (oreVeinLocation.isActiveAsWaypoint()) { - tooltip.add(oreVeinLocation.getActiveWaypointHint()); - } - tooltip.add(oreVeinLocation.getName()); - if (!oreVeinLocation.isDepleted()) { - tooltip.addAll(oreVeinLocation.getMaterialNames()); - } - tooltip.add(oreVeinLocation.getToggleDepletedHint()); - - GL11.glPushMatrix(); - - DrawUtils.drawSimpleTooltip(gui, tooltip, mouseX / scaleAdj + 6, mouseY / scaleAdj - 12, 0xFFFFFFFF, 0x86000000); - - GL11.glPopMatrix(); - } - - @Override - public void onActionButton() { - oreVeinLocation.toggleOreVein(); - } - - @Override - public IWaypointAndLocationProvider getLocationProvider() { - return oreVeinLocation; - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/rendersteps/RenderStep.java b/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/rendersteps/RenderStep.java deleted file mode 100644 index 762f6da6..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/rendersteps/RenderStep.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.sinthoras.visualprospecting.integration.xaeroworldmap.rendersteps; - -import net.minecraft.client.gui.GuiScreen; - -import javax.annotation.Nullable; - -public interface RenderStep { - void draw(@Nullable GuiScreen gui, double cameraX, double cameraZ, double scale); -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/rendersteps/ThaumcraftNodeRenderStep.java b/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/rendersteps/ThaumcraftNodeRenderStep.java deleted file mode 100644 index 126b2bb9..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/rendersteps/ThaumcraftNodeRenderStep.java +++ /dev/null @@ -1,165 +0,0 @@ -package com.sinthoras.visualprospecting.integration.xaeroworldmap.rendersteps; - -import com.sinthoras.visualprospecting.Tags; -import com.sinthoras.visualprospecting.integration.DrawUtils; -import com.sinthoras.visualprospecting.integration.model.layers.ThaumcraftNodeLayerManager; -import com.sinthoras.visualprospecting.integration.model.locations.IWaypointAndLocationProvider; -import com.sinthoras.visualprospecting.integration.model.locations.ThaumcraftNodeLocation; -import net.minecraft.client.gui.GuiScreen; -import net.minecraft.util.ResourceLocation; -import org.lwjgl.opengl.GL11; -import thaumcraft.api.aspects.Aspect; -import thaumcraft.client.lib.UtilsFX; - -import javax.annotation.Nullable; - -public class ThaumcraftNodeRenderStep implements InteractableRenderStep { - - private static final ResourceLocation markedTextureLocation = new ResourceLocation(Tags.MODID, "textures/node_marked.png"); - private static final ResourceLocation unmarkedTextureLocation = new ResourceLocation(Tags.MODID, "textures/node_unmarked.png"); - - private final ThaumcraftNodeLocation thaumcraftNodeLocation; - - private double centerPixelX = 0; - private double centerPixelY = 0; - private double clickableRadiusPixelSquared = 0; - - public ThaumcraftNodeRenderStep(ThaumcraftNodeLocation thaumcraftNodeLocation) { - this.thaumcraftNodeLocation = thaumcraftNodeLocation; - } - - @Override - public void draw(@Nullable GuiScreen gui, double cameraX, double cameraZ, double scale) { - final double borderSize = 44; - final double borderSizeHalf = borderSize / 2; - final double scaleForGui = Math.max(0.5, scale); - centerPixelX = (thaumcraftNodeLocation.getBlockX() - cameraX) * scaleForGui; - centerPixelY = (thaumcraftNodeLocation.getBlockZ() - cameraZ) * scaleForGui; - clickableRadiusPixelSquared = borderSizeHalf * borderSizeHalf; - - GL11.glPushMatrix(); - GL11.glTranslated(thaumcraftNodeLocation.getBlockX() - cameraX, thaumcraftNodeLocation.getBlockZ() - cameraZ, 0); - GL11.glScaled(1 / scaleForGui, 1 / scaleForGui, 1); - - final int alpha = 204; - DrawUtils.drawQuad(thaumcraftNodeLocation.isActiveAsWaypoint() ? markedTextureLocation : unmarkedTextureLocation, -borderSizeHalf, -borderSizeHalf, borderSize, borderSize, 0xFFFFFF, alpha); - - final int aspectPixelDiameter = 32; - DrawUtils.drawAspect(0, 0, aspectPixelDiameter, thaumcraftNodeLocation.getStrongestAspect(), 0); - - GL11.glPopMatrix(); - } - - @Override - public boolean isMouseOver(double mouseX, double mouseY, double scale) { - final double scaleForGui = Math.max(0.5, scale); - final double deltaX = mouseX * scaleForGui - centerPixelX; - final double deltaY = mouseY * scaleForGui - centerPixelY; - return deltaX * deltaX + deltaY * deltaY <= clickableRadiusPixelSquared; - } - - @Override - public void drawTooltip(GuiScreen gui, double mouseX, double mouseY, double scale, int scaleAdj) { - - GL11.glPushMatrix(); - - mouseX = (mouseX * scale + (gui.mc.displayWidth >> 1)) / scaleAdj; - mouseY = (mouseY * scale + (gui.mc.displayHeight >> 1)) / scaleAdj; - - final boolean isWaypoint = thaumcraftNodeLocation.isActiveAsWaypoint(); - final String asWaypoint = thaumcraftNodeLocation.getActiveWaypointHint(); - final String title = thaumcraftNodeLocation.getTitle(); - final String nodeDescription = thaumcraftNodeLocation.getDescription(); - final String deleteHint = thaumcraftNodeLocation.getDeleteHint(); - - int maxTextWidth = Math.max(Math.max(gui.mc.fontRenderer.getStringWidth(title), gui.mc.fontRenderer.getStringWidth(nodeDescription)), gui.mc.fontRenderer.getStringWidth(deleteHint)); - if (isWaypoint) { - maxTextWidth = Math.max(maxTextWidth, gui.mc.fontRenderer.getStringWidth(asWaypoint)); - } - if (gui.mc.fontRenderer.getBidiFlag()) { - maxTextWidth = (int) Math.ceil(maxTextWidth * 1.25f); - } - - final int aspectRows = (thaumcraftNodeLocation.getAspects().size() + 4) / 5; // Equivalent to Math.ceil(size / 5) - final int aspectColumns = Math.min(thaumcraftNodeLocation.getAspects().size(), 5); - - int pixelX = (int) (mouseX + 12); - int pixelY = (int) (mouseY - 12); - final int tooltipHeight = (isWaypoint ? 44 : 32) + aspectRows * 16; - final int tooltipWidth = Math.max(aspectColumns * 16, maxTextWidth); - if (pixelX + tooltipWidth > gui.mc.displayWidth) { - pixelX -= 28 + tooltipWidth; - } - if (pixelY + tooltipHeight + 6 > gui.mc.displayHeight) { - pixelY = gui.mc.displayHeight - tooltipHeight - 6; - } - - GL11.glDisable(GL11.GL_DEPTH_TEST); - - // Draw background - final int backgroundColor = 0xF0100010; - DrawUtils.drawGradientRect(pixelX - 3, pixelY - 4, pixelX + tooltipWidth + 3, pixelY - 3, backgroundColor, backgroundColor); - DrawUtils.drawGradientRect(pixelX - 3, pixelY + tooltipHeight + 3, pixelX + tooltipWidth + 3, pixelY + tooltipHeight + 4, backgroundColor, backgroundColor); - DrawUtils.drawGradientRect(pixelX - 3, pixelY - 3, pixelX + tooltipWidth + 3, pixelY + tooltipHeight + 3, backgroundColor, backgroundColor); - DrawUtils.drawGradientRect(pixelX - 4, pixelY - 3, pixelX - 3, pixelY + tooltipHeight + 3, backgroundColor, backgroundColor); - DrawUtils.drawGradientRect(pixelX + tooltipWidth + 3, pixelY - 3, pixelX + tooltipWidth + 4, pixelY + tooltipHeight + 3, backgroundColor, backgroundColor); - - // Draw text - int offset = 0; - if (gui.mc.fontRenderer.getBidiFlag()) { - if (isWaypoint) { - final int asWaypointWidth = (int) Math.ceil(gui.mc.fontRenderer.getStringWidth(asWaypoint) * 1.1f); - gui.mc.fontRenderer.drawString(title, pixelX + tooltipWidth - asWaypointWidth, pixelY, 0xFFFFFFFF); - offset += 12; - } - final int titleWidth = (int) Math.ceil(gui.mc.fontRenderer.getStringWidth(title) * 1.1f); - gui.mc.fontRenderer.drawString(title, pixelX + tooltipWidth - titleWidth, pixelY + offset, 0xFFFFFFFF); - offset += 12; - final int nodeDescriptonWidth = (int) Math.ceil(gui.mc.fontRenderer.getStringWidth(nodeDescription) * 1.1f); - gui.mc.fontRenderer.drawString(nodeDescription, pixelX + nodeDescriptonWidth - titleWidth, pixelY + offset, 0xFFFFFFFF); - - final int deleteHintWidth = (int) Math.ceil(gui.mc.fontRenderer.getStringWidth(deleteHint) * 1.1f); - gui.mc.fontRenderer.drawString(deleteHint, pixelX + tooltipWidth - deleteHintWidth, pixelY + aspectRows * 16 + offset + 12, 0xFFFFFFFF); - } else { - if (isWaypoint) { - gui.mc.fontRenderer.drawString(asWaypoint, pixelX, pixelY, 0xFFFFFFFF); - offset += 12; - } - gui.mc.fontRenderer.drawString(title, pixelX, pixelY + offset, 0xFFFFFFFF); - offset += 12; - gui.mc.fontRenderer.drawString(nodeDescription, pixelX, pixelY + offset, 0xFFFFFFFF); - - gui.mc.fontRenderer.drawString(deleteHint, pixelX, pixelY + aspectRows * 16 + offset + 12, 0xFFFFFFFF); - } - - // Draw aspects - int aspectX = 0; - int aspectY = 0; - - - for (Aspect aspect : thaumcraftNodeLocation.getAspects().getAspectsSortedAmount()) { - GL11.glPushMatrix(); - UtilsFX.drawTag(pixelX + aspectX * 16, pixelY + aspectY * 16 + offset + 10, aspect, thaumcraftNodeLocation.getAspects().getAmount(aspect), 0, 0.01, 1, 1, false); - GL11.glPopMatrix(); - ++aspectX; - if (aspectX >= 5) { - aspectX = 0; - ++aspectY; - } - } - - GL11.glDisable(GL11.GL_LIGHTING); - - GL11.glPopMatrix(); - } - - @Override - public void onActionButton() { - ThaumcraftNodeLayerManager.instance.deleteNode(thaumcraftNodeLocation); - } - - @Override - public IWaypointAndLocationProvider getLocationProvider() { - return thaumcraftNodeLocation; - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/rendersteps/UndergroundFluidChunkRenderStep.java b/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/rendersteps/UndergroundFluidChunkRenderStep.java deleted file mode 100644 index d2a1d595..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/rendersteps/UndergroundFluidChunkRenderStep.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.sinthoras.visualprospecting.integration.xaeroworldmap.rendersteps; - -import com.sinthoras.visualprospecting.Config; -import com.sinthoras.visualprospecting.Utils; -import com.sinthoras.visualprospecting.VP; -import com.sinthoras.visualprospecting.integration.DrawUtils; -import com.sinthoras.visualprospecting.integration.model.locations.UndergroundFluidChunkLocation; -import net.minecraft.client.gui.GuiScreen; -import org.lwjgl.opengl.GL11; - -import javax.annotation.Nullable; - -public class UndergroundFluidChunkRenderStep implements RenderStep { - private final UndergroundFluidChunkLocation undergroundFluidChunkLocation; - - public UndergroundFluidChunkRenderStep(UndergroundFluidChunkLocation location) { - undergroundFluidChunkLocation = location; - } - - private String getFluidAmountFormatted() { - if (undergroundFluidChunkLocation.getFluidAmount() >= 1000) { - return (undergroundFluidChunkLocation.getFluidAmount() / 1000) + "kL"; - } - return undergroundFluidChunkLocation.getFluidAmount() + "L"; - } - - @Override - public void draw(@Nullable GuiScreen gui, double cameraX, double cameraZ, double scale) { - if (undergroundFluidChunkLocation.getFluidAmount() > 0 && scale >= Utils.journeyMapScaleToLinear(Config.minZoomLevelForUndergroundFluidDetails)) { - GL11.glPushMatrix(); - GL11.glTranslated(undergroundFluidChunkLocation.getBlockX() - 0.5 - cameraX, undergroundFluidChunkLocation.getBlockZ() - 0.5 - cameraZ, 0); - - float alpha = ((float) (undergroundFluidChunkLocation.getFluidAmount() - undergroundFluidChunkLocation.getMinAmountInField())) / - (undergroundFluidChunkLocation.getMaxAmountInField() - undergroundFluidChunkLocation.getMinAmountInField() + 1); - alpha *= alpha * 204; - int fluidColor = undergroundFluidChunkLocation.getFluid().getColor() | (((int) alpha) << 24); - DrawUtils.drawGradientRect(0, 0, VP.chunkWidth, VP.chunkDepth, 0, fluidColor, fluidColor); - - if (undergroundFluidChunkLocation.getFluidAmount() >= undergroundFluidChunkLocation.getMaxAmountInField()) { - final int borderColor = 0xCCFFD700; - DrawUtils.drawGradientRect(0, 0, 15, 1, 0, borderColor, borderColor); - DrawUtils.drawGradientRect(15, 0, 16, 15, 0, borderColor, borderColor); - DrawUtils.drawGradientRect(1, 15, 16, 16, 0, borderColor, borderColor); - DrawUtils.drawGradientRect(0, 1, 1, 16, 0, borderColor, borderColor); - } - - GL11.glScaled(1 / scale, 1 / scale, 1); - if (gui != null) { - DrawUtils.drawSimpleLabel(gui, getFluidAmountFormatted(), 13, 13, 0xFFFFFFFF, 0xB4000000, false); - } - GL11.glPopMatrix(); - } - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/rendersteps/UndergroundFluidRenderStep.java b/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/rendersteps/UndergroundFluidRenderStep.java deleted file mode 100644 index 8a1e4207..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/integration/xaeroworldmap/rendersteps/UndergroundFluidRenderStep.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.sinthoras.visualprospecting.integration.xaeroworldmap.rendersteps; - -import com.sinthoras.visualprospecting.VP; -import com.sinthoras.visualprospecting.integration.DrawUtils; -import com.sinthoras.visualprospecting.integration.model.locations.UndergroundFluidLocation; -import net.minecraft.client.gui.GuiScreen; -import org.lwjgl.opengl.GL11; - -import javax.annotation.Nullable; - -public class UndergroundFluidRenderStep implements RenderStep { - - private final UndergroundFluidLocation undergroundFluidLocation; - - public UndergroundFluidRenderStep(UndergroundFluidLocation undergroundFluid) { - undergroundFluidLocation = undergroundFluid; - } - - @Override - public void draw(@Nullable GuiScreen gui, double cameraX, double cameraZ, double scale) { - final int maxAmountInField = undergroundFluidLocation.getMaxProduction(); - // < 0.5 scale is when scaling issues show up - if (maxAmountInField > 0 && scale >= 0.5) { - GL11.glPushMatrix(); - GL11.glTranslated(undergroundFluidLocation.getBlockX() - 0.5 - cameraX, undergroundFluidLocation.getBlockZ() - 0.5 - cameraZ, 0); - - final int borderColor = undergroundFluidLocation.getFluid().getColor() | 0xCC000000; - final double lenX = VP.undergroundFluidSizeChunkX * VP.chunkWidth; - final double lenZ = VP.undergroundFluidSizeChunkZ * VP.chunkDepth; - DrawUtils.drawGradientRect(0, 0, lenX, 2, 0, borderColor, borderColor); - DrawUtils.drawGradientRect(lenX, 0, lenX + 2, lenZ, 0, borderColor, borderColor); - DrawUtils.drawGradientRect(2, lenZ, lenX + 2, lenZ + 2, 0, borderColor, borderColor); - DrawUtils.drawGradientRect(0, 2, 2, lenZ + 2, 0, borderColor, borderColor); - - // min scale that journeymap can go to - if (scale >= 1 && gui != null) { - GL11.glScaled(1 / scale, 1 / scale, 1); - final String label = undergroundFluidLocation.getMinProduction() + "L - " + maxAmountInField + "L " + undergroundFluidLocation.getFluid().getLocalizedName(); - DrawUtils.drawSimpleLabel(gui, label, VP.chunkWidth * scale, 0, 0xFFFFFFFF, 0xB4000000, false); - } - - GL11.glPopMatrix(); - } - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/item/ProspectorsLog.java b/src/main/java/com/sinthoras/visualprospecting/item/ProspectorsLog.java index 1c7f368d..77a312c2 100644 --- a/src/main/java/com/sinthoras/visualprospecting/item/ProspectorsLog.java +++ b/src/main/java/com/sinthoras/visualprospecting/item/ProspectorsLog.java @@ -1,12 +1,7 @@ package com.sinthoras.visualprospecting.item; -import com.sinthoras.visualprospecting.Tags; -import com.sinthoras.visualprospecting.VP; -import com.sinthoras.visualprospecting.database.TransferCache; -import com.sinthoras.visualprospecting.task.SnapshotDownloadTask; -import com.sinthoras.visualprospecting.task.SnapshotUploadTask; -import com.sinthoras.visualprospecting.task.TaskManager; -import gregtech.api.GregTech_API; +import java.util.List; + import net.minecraft.client.resources.I18n; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; @@ -18,7 +13,14 @@ import net.minecraft.util.IChatComponent; import net.minecraft.world.World; -import java.util.List; +import com.sinthoras.visualprospecting.Tags; +import com.sinthoras.visualprospecting.VP; +import com.sinthoras.visualprospecting.database.TransferCache; +import com.sinthoras.visualprospecting.task.SnapshotDownloadTask; +import com.sinthoras.visualprospecting.task.SnapshotUploadTask; +import com.sinthoras.visualprospecting.task.TaskManager; + +import gregtech.api.GregTechAPI; public class ProspectorsLog extends Item { @@ -27,57 +29,56 @@ public class ProspectorsLog extends Item { public ProspectorsLog() { maxStackSize = 1; setUnlocalizedName("visualprospecting.prospectorslog"); - setCreativeTab(GregTech_API.TAB_GREGTECH); + setCreativeTab(GregTechAPI.TAB_GREGTECH); setTextureName(Tags.MODID + ":prospectorslog"); } @Override public ItemStack onItemRightClick(ItemStack item, World world, EntityPlayer player) { - if(isFilledLog(item) == false) { + if (!isFilledLog(item)) { final NBTTagCompound compound = new NBTTagCompound(); compound.setString(Tags.PROSPECTORSLOG_AUTHOR, player.getDisplayName()); compound.setString(Tags.PROSPECTORSLOG_AUTHOR_ID, player.getPersistentID().toString()); item.setTagCompound(compound); if (world.isRemote) { - TaskManager.instance.addTask(new SnapshotUploadTask()); - } - else { + TaskManager.CLIENT_INSTANCE.addTask(new SnapshotUploadTask()); + } else { final int random = VP.randomGeneration.nextInt(1000); - if(random < 5) { + if (random < 5) { final String localizationKey = "item.visualprospecting.prospectorslog.creation.fail" + random; final IChatComponent notification = new ChatComponentTranslation(localizationKey); notification.getChatStyle().setItalic(true); notification.getChatStyle().setColor(EnumChatFormatting.GRAY); player.addChatMessage(notification); player.destroyCurrentEquippedItem(); - } - else { - final IChatComponent notification = new ChatComponentTranslation("item.visualprospecting.prospectorslog.creation.begin"); + } else { + final IChatComponent notification = new ChatComponentTranslation( + "item.visualprospecting.prospectorslog.creation.begin"); notification.getChatStyle().setItalic(true); notification.getChatStyle().setColor(EnumChatFormatting.GRAY); player.addChatMessage(notification); } } - } - else if(world.isRemote == false){ + } else if (!world.isRemote) { final NBTTagCompound compound = item.getTagCompound(); final String authorUuid = compound.getString(Tags.PROSPECTORSLOG_AUTHOR_ID); - if(authorUuid.equals(player.getPersistentID().toString()) == false) { - final int random = VP.randomGeneration.nextInt(TransferCache.instance.isClientDataAvailable(authorUuid) ? 1000 : 5); - if(random < 5) { + if (!authorUuid.equals(player.getPersistentID().toString())) { + final int random = VP.randomGeneration + .nextInt(TransferCache.instance.isClientDataAvailable(authorUuid) ? 1000 : 5); + if (random < 5) { final String localizationKey = "item.visualprospecting.prospectorslog.reading.fail" + random; final IChatComponent notification = new ChatComponentTranslation(localizationKey); notification.getChatStyle().setItalic(true); notification.getChatStyle().setColor(EnumChatFormatting.GRAY); player.addChatMessage(notification); player.destroyCurrentEquippedItem(); - } - else { - final IChatComponent notification = new ChatComponentTranslation("item.visualprospecting.prospectorslog.reading.begin"); + } else { + final IChatComponent notification = new ChatComponentTranslation( + "item.visualprospecting.prospectorslog.reading.begin"); notification.getChatStyle().setItalic(true); notification.getChatStyle().setColor(EnumChatFormatting.GRAY); player.addChatMessage(notification); - TaskManager.instance.addTask(new SnapshotDownloadTask(authorUuid, (EntityPlayerMP) player)); + TaskManager.SERVER_INSTANCE.addTask(new SnapshotDownloadTask(authorUuid, (EntityPlayerMP) player)); } } } @@ -85,18 +86,21 @@ else if(world.isRemote == false){ } @Override - public void addInformation(ItemStack item, EntityPlayer player, List infoList, boolean ignored) { - if(isFilledLog(item)) { + public void addInformation(ItemStack item, EntityPlayer player, List infoList, boolean ignored) { + if (isFilledLog(item)) { final NBTTagCompound compound = item.getTagCompound(); - infoList.add(I18n.format("item.visualprospecting.prospectorslog.author", compound.getString(Tags.PROSPECTORSLOG_AUTHOR))); - } - else { + infoList.add( + I18n.format( + "item.visualprospecting.prospectorslog.author", + compound.getString(Tags.PROSPECTORSLOG_AUTHOR))); + } else { infoList.add(I18n.format("item.visualprospecting.prospectorslog.empty")); } } private boolean isFilledLog(ItemStack item) { final NBTTagCompound compound = item.getTagCompound(); - return compound != null && compound.hasKey(Tags.PROSPECTORSLOG_AUTHOR) && compound.hasKey(Tags.PROSPECTORSLOG_AUTHOR_ID); + return compound != null && compound.hasKey(Tags.PROSPECTORSLOG_AUTHOR) + && compound.hasKey(Tags.PROSPECTORSLOG_AUTHOR_ID); } } diff --git a/src/main/java/com/sinthoras/visualprospecting/mixinplugin/Mixin.java b/src/main/java/com/sinthoras/visualprospecting/mixinplugin/Mixin.java deleted file mode 100644 index 289f2a4e..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/mixinplugin/Mixin.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.sinthoras.visualprospecting.mixinplugin; - -import static com.sinthoras.visualprospecting.mixinplugin.TargetedMod.*; - -import cpw.mods.fml.relauncher.FMLLaunchHandler; -import java.util.Arrays; -import java.util.List; - -public enum Mixin { - - // - // IMPORTANT: Do not make any references to any mod from this file. This file is loaded quite early on and if - // you refer to other mods you load them as well. The consequence is: You can't inject any previously loaded classes! - // Exception: Tags.java, as long as it is used for Strings only! - // - - WorldGenContainerMixin("bartworks.WorldGenContainerMixin", BARTWORKS), - - GT_Worldgenerator_SpaceMixin("galacticgreg.GT_Worldgenerator_SpaceMixin", GALACTICGREG), - - GT_Block_Ores_AbstractMixin("gregtech.GT_Block_Ores_AbstractMixin", GREGTECH), - GT_MetaTileEntity_AdvSeismicProspectorMixin("gregtech.GT_MetaTileEntity_AdvSeismicProspectorMixin", GREGTECH), - GT_MetaTileEntity_ScannerMixin("gregtech.GT_MetaTileEntity_ScannerMixin", GREGTECH), - GT_WorldGenContainerMixin("gregtech.WorldGenContainerMixin", GREGTECH), - - ItemOreFinderToolMixin("ifu.ItemOreFinderToolMixin", IFU), - - FullscreenMixin("journeymap.FullscreenMixin", Side.CLIENT, JOURNEYMAP), - FullscreenActionsMixin("journeymap.FullscreenActionsMixin", Side.CLIENT, JOURNEYMAP), - MiniMapMixin("journeymap.MiniMapMixin", Side.CLIENT, JOURNEYMAP), - RenderWaypointBeaconMixin("journeymap.RenderWaypointBeaconMixin", Side.CLIENT, JOURNEYMAP), - WaypointManagerMixin("journeymap.WaypointManagerMixin", Side.CLIENT, JOURNEYMAP), - - GuiMainMixin("tcnodetracker.GuiMainMixin", Side.CLIENT, TCNODETRACKER), - - GuiMapMixin("xaerosworldmap.GuiMapMixin", Side.CLIENT, XAEROWORLDMAP), - WaypointsIngameRendererMixin("xaerosminimap.WaypointsIngameRendererMixin", Side.CLIENT, XAEROMINIMAP), - MinimapRendererMixin("xaerosminimap.MinimapRendererMixin", Side.CLIENT, XAEROMINIMAP, XAEROWORLDMAP), - // used to enable the stencil buffer for on-minimap rendering - ForgeHooksClientMixin("minecraft.ForgeHooksClientMixin", Side.CLIENT, XAEROMINIMAP, XAEROWORLDMAP), - - ItemEditableBookMixin("minecraft.ItemEditableBookMixin", VANILLA); - - public final String mixinClass; - public final List targetedMods; - private final Side side; - - Mixin(String mixinClass, Side side, TargetedMod... targetedMods) { - this.mixinClass = mixinClass; - this.targetedMods = Arrays.asList(targetedMods); - this.side = side; - } - - Mixin(String mixinClass, TargetedMod... targetedMods) { - this.mixinClass = mixinClass; - this.targetedMods = Arrays.asList(targetedMods); - this.side = Side.BOTH; - } - - public boolean shouldLoad(List loadedMods) { - return (side == Side.BOTH - || side == Side.SERVER && FMLLaunchHandler.side().isServer() - || side == Side.CLIENT && FMLLaunchHandler.side().isClient()) - && loadedMods.containsAll(targetedMods); - } -} - -enum Side { - BOTH, - CLIENT, - SERVER; -} diff --git a/src/main/java/com/sinthoras/visualprospecting/mixinplugin/MixinPlugin.java b/src/main/java/com/sinthoras/visualprospecting/mixinplugin/MixinPlugin.java deleted file mode 100644 index 4a0ebdaa..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/mixinplugin/MixinPlugin.java +++ /dev/null @@ -1,123 +0,0 @@ -package com.sinthoras.visualprospecting.mixinplugin; - -import static com.sinthoras.visualprospecting.mixinplugin.TargetedMod.VANILLA; -import static java.nio.file.Files.walk; - -import com.sinthoras.visualprospecting.Tags; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; -import net.minecraft.launchwrapper.Launch; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.spongepowered.asm.lib.tree.ClassNode; -import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; -import org.spongepowered.asm.mixin.extensibility.IMixinInfo; -import ru.timeconqueror.spongemixins.MinecraftURLClassPath; - -public class MixinPlugin implements IMixinConfigPlugin { - - private static final Logger LOG = LogManager.getLogger(Tags.MODID + " mixins"); - private static final Path MODS_DIRECTORY_PATH = new File(Launch.minecraftHome, "mods/").toPath(); - - @Override - public void onLoad(String mixinPackage) { - - } - - @Override - public String getRefMapperConfig() { - return null; - } - - @Override - public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { - return true; - } - - @Override - public void acceptTargets(Set myTargets, Set otherTargets) { - - } - - // This method return a List of mixins. Every mixins in this list will be loaded. - @Override - public List getMixins() { - final boolean isDevelopmentEnvironment = (boolean) Launch.blackboard.get("fml.deobfuscatedEnvironment"); - - List loadedMods = Arrays.stream(TargetedMod.values()) - .filter(mod -> mod == VANILLA - || (mod.loadInDevelopment && isDevelopmentEnvironment) - || loadJarOf(mod)) - .collect(Collectors.toList()); - - for (TargetedMod mod : TargetedMod.values()) { - if(loadedMods.contains(mod)) { - LOG.info("Found " + mod.modName + "! Integrating now..."); - } - else { - LOG.info("Could not find " + mod.modName + "! Skipping integration...."); - } - } - - List mixins = new ArrayList<>(); - for (Mixin mixin : Mixin.values()) { - if (mixin.shouldLoad(loadedMods)) { - mixins.add(mixin.mixinClass); - LOG.debug("Loading mixin: " + mixin.mixinClass); - } - } - return mixins; - } - - private boolean loadJarOf(final TargetedMod mod) { - try { - File jar = findJarOf(mod); - if(jar == null) { - LOG.info("Jar not found for " + mod); - return false; - } - - LOG.info("Attempting to add " + jar + " to the URL Class Path"); - if(!jar.exists()) { - throw new FileNotFoundException(jar.toString()); - } - MinecraftURLClassPath.addJar(jar); - return true; - } - catch (Exception e) { - e.printStackTrace(); - return false; - } - } - - public static File findJarOf(final TargetedMod mod) { - try { - return walk(MODS_DIRECTORY_PATH) - .filter(mod::isMatchingJar) - .map(Path::toFile) - .findFirst() - .orElse(null); - } - catch (IOException e) { - e.printStackTrace(); - return null; - } - } - - @Override - public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { - - } - - @Override - public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { - - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/mixinplugin/Mixins.java b/src/main/java/com/sinthoras/visualprospecting/mixinplugin/Mixins.java new file mode 100644 index 00000000..79f76946 --- /dev/null +++ b/src/main/java/com/sinthoras/visualprospecting/mixinplugin/Mixins.java @@ -0,0 +1,39 @@ +package com.sinthoras.visualprospecting.mixinplugin; + +import static com.sinthoras.visualprospecting.mixinplugin.TargetedMod.*; + +import javax.annotation.Nonnull; + +import com.gtnewhorizon.gtnhmixins.builders.IMixins; +import com.gtnewhorizon.gtnhmixins.builders.MixinBuilder; + +public enum Mixins implements IMixins { + + // spotless:off + // Gregtech mixins + MTEAdvSeismicProspectorMixin("gregtech.MTEAdvSeismicProspectorMixin", GT5U), + + // Vanilla Mixins + MINECRAFT(new MixinBuilder() + .addCommonMixins( + "minecraft.MinecraftServerAccessor", + "minecraft.ItemEditableBookMixin") + .setPhase(Phase.EARLY)); + // spotless:on + + private final MixinBuilder builder; + + Mixins(MixinBuilder builder) { + this.builder = builder; + } + + Mixins(String mixinClass, TargetedMod requiredMod) { + this.builder = new MixinBuilder().addCommonMixins(mixinClass).addRequiredMod(requiredMod).setPhase(Phase.LATE); + } + + @Nonnull + @Override + public MixinBuilder getBuilder() { + return this.builder; + } +} diff --git a/src/main/java/com/sinthoras/visualprospecting/mixinplugin/TargetedMod.java b/src/main/java/com/sinthoras/visualprospecting/mixinplugin/TargetedMod.java index 84813210..ba4823c1 100644 --- a/src/main/java/com/sinthoras/visualprospecting/mixinplugin/TargetedMod.java +++ b/src/main/java/com/sinthoras/visualprospecting/mixinplugin/TargetedMod.java @@ -1,51 +1,25 @@ package com.sinthoras.visualprospecting.mixinplugin; -import com.google.common.io.Files; -import java.nio.file.Path; +import javax.annotation.Nonnull; -public enum TargetedMod { +import com.gtnewhorizon.gtnhmixins.builders.ITargetMod; +import com.gtnewhorizon.gtnhmixins.builders.TargetModBuilder; - // - // IMPORTANT: Do not make any references to any mod from this file. This file is loaded quite early on and if - // you refer to other mods you load them as well. The consequence is: You can't inject any previously loaded classes! - // Exception: Tags.java, as long as it is used for Strings only! - // +public enum TargetedMod implements ITargetMod { - // Replace with your injected mods here, but always keep VANILLA: - VANILLA("Minecraft", "unused", true), - GREGTECH("GregTech", "gregtech", true), - JOURNEYMAP("JourneyMap", "journeymap-1.7.10", true), - XAEROWORLDMAP("Xaero's World Map", "XaerosWorldMap", true), - XAEROMINIMAP("Xaero's Minimap", "Xaeros_Minimap", true), - TCNODETRACKER("TCNodeTracker", "tcnodetracker-1.7.10", true), - IFU("I will find you", "ifu", false), - BARTWORKS("Bartworks", "bartworks", false), - GALACTICGREG("GalacticGreg", "GalacticGreg", false); + BARTWORKS("com.github.bartimaeusnek.bartworks.ASM.BWCorePlugin", "bartworks"), + GALACTICGREG(null, "galacticgreg"), + GT5U(null, "gregtech"); // Also matches GT6. - public final String modName; - public final String jarNamePrefixLowercase; - // Optional dependencies can be omitted in development. Especially skipping GT5U will drastically speed up your game start! - public final boolean loadInDevelopment; + private final TargetModBuilder builder; - TargetedMod(String modName, String jarNamePrefix, boolean loadInDevelopment) { - this.modName = modName; - this.jarNamePrefixLowercase = jarNamePrefix.toLowerCase(); - this.loadInDevelopment = loadInDevelopment; - } - - public boolean isMatchingJar(Path path) { - final String pathString = path.toString(); - final String nameLowerCase = Files.getNameWithoutExtension(pathString).toLowerCase(); - final String fileExtension = Files.getFileExtension(pathString); - - return nameLowerCase.startsWith(jarNamePrefixLowercase) && ("jar".equals(fileExtension) || "litemod".equals(fileExtension)); + TargetedMod(String coreModClass, String modId) { + this.builder = new TargetModBuilder().setCoreModClass(coreModClass).setModId(modId); } + @Nonnull @Override - public String toString() { - return "TargetedMod{" + - "modName='" + modName + '\'' + - ", jarNamePrefixLowercase='" + jarNamePrefixLowercase + '\'' + - '}'; + public TargetModBuilder getBuilder() { + return builder; } } diff --git a/src/main/java/com/sinthoras/visualprospecting/mixinplugin/VisualProspectingEarlyMixinLoader.java b/src/main/java/com/sinthoras/visualprospecting/mixinplugin/VisualProspectingEarlyMixinLoader.java new file mode 100644 index 00000000..0c1cb1b8 --- /dev/null +++ b/src/main/java/com/sinthoras/visualprospecting/mixinplugin/VisualProspectingEarlyMixinLoader.java @@ -0,0 +1,48 @@ +package com.sinthoras.visualprospecting.mixinplugin; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.gtnewhorizon.gtnhmixins.IEarlyMixinLoader; +import com.gtnewhorizon.gtnhmixins.builders.IMixins; + +import cpw.mods.fml.relauncher.IFMLLoadingPlugin; + +@IFMLLoadingPlugin.MCVersion("1.7.10") +@IFMLLoadingPlugin.TransformerExclusions("com.sinthoras.visualprospecting.mixinplugin") +public class VisualProspectingEarlyMixinLoader implements IFMLLoadingPlugin, IEarlyMixinLoader { + + @Override + public String[] getASMTransformerClass() { + return null; + } + + @Override + public String getModContainerClass() { + return null; + } + + @Override + public String getSetupClass() { + return null; + } + + @Override + public void injectData(Map data) {} + + @Override + public String getAccessTransformerClass() { + return null; + } + + @Override + public String getMixinConfig() { + return "mixins.visualprospecting.early.json"; + } + + @Override + public List getMixins(Set loadedCoreMods) { + return IMixins.getEarlyMixins(Mixins.class, loadedCoreMods); + } +} diff --git a/src/main/java/com/sinthoras/visualprospecting/mixinplugin/VisualProspectingLateMixinLoader.java b/src/main/java/com/sinthoras/visualprospecting/mixinplugin/VisualProspectingLateMixinLoader.java new file mode 100644 index 00000000..24da6916 --- /dev/null +++ b/src/main/java/com/sinthoras/visualprospecting/mixinplugin/VisualProspectingLateMixinLoader.java @@ -0,0 +1,23 @@ +package com.sinthoras.visualprospecting.mixinplugin; + +import java.util.List; +import java.util.Set; + +import com.gtnewhorizon.gtnhmixins.ILateMixinLoader; +import com.gtnewhorizon.gtnhmixins.LateMixin; +import com.gtnewhorizon.gtnhmixins.builders.IMixins; + +@LateMixin +public class VisualProspectingLateMixinLoader implements ILateMixinLoader { + + @Override + public String getMixinConfig() { + return "mixins.visualprospecting.late.json"; + } + + @Override + public List getMixins(Set loadedMods) { + return IMixins.getLateMixins(Mixins.class, loadedMods); + } + +} diff --git a/src/main/java/com/sinthoras/visualprospecting/mixins/bartworks/WorldGenContainerMixin.java b/src/main/java/com/sinthoras/visualprospecting/mixins/bartworks/WorldGenContainerMixin.java deleted file mode 100644 index 884b8b70..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/mixins/bartworks/WorldGenContainerMixin.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.sinthoras.visualprospecting.mixins.bartworks; - -import com.github.bartimaeusnek.bartworks.system.oregen.BW_OreLayer; -import com.github.bartimaeusnek.bartworks.system.oregen.BW_WordGenerator; -import com.sinthoras.visualprospecting.Utils; -import com.sinthoras.visualprospecting.database.ServerCache; -import net.minecraft.world.World; -import net.minecraft.world.chunk.IChunkProvider; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; - -import java.util.Random; - -@Mixin(BW_WordGenerator.WorldGenContainer.class) -public class WorldGenContainerMixin { - - @Redirect(method = "run", - at = @At(value = "INVOKE", - target = "Lcom/github/bartimaeusnek/bartworks/system/oregen/BW_OreLayer;executeWorldgen(Lnet/minecraft/world/World;Ljava/util/Random;Ljava/lang/String;IIILnet/minecraft/world/chunk/IChunkProvider;Lnet/minecraft/world/chunk/IChunkProvider;)Z"), - remap = false, - require = 1) - private boolean onOreVeinGenerationAttempt(BW_OreLayer worldGen, World world, Random random, String biome, int dimensionType, int blockX, int blockZ, IChunkProvider chunkGenerator, IChunkProvider chunkProvider) { - final boolean oreVeinPlaced = worldGen.executeWorldgen(world, random, biome, Integer.MIN_VALUE, blockX, blockZ, chunkGenerator, chunkProvider); - if(oreVeinPlaced) { - ServerCache.instance.notifyOreVeinGeneration(world.provider.dimensionId, Utils.mapToCenterOreChunkCoord(Utils.coordBlockToChunk(blockX)), Utils.mapToCenterOreChunkCoord(Utils.coordBlockToChunk(blockZ)), worldGen.mWorldGenName); - } - return oreVeinPlaced; - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/mixins/early/minecraft/ItemEditableBookMixin.java b/src/main/java/com/sinthoras/visualprospecting/mixins/early/minecraft/ItemEditableBookMixin.java new file mode 100644 index 00000000..2e5bebbb --- /dev/null +++ b/src/main/java/com/sinthoras/visualprospecting/mixins/early/minecraft/ItemEditableBookMixin.java @@ -0,0 +1,54 @@ +package com.sinthoras.visualprospecting.mixins.early.minecraft; + +import java.util.List; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemEditableBook; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.world.World; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import com.sinthoras.visualprospecting.Tags; +import com.sinthoras.visualprospecting.VP; +import com.sinthoras.visualprospecting.database.OreVeinPosition; +import com.sinthoras.visualprospecting.database.ServerCache; +import com.sinthoras.visualprospecting.database.UndergroundFluidPosition; +import com.sinthoras.visualprospecting.teams.TeamProspectionDispatcher; + +@Mixin(value = ItemEditableBook.class) +public class ItemEditableBookMixin { + + @Inject(method = "onItemRightClick", at = @At("HEAD")) + private void visualprospecting$onItemRightClick(ItemStack itemStack, World world, EntityPlayer entityPlayer, + CallbackInfoReturnable cir) { + if (!world.isRemote) { + final NBTTagCompound compound = itemStack.getTagCompound(); + if (compound != null && compound.hasKey(Tags.VISUALPROSPECTING_FLAG)) { + final int dimensionId = compound.getInteger(Tags.PROSPECTION_DIMENSION_ID); + final int blockX = compound.getInteger(Tags.PROSPECTION_BLOCK_X); + final int blockZ = compound.getInteger(Tags.PROSPECTION_BLOCK_Z); + final int blockRadius = compound.getInteger(Tags.PROSPECTION_ORE_RADIUS); + if (world.provider.dimensionId == dimensionId) { + final List foundOreVeins = ServerCache.instance + .prospectOreBlockRadius(dimensionId, blockX, blockZ, blockRadius); + final List foundUndergroundFluids = ServerCache.instance + .prospectUndergroundFluidBlockRadius( + world, + blockX, + blockZ, + VP.undergroundFluidChunkProspectingBlockRadius); + TeamProspectionDispatcher.deliverProspectingResults( + (EntityPlayerMP) entityPlayer, + foundOreVeins, + foundUndergroundFluids); + } + } + } + } +} diff --git a/src/main/java/com/sinthoras/visualprospecting/mixins/early/minecraft/MinecraftServerAccessor.java b/src/main/java/com/sinthoras/visualprospecting/mixins/early/minecraft/MinecraftServerAccessor.java new file mode 100644 index 00000000..9403ea71 --- /dev/null +++ b/src/main/java/com/sinthoras/visualprospecting/mixins/early/minecraft/MinecraftServerAccessor.java @@ -0,0 +1,14 @@ +package com.sinthoras.visualprospecting.mixins.early.minecraft; + +import net.minecraft.server.MinecraftServer; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(MinecraftServer.class) +public interface MinecraftServerAccessor { + + @Accessor + void setUserMessage(String msg); + +} diff --git a/src/main/java/com/sinthoras/visualprospecting/mixins/galacticgreg/GT_Worldgenerator_SpaceMixin.java b/src/main/java/com/sinthoras/visualprospecting/mixins/galacticgreg/GT_Worldgenerator_SpaceMixin.java deleted file mode 100644 index cd834c46..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/mixins/galacticgreg/GT_Worldgenerator_SpaceMixin.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.sinthoras.visualprospecting.mixins.galacticgreg; - -import bloodasp.galacticgreg.GT_Worldgenerator_Space; -import com.sinthoras.visualprospecting.Utils; -import com.sinthoras.visualprospecting.database.ServerCache; -import gregtech.api.world.GT_Worldgen; -import net.minecraft.world.World; -import net.minecraft.world.chunk.IChunkProvider; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; - -import java.util.Random; - -@Mixin(GT_Worldgenerator_Space.class) -public class GT_Worldgenerator_SpaceMixin { - - @Redirect(method = "Generate_OreVeins", - at = @At(value = "INVOKE", - target = "Lgregtech/api/world/GT_Worldgen;executeWorldgen(Lnet/minecraft/world/World;Ljava/util/Random;Ljava/lang/String;IIILnet/minecraft/world/chunk/IChunkProvider;Lnet/minecraft/world/chunk/IChunkProvider;)Z"), - remap = false, - require = 1) - private boolean onOreVeinGenerated(GT_Worldgen worldGen, World world, Random random, String biome, int dimensionType, int blockX, int blockZ, IChunkProvider chunkGenerator, IChunkProvider chunkProvider) { - final boolean oreVeinPlaced = worldGen.executeWorldgen(world, random, biome, Integer.MIN_VALUE, blockX, blockZ, chunkGenerator, chunkProvider); - if(oreVeinPlaced) { - ServerCache.instance.notifyOreVeinGeneration(world.provider.dimensionId, Utils.mapToCenterOreChunkCoord(Utils.coordBlockToChunk(blockX)), Utils.mapToCenterOreChunkCoord(Utils.coordBlockToChunk(blockZ)), worldGen.mWorldGenName); - } - return oreVeinPlaced; - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/mixins/gregtech/GT_Block_Ores_AbstractMixin.java b/src/main/java/com/sinthoras/visualprospecting/mixins/gregtech/GT_Block_Ores_AbstractMixin.java deleted file mode 100644 index 1975cec2..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/mixins/gregtech/GT_Block_Ores_AbstractMixin.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.sinthoras.visualprospecting.mixins.gregtech; - -import com.sinthoras.visualprospecting.database.ClientCache; -import gregtech.api.items.GT_Generic_Block; -import gregtech.common.blocks.GT_Block_Ores_Abstract; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.world.World; -import org.spongepowered.asm.mixin.Mixin; - -@Mixin(value = GT_Block_Ores_Abstract.class, remap = false) -public class GT_Block_Ores_AbstractMixin extends GT_Generic_Block { - - protected GT_Block_Ores_AbstractMixin() { - super(null, null, null); - } - - @Override - public boolean onBlockActivated(World world, int blockX, int blockY, int blockZ, EntityPlayer player, int side, float offsetX, float offsetY, float offsetZ) { - ClientCache.instance.onOreInteracted(world, blockX, blockY, blockZ, player); - return super.onBlockActivated(world, blockX, blockY, blockZ, player, side, offsetX, offsetY, offsetZ); - } - - @Override - public void onBlockClicked(World world, int blockX, int blockY, int blockZ, EntityPlayer player) { - ClientCache.instance.onOreInteracted(world, blockX, blockY, blockZ, player); - super.onBlockClicked(world, blockX, blockY, blockZ, player); - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/mixins/gregtech/GT_MetaTileEntity_AdvSeismicProspectorMixin.java b/src/main/java/com/sinthoras/visualprospecting/mixins/gregtech/GT_MetaTileEntity_AdvSeismicProspectorMixin.java deleted file mode 100644 index ffe5358a..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/mixins/gregtech/GT_MetaTileEntity_AdvSeismicProspectorMixin.java +++ /dev/null @@ -1,97 +0,0 @@ -package com.sinthoras.visualprospecting.mixins.gregtech; - -import com.sinthoras.visualprospecting.ServerTranslations; -import com.sinthoras.visualprospecting.VP; -import com.sinthoras.visualprospecting.Tags; -import com.sinthoras.visualprospecting.Utils; -import com.sinthoras.visualprospecting.database.ServerCache; -import com.sinthoras.visualprospecting.database.UndergroundFluidPosition; -import gregtech.api.enums.ItemList; -import gregtech.api.enums.Materials; -import gregtech.api.interfaces.ITexture; -import gregtech.api.interfaces.tileentity.IGregTechTileEntity; -import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicMachine; -import gregtech.api.util.GT_Utility; -import gregtech.common.tileentities.machines.basic.GT_MetaTileEntity_AdvSeismicProspector; -import ic2.core.Ic2Items; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.Blocks; -import net.minecraft.item.Item; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NBTTagCompound; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Overwrite; -import org.spongepowered.asm.mixin.Shadow; - -import java.util.List; - -import static gregtech.api.util.GT_Utility.ItemNBT.getNBT; -import static gregtech.api.util.GT_Utility.ItemNBT.setNBT; - -@Mixin(value = GT_MetaTileEntity_AdvSeismicProspector.class, remap = false) -public abstract class GT_MetaTileEntity_AdvSeismicProspectorMixin extends GT_MetaTileEntity_BasicMachine { - - @Shadow(remap = false) - boolean ready = false; - - @Shadow(remap = false) - int radius; - - public GT_MetaTileEntity_AdvSeismicProspectorMixin() { - super(0, "", "", 0, 0, "", 0, 0, "", "", (ITexture[]) null); - } - - /** - * @author SinTh0r4s - * @reason Redirect game mechanics onto VP database - */ - @Overwrite(remap = false) - @Override - public boolean onRightclick(IGregTechTileEntity aBaseMetaTileEntity, EntityPlayer aPlayer) { - if (aBaseMetaTileEntity.isServerSide()) { - ItemStack aStack = aPlayer.getCurrentEquippedItem(); - - if (!ready && (GT_Utility.consumeItems(aPlayer, aStack, Item.getItemFromBlock(Blocks.tnt), 16) - || GT_Utility.consumeItems(aPlayer, aStack, Ic2Items.industrialTnt.getItem(), 8) - || GT_Utility.consumeItems(aPlayer, aStack, Materials.Glyceryl, 4) - || GT_Utility.consumeItems(aPlayer, aStack, ItemList.Block_Powderbarrel.getItem(), 2))) { - - this.ready = true; - this.mMaxProgresstime = (aPlayer.capabilities.isCreativeMode ? 20 : 800); - - } - else if (ready && mMaxProgresstime == 0 - && aStack != null && aStack.stackSize == 1 - && aStack.getItem() == ItemList.Tool_DataStick.getItem()) { - this.ready = false; - - final NBTTagCompound compound = getNBT(aStack); - compound.setString(Tags.BOOK_TITLE, "Raw Prospection Data"); - compound.setBoolean(Tags.VISUALPROSPECTING_FLAG, true); - compound.setByte(Tags.PROSPECTION_TIER, mTier); - compound.setInteger(Tags.PROSPECTION_DIMENSION_ID, getBaseMetaTileEntity().getWorld().provider.dimensionId); - compound.setInteger(Tags.PROSPECTION_BLOCK_X, getBaseMetaTileEntity().getXCoord()); - compound.setInteger(Tags.PROSPECTION_BLOCK_Y, getBaseMetaTileEntity().getYCoord()); - compound.setInteger(Tags.PROSPECTION_BLOCK_Z, getBaseMetaTileEntity().getZCoord()); - compound.setInteger(Tags.PROSPECTION_ORE_RADIUS, radius); - - final List undergroundFluidPositions = ServerCache.instance.prospectUndergroundFluidBlockRadius(aPlayer.worldObj, getBaseMetaTileEntity().getXCoord(), getBaseMetaTileEntity().getZCoord(), VP.undergroundFluidChunkProspectingBlockRadius); - compound.setInteger(Tags.PROSPECTION_NUMBER_OF_UNDERGROUND_FLUID, undergroundFluidPositions.size()); - - String[] fluidStrings = new String[9]; - final int minUndergroundFluidX = Utils.mapToCornerUndergroundFluidChunkCoord(Utils.coordBlockToChunk(getBaseMetaTileEntity().getXCoord() - VP.undergroundFluidChunkProspectingBlockRadius)); - final int minUndergroundFluidZ = Utils.mapToCornerUndergroundFluidChunkCoord(Utils.coordBlockToChunk(getBaseMetaTileEntity().getZCoord() - VP.undergroundFluidChunkProspectingBlockRadius)); - for(UndergroundFluidPosition undergroundFluidPosition : undergroundFluidPositions) { - final int offsetUndergroundFluidX = (Utils.mapToCornerUndergroundFluidChunkCoord(undergroundFluidPosition.chunkX) - minUndergroundFluidX) >> 3; - final int offsetUndergroundFluidZ = (Utils.mapToCornerUndergroundFluidChunkCoord(undergroundFluidPosition.chunkZ) - minUndergroundFluidZ) >> 3; - final int undergroundFluidBookId = offsetUndergroundFluidX + offsetUndergroundFluidZ * 3; - fluidStrings[undergroundFluidBookId] = "" + undergroundFluidBookId + ": " + undergroundFluidPosition.getMinProduction() + "-" + undergroundFluidPosition.getMaxProduction() + " " + ServerTranslations.getEnglishLocalization(undergroundFluidPosition.fluid); - } - compound.setString(Tags.PROSPECTION_FLUIDS, String.join("|", fluidStrings)); - - setNBT(aStack, compound); - } - } - return true; - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/mixins/gregtech/GT_MetaTileEntity_ScannerMixin.java b/src/main/java/com/sinthoras/visualprospecting/mixins/gregtech/GT_MetaTileEntity_ScannerMixin.java deleted file mode 100644 index 6c25b223..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/mixins/gregtech/GT_MetaTileEntity_ScannerMixin.java +++ /dev/null @@ -1,143 +0,0 @@ -package com.sinthoras.visualprospecting.mixins.gregtech; - -import com.sinthoras.visualprospecting.ServerTranslations; -import com.sinthoras.visualprospecting.Tags; -import com.sinthoras.visualprospecting.database.OreVeinPosition; -import com.sinthoras.visualprospecting.database.ServerCache; -import gregtech.api.interfaces.ITexture; -import gregtech.api.metatileentity.implementations.GT_MetaTileEntity_BasicMachine; -import gregtech.api.util.GT_Utility; -import gregtech.common.tileentities.machines.basic.GT_MetaTileEntity_Scanner; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.nbt.NBTTagList; -import net.minecraft.nbt.NBTTagString; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -import java.util.List; - -import static gregtech.api.util.GT_Utility.ItemNBT.setNBT; - -@Mixin(value = GT_MetaTileEntity_Scanner.class, remap = false) -public abstract class GT_MetaTileEntity_ScannerMixin extends GT_MetaTileEntity_BasicMachine { - - public GT_MetaTileEntity_ScannerMixin() { - super(0, "", "", 0, 0, "", 0, 0, "", "", (ITexture[]) null); - } - - @Inject(method = "checkRecipe", - at = @At(value = "INVOKE", - target = "Lgregtech/api/util/GT_Utility$ItemNBT;convertProspectionData(Lnet/minecraft/item/ItemStack;)V"), - remap = false, - require = 1, - cancellable = true) - private void onAnalyzeProspectionData(CallbackInfoReturnable callbackInfoReturnable) { - final ItemStack itemStack = getInputAt(0); - final NBTTagCompound compound = itemStack.getTagCompound(); - if(compound.hasKey(Tags.VISUALPROSPECTING_FLAG)) { - final int dimensionId = compound.getInteger(Tags.PROSPECTION_DIMENSION_ID); - final int blockX = compound.getInteger(Tags.PROSPECTION_BLOCK_X); - final int blockY = compound.getInteger(Tags.PROSPECTION_BLOCK_Y); - final int blockZ = compound.getInteger(Tags.PROSPECTION_BLOCK_Z); - final int blockRadius = compound.getInteger(Tags.PROSPECTION_ORE_RADIUS); - final int numberOfUndergroundFluids = compound.getInteger(Tags.PROSPECTION_NUMBER_OF_UNDERGROUND_FLUID); - final String position = "X: " + blockX + " Y: " + blockY + " Z: " + blockZ; - - final NBTTagList bookPages = new NBTTagList(); - - final String frontPage = "Prospector report\n" - + position + "\n\n" - + "Fluids: " + numberOfUndergroundFluids + "\n\n" - + "Ores within " + blockRadius + " blocks\n\n" - + "Location is center of orevein\n\n" - + "Results are synchronized to your map"; - bookPages.appendTag(new NBTTagString(frontPage)); - - final List foundOreVeins = ServerCache.instance.prospectOreBlockRadius(dimensionId, blockX, blockZ, blockRadius); - if(foundOreVeins.isEmpty() == false) { - final int pageSize = 7; - final int numberOfPages = (foundOreVeins.size() + pageSize) / pageSize; // Equals to ceil((foundOreVeins.size()) - - for(int pageNumber = 0; pageNumber < numberOfPages; pageNumber++) { - final StringBuilder pageString = new StringBuilder(); - for(int i = 0; i < pageSize; i++) { - final int veinId = pageNumber * pageSize + i; - if(veinId < foundOreVeins.size()) { - final OreVeinPosition oreVein = foundOreVeins.get(veinId); - pageString.append(oreVein.getBlockX()).append(",").append(oreVein.getBlockZ()).append(" - ").append(ServerTranslations.getEnglishLocalization(oreVein.veinType)).append("\n"); - } - } - String pageCounter = numberOfPages > 1 ? String.format(" %d/%d", pageNumber + 1, numberOfPages) : ""; - NBTTagString pageTag = new NBTTagString(String.format("Ore Veins %s\n\n", pageCounter) + pageString); - bookPages.appendTag(pageTag); - } - } - - if(compound.hasKey(Tags.PROSPECTION_FLUIDS)) { - GT_Utility.ItemNBT.fillBookWithList(bookPages, "Fluids%s\n\n", "\n", 9, compound.getString(Tags.PROSPECTION_FLUIDS).split("\\|")); - - final String fluidCoverPage = "Fluid notes\n\n" - + "Prospects from NW to SE 576 chunks" - + "(9 8x8 fields)\n around and gives min-max amount" + "\n\n" - + "[1][2][3]" + "\n" - + "[4][5][6]" + "\n" - + "[7][8][9]" + "\n" - + "\n" - + "[5] - Prospector in this 8x8 area"; - bookPages.appendTag(new NBTTagString(fluidCoverPage)); - - - String tFluidsPosStr = "X: " + Math.floorDiv(blockX, 16 * 8) * 16 * 8 + " Z: " + Math.floorDiv(blockZ, 16 * 8) * 16 * 8 + "\n"; - int xOff = blockX - Math.floorDiv(blockX, 16 * 8) * 16 * 8; - xOff = xOff / 16; - int xOffRemain = 7 - xOff; - - int zOff = blockZ - Math.floorDiv(blockZ, 16 * 8) * 16 * 8; - zOff = zOff / 16; - int zOffRemain = 7 - zOff; - - for (; zOff > 0; zOff--) { - tFluidsPosStr = tFluidsPosStr.concat("--------\n"); - } - for (; xOff > 0; xOff--) { - tFluidsPosStr = tFluidsPosStr.concat("-"); - } - - tFluidsPosStr = tFluidsPosStr.concat("P"); - - for (; xOffRemain > 0; xOffRemain--) { - tFluidsPosStr = tFluidsPosStr.concat("-"); - } - tFluidsPosStr = tFluidsPosStr.concat("\n"); - for (; zOffRemain > 0; zOffRemain--) { - tFluidsPosStr = tFluidsPosStr.concat("--------\n"); - } - tFluidsPosStr = tFluidsPosStr.concat(" X: " + (Math.floorDiv(blockX, 16 * 8) + 1) * 16 * 8 + " Z: " + (Math.floorDiv(blockZ, 16 * 8) + 1) * 16 * 8); // +1 field to find bottomright of [5] - final String fluidsPage = "Corners of [5] are \n" + - tFluidsPosStr + "\n" + - "P - Prospector in 8x8 field"; - bookPages.appendTag(new NBTTagString(fluidsPage)); - } - - compound.setString("author", position); - compound.setTag("pages", bookPages); - setNBT(itemStack, compound); - - - // Mimic original behaviour - itemStack.stackSize -= 1; - mOutputItems[0] = GT_Utility.copyAmount(1L, itemStack); - calculateOverclockedNess(30, 1000); - //In case recipe is too OP for that machine - if (mMaxProgresstime == Integer.MAX_VALUE - 1 && mEUt == Integer.MAX_VALUE - 1) { - callbackInfoReturnable.setReturnValue(FOUND_RECIPE_BUT_DID_NOT_MEET_REQUIREMENTS); - } - else { - callbackInfoReturnable.setReturnValue(2); - } - } - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/mixins/gregtech/WorldGenContainerMixin.java b/src/main/java/com/sinthoras/visualprospecting/mixins/gregtech/WorldGenContainerMixin.java deleted file mode 100644 index 81fcd699..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/mixins/gregtech/WorldGenContainerMixin.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.sinthoras.visualprospecting.mixins.gregtech; - -import com.sinthoras.visualprospecting.Utils; -import com.sinthoras.visualprospecting.database.ServerCache; -import gregtech.common.GT_Worldgen_GT_Ore_Layer; -import gregtech.common.GT_Worldgenerator; -import net.minecraft.world.World; -import net.minecraft.world.chunk.IChunkProvider; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; - -import java.util.Random; - -@Mixin(GT_Worldgenerator.WorldGenContainer.class) -public class WorldGenContainerMixin { - - // Redirect both calls to ensure that Bartworks ore veins are captured as well - @Redirect(method = "worldGenFindVein", - at = @At(value = "INVOKE", - target = "Lgregtech/common/GT_Worldgen_GT_Ore_Layer;executeWorldgenChunkified(Lnet/minecraft/world/World;Ljava/util/Random;Ljava/lang/String;IIIIILnet/minecraft/world/chunk/IChunkProvider;Lnet/minecraft/world/chunk/IChunkProvider;)I"), - remap = false, - require = 2) - protected int onOreVeinPlaced(GT_Worldgen_GT_Ore_Layer instance, World aWorld, Random aRandom, String aBiome, int aDimensionType, int aChunkX, int aChunkZ, int aSeedX, int aSeedZ, IChunkProvider aChunkGenerator, IChunkProvider aChunkProvider) { - final int result = instance.executeWorldgenChunkified(aWorld, aRandom, aBiome, aDimensionType, aChunkX, aChunkZ, aSeedX, aSeedZ, aChunkGenerator, aChunkProvider); - if(result == GT_Worldgen_GT_Ore_Layer.ORE_PLACED && instance.mWorldGenName.equals("NoOresInVein") == false) { - ServerCache.instance.notifyOreVeinGeneration( - aWorld.provider.dimensionId, - Utils.coordBlockToChunk(aSeedX), - Utils.coordBlockToChunk(aSeedZ), - instance.mWorldGenName); - } - return result; - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/mixins/ifu/ItemOreFinderToolMixin.java b/src/main/java/com/sinthoras/visualprospecting/mixins/ifu/ItemOreFinderToolMixin.java deleted file mode 100644 index ce5f884a..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/mixins/ifu/ItemOreFinderToolMixin.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.sinthoras.visualprospecting.mixins.ifu; - -import com.encraft.dz.items.ItemOreFinderTool; -import com.sinthoras.visualprospecting.VisualProspecting_API; -import com.sinthoras.visualprospecting.VisualProspecting_API.LogicalServer; -import com.sinthoras.visualprospecting.database.OreVeinPosition; -import gregtech.api.enums.OrePrefixes; -import gregtech.api.objects.ItemData; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; -import net.minecraft.block.Block; -import net.minecraft.entity.Entity; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.item.Item; -import net.minecraft.item.ItemStack; -import net.minecraft.world.World; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.At.Shift; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; - -@Mixin(value = ItemOreFinderTool.class, remap = false) -public class ItemOreFinderToolMixin extends Item { - - @Shadow - private static int found; - - @Shadow - private static int MAX_DAMAGE; - - @Inject( - method = "onUpdate", - at = @At(value = "INVOKE", target = "Lcom/encraft/dz/items/ItemOreFinderTool;shouldKeepLooking()Z", shift = Shift.AFTER, remap = false, ordinal = 0), - locals = LocalCapture.CAPTURE_FAILEXCEPTION, - remap = true - ) - public void onGtOreFound( - ItemStack itemstack, - World world, Entity entity, - int par4, boolean par5, - CallbackInfo ci, - ItemStack searchItem, ItemData data, boolean vanilla, int id, - double cur_x, double cur_y, double cur_z, - int min_x, int min_y, int min_z, - int max_x, int max_y, int max_z, - boolean keepLooking, - int z1, int x1, int y1, - Block tBlock, int meta, ItemStack inWorld, ItemData dataInWorld, List oreTypes - ) { - if (vanilla || world.isRemote || didNotDetectSignificantOreCluster() || !(entity instanceof EntityPlayer)) return; - - final short foundMaterialMetaId = (short) dataInWorld.mMaterial.mMaterial.mMetaItemSubID; - - final int blockX = x1; - final int blockZ = z1; - final List discoveredOreVeins = listVeinsInProximityContaining(foundMaterialMetaId, blockX, blockZ, world); - - VisualProspecting_API.LogicalServer.sendProspectionResultsToClient((EntityPlayerMP) entity, discoveredOreVeins, Collections.emptyList()); - } - - private boolean didNotDetectSignificantOreCluster() { - return found < MAX_DAMAGE; - } - - private List listVeinsInProximityContaining(short foundMaterialMetaId, int blocX, int blockZ, World world) { - return LogicalServer - .prospectOreVeinsWithinRadius(world.provider.dimensionId, blocX, blockZ, 48) - .stream() - .filter(it -> it.veinType.containsOre(foundMaterialMetaId)) - .collect(Collectors.toList()); - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/mixins/journeymap/FullscreenActionsMixin.java b/src/main/java/com/sinthoras/visualprospecting/mixins/journeymap/FullscreenActionsMixin.java deleted file mode 100644 index 90eab92d..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/mixins/journeymap/FullscreenActionsMixin.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.sinthoras.visualprospecting.mixins.journeymap; - -import com.sinthoras.visualprospecting.integration.journeymap.ResetClientCacheConfirmation; -import journeymap.client.ui.UIManager; -import journeymap.client.ui.component.Button; -import journeymap.client.ui.component.ButtonList; -import journeymap.client.ui.component.JmUI; -import journeymap.client.ui.dialog.FullscreenActions; -import net.minecraft.client.gui.GuiButton; -import net.minecraft.client.resources.I18n; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; - -@Mixin(FullscreenActions.class) -public abstract class FullscreenActionsMixin extends JmUI { - - @Shadow(remap = false) - Button buttonAbout; - - private Button resetVisualProspectingCacheButton; - - public FullscreenActionsMixin() { - super(""); - } - - @Inject(method = "func_73866_w_", - at = @At(value = "INVOKE", - target = "Ljourneymap/client/ui/component/Button;setDrawFrame(Z)V"), - remap = false, - require = 1) - private void onInitGui(CallbackInfo callbackInfo) { - resetVisualProspectingCacheButton = new Button(I18n.format("visualprospecting.button.resetprogress")); - resetVisualProspectingCacheButton.setTooltip(I18n.format("visualprospecting.button.resetprogress.tooltip")); - buttonList.add(resetVisualProspectingCacheButton); - } - - @Inject(method = "layoutButtons", at = @At("RETURN"), remap = false, require = 1) - private void onLayoutButtons(CallbackInfo callbackInfo) { - final ButtonList row = new ButtonList(buttonAbout, resetVisualProspectingCacheButton); - row.layoutCenteredHorizontal(width / 2, height / 4, true, 4); - } - - @Inject(method = "func_146284_a", at = @At("HEAD"), remap = false, cancellable = true, require = 1, locals = LocalCapture.CAPTURE_FAILEXCEPTION) - private void onButtonClicked(GuiButton guibutton, CallbackInfo callbackInfo) { - if(guibutton == resetVisualProspectingCacheButton) { - UIManager.getInstance().open(ResetClientCacheConfirmation.class); - callbackInfo.cancel(); - } - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/mixins/journeymap/FullscreenMixin.java b/src/main/java/com/sinthoras/visualprospecting/mixins/journeymap/FullscreenMixin.java deleted file mode 100644 index 57999d67..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/mixins/journeymap/FullscreenMixin.java +++ /dev/null @@ -1,293 +0,0 @@ -package com.sinthoras.visualprospecting.mixins.journeymap; - -import com.sinthoras.visualprospecting.VP; -import com.sinthoras.visualprospecting.integration.journeymap.JourneyMapState; -import com.sinthoras.visualprospecting.integration.journeymap.buttons.LayerButton; -import com.sinthoras.visualprospecting.integration.journeymap.render.LayerRenderer; -import com.sinthoras.visualprospecting.integration.journeymap.render.WaypointProviderLayerRenderer; -import com.sinthoras.visualprospecting.integration.model.MapState; -import com.sinthoras.visualprospecting.integration.model.layers.LayerManager; -import journeymap.client.Constants; -import journeymap.client.io.ThemeFileHandler; -import journeymap.client.log.LogFormatter; -import journeymap.client.log.StatTimer; -import journeymap.client.model.BlockCoordIntPair; -import journeymap.client.render.map.GridRenderer; -import journeymap.client.ui.UIManager; -import journeymap.client.ui.component.Button; -import journeymap.client.ui.component.ButtonList; -import journeymap.client.ui.component.JmUI; -import journeymap.client.ui.fullscreen.Fullscreen; -import journeymap.client.ui.fullscreen.MapChat; -import journeymap.client.ui.fullscreen.layer.LayerDelegate; -import journeymap.client.ui.theme.Theme; -import journeymap.client.ui.theme.ThemeButton; -import journeymap.client.ui.theme.ThemeToggle; -import journeymap.client.ui.theme.ThemeToolbar; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiButton; -import net.minecraft.client.renderer.RenderHelper; -import org.apache.logging.log4j.Level; -import org.lwjgl.input.Mouse; -import org.spongepowered.asm.lib.Opcodes; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; - -import java.util.List; - -@Mixin(value = Fullscreen.class, remap = false) -public abstract class FullscreenMixin extends JmUI { - - private int oldMouseX = 0; - private int oldMouseY = 0; - private long timeLastClick = 0; - - @Final - @Shadow(remap = false) - static GridRenderer gridRenderer; - - @Shadow(remap = false) - ThemeToolbar mapTypeToolbar; - - @Shadow(remap = false) - ThemeButton buttonCaves; - - @Shadow(remap = false) - ThemeButton buttonNight; - - @Shadow(remap = false) - ThemeButton buttonDay; - - @Shadow(remap = false) - StatTimer drawScreenTimer; - - @Shadow(remap = false) - MapChat chat; - - @Final - @Shadow(remap = false) - LayerDelegate layerDelegate; - - @Shadow(remap = false) - boolean firstLayoutPass; - - @Shadow(remap = false) - int mx; - - @Shadow(remap = false) - int my; - - public FullscreenMixin() { - super(""); - } - - @Inject(method = "*", at = @At("RETURN"), remap = false, require = 1) - private void init(CallbackInfo callbackInfo) { - MapState.instance.layers.forEach(LayerManager::forceRefresh); - } - - @Shadow(remap = false) - private int getMapFontScale() { - throw new IllegalStateException("Mixin failed to shadow getMapFontScale()"); - } - - @Shadow(remap = false) - void drawMap() { - throw new IllegalStateException("Mixin failed to shadow drawMap()"); - } - - @Inject(method = "*", at = @At("RETURN"), remap = false, require = 1) - private void onConstructed(CallbackInfo callbackInfo) { - MapState.instance.layers.forEach(LayerManager::onOpenMap); - } - - @Inject(method = "drawMap", - at = @At(value = "INVOKE", - target = "Ljourneymap/client/model/MapState;getDrawWaypointSteps()Ljava/util/List;"), - remap = false, - require = 1, - locals = LocalCapture.CAPTURE_FAILEXCEPTION) - private void onBeforeDrawJourneyMapWaypoints(CallbackInfo callbackInfo, boolean refreshReady, StatTimer timer, int xOffset, int yOffset, float drawScale) { - final int fontScale = getMapFontScale(); - final Minecraft minecraft = Minecraft.getMinecraft(); - final int centerBlockX = (int) Math.round(gridRenderer.getCenterBlockX()); - final int centerBlockZ = (int) Math.round(gridRenderer.getCenterBlockZ()); - final int widthBlocks = minecraft.displayWidth >> gridRenderer.getZoom(); - final int heightBlocks = minecraft.displayHeight >> gridRenderer.getZoom(); - for(LayerManager layerManager : MapState.instance.layers) { - if(layerManager.isLayerActive()) { - layerManager.recacheFullscreenMap(centerBlockX, centerBlockZ, widthBlocks, heightBlocks); - } - } - - for(LayerRenderer layerRenderer : JourneyMapState.instance.renderers) { - if(layerRenderer.isLayerActive()) { - gridRenderer.draw(layerRenderer.getDrawStepsCachedForRendering(), xOffset, yOffset, drawScale, fontScale, 0.0); - } - } - } - - @Redirect(method = "initButtons", - at = @At(value = "FIELD", - target = "Ljourneymap/client/ui/fullscreen/Fullscreen;mapTypeToolbar:Ljourneymap/client/ui/theme/ThemeToolbar;", - opcode = Opcodes.PUTFIELD), - remap = false, - require = 1) - private void OnCreateMapTypeToolbar(Fullscreen owner, ThemeToolbar value) { - final Theme theme = ThemeFileHandler.getCurrentTheme(); - final ButtonList buttonList = new ButtonList(); - - for(LayerButton layerButton : JourneyMapState.instance.buttons) { - final ThemeToggle button = new ThemeToggle(theme, layerButton.getButtonTextKey(), layerButton.getIconName()); - layerButton.setButton(button); - button.setToggled(layerButton.isActive(), false); - button.addToggleListener((unused, toggled) -> { - layerButton.toggle(); - return true; - }); - buttonList.add(button); - } - - buttonList.add(buttonCaves); - buttonList.add(buttonNight); - buttonList.add(buttonDay); - mapTypeToolbar = new ThemeToolbar(theme, buttonList); - } - - @Override - public void func_73863_a(int width, int height, float f) { - try { - func_146278_c(0); - drawMap(); - drawScreenTimer.start(); - layoutButtons(); - List tooltip = null; - if (firstLayoutPass) { - layoutButtons(); - firstLayoutPass = false; - } - else { - for(int k = 0; k < buttonList.size(); ++k) { - GuiButton guibutton = (GuiButton) buttonList.get(k); - guibutton.drawButton(mc, width, height); - if (tooltip == null && guibutton instanceof Button) { - Button button = (Button)guibutton; - if (button.mouseOver(mx, my)) { - tooltip = button.getTooltip(); - } - } - } - } - - final int scaledMouseX = (mx * mc.displayWidth) / this.width; - final int scaledMouseY = (my * mc.displayHeight) / this.height; - for(LayerRenderer layer : JourneyMapState.instance.renderers) { - if (layer instanceof WaypointProviderLayerRenderer) { - final WaypointProviderLayerRenderer waypointProviderLayer = (WaypointProviderLayerRenderer) layer; - waypointProviderLayer.onMouseMove(scaledMouseX, scaledMouseY); - } - } - - if(tooltip == null) { - for(LayerRenderer layer : JourneyMapState.instance.renderers) { - if (layer instanceof WaypointProviderLayerRenderer) { - final WaypointProviderLayerRenderer waypointProviderLayer = (WaypointProviderLayerRenderer) layer; - if (waypointProviderLayer.isLayerActive()) { - tooltip = waypointProviderLayer.getTextTooltip(); - } - } - } - } - - if (chat != null) { - chat.func_73863_a(width, height, f); - } - - if (tooltip != null && !tooltip.isEmpty()) { - drawHoveringText(tooltip, mx, my, getFontRenderer()); - RenderHelper.disableStandardItemLighting(); - } - else { - for(LayerRenderer layer : JourneyMapState.instance.renderers) { - if (layer instanceof WaypointProviderLayerRenderer) { - final WaypointProviderLayerRenderer waypointProviderLayer = (WaypointProviderLayerRenderer) layer; - if (waypointProviderLayer.isLayerActive()) { - waypointProviderLayer.drawCustomTooltip(getFontRenderer(), mx, my, this.width, this.height); - } - } - } - } - - - } - catch (Throwable var11) { - logger.log(Level.ERROR, "Unexpected exception in jm.fullscreen.drawScreen(): " + LogFormatter.toString(var11)); - UIManager.getInstance().closeAll(); - } - finally { - drawScreenTimer.stop(); - } - - } - - @Inject(method = "func_73869_a", - at = @At(value = "HEAD"), - remap = false, - require = 1, - cancellable = true) - private void onKeyPress(CallbackInfo callbackInfo) { - if((chat == null || chat.isHidden()) && Constants.isPressed(VP.keyAction)) { - for(LayerRenderer layer : JourneyMapState.instance.renderers) { - if (layer instanceof WaypointProviderLayerRenderer) { - ((WaypointProviderLayerRenderer) layer).onActionKeyPressed(); - } - } - callbackInfo.cancel(); - } - } - - @Override - protected void mouseClicked(int mouseX, int mouseY, int mouseButton) { - if (chat != null && !chat.isHidden()) { - chat.func_73864_a(mouseX, mouseY, mouseButton); - } - - super.mouseClicked(mouseX, mouseY, mouseButton); - if (!this.isMouseOverButton(mouseX, mouseY)) { - final int scaledMouseX = mx * mc.displayWidth / width; - final int scaledMouseY = my * mc.displayHeight / height; - final double blockSize = Math.pow(2.0D, gridRenderer.getZoom()); - if(onMapClicked(mouseButton, scaledMouseX, scaledMouseY, blockSize) == false) { - BlockCoordIntPair blockCoord = gridRenderer.getBlockUnderMouse(Mouse.getEventX(), Mouse.getEventY(), mc.displayWidth, mc.displayHeight); - layerDelegate.onMouseClicked(mc, Mouse.getEventX(), Mouse.getEventY(), gridRenderer.getWidth(), gridRenderer.getHeight(), blockCoord, mouseButton); - } - } - } - - private boolean onMapClicked(int mouseButton, int mouseX, int mouseY, double blockSize) { - final long timestamp = System.currentTimeMillis(); - final boolean isDoubleClick = mouseX == oldMouseX && mouseY == oldMouseY && timestamp - timeLastClick < 500; - oldMouseX = mouseX; - oldMouseY = mouseY; - timeLastClick = isDoubleClick ? 0 : timestamp; - - if(mouseButton != 0) { - return false; - } - - boolean layerHit = false; - for(LayerRenderer layer : JourneyMapState.instance.renderers) { - if (layer instanceof WaypointProviderLayerRenderer) { - ((WaypointProviderLayerRenderer) layer).onMouseMove(mouseX, mouseY); - layerHit |= ((WaypointProviderLayerRenderer) layer).onMouseAction(isDoubleClick); - } - } - return layerHit; - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/mixins/journeymap/MiniMapMixin.java b/src/main/java/com/sinthoras/visualprospecting/mixins/journeymap/MiniMapMixin.java deleted file mode 100644 index 1e74c4b7..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/mixins/journeymap/MiniMapMixin.java +++ /dev/null @@ -1,120 +0,0 @@ -package com.sinthoras.visualprospecting.mixins.journeymap; - -import com.sinthoras.visualprospecting.integration.journeymap.JourneyMapState; -import com.sinthoras.visualprospecting.integration.journeymap.render.LayerRenderer; -import com.sinthoras.visualprospecting.integration.model.MapState; -import com.sinthoras.visualprospecting.integration.model.layers.LayerManager; -import journeymap.client.render.draw.DrawStep; -import journeymap.client.render.map.GridRenderer; -import journeymap.client.ui.minimap.DisplayVars; -import journeymap.client.ui.minimap.MiniMap; -import journeymap.client.ui.minimap.Shape; -import net.minecraft.client.Minecraft; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; - -import java.awt.geom.Point2D; -import java.lang.reflect.Field; - -@Mixin(MiniMap.class) -public abstract class MiniMapMixin { - - private static Field drawScale; - private static Field fontScale; - private static Field shape; - private static Field minimapWidth; - - static { - try { - drawScale = DisplayVars.class.getDeclaredField("drawScale"); - drawScale.setAccessible(true); - fontScale = DisplayVars.class.getDeclaredField("fontScale"); - fontScale.setAccessible(true); - shape = DisplayVars.class.getDeclaredField("shape"); - shape.setAccessible(true); - minimapWidth = DisplayVars.class.getDeclaredField("minimapWidth"); - minimapWidth.setAccessible(true); - } - catch (NoSuchFieldException e) { - e.printStackTrace(); - } - } - - private static float getDrawScale(DisplayVars displayVars) { - try { - return drawScale.getFloat(displayVars); - } - catch (IllegalAccessException e) { - return 0.0f; - } - } - - private static double getFontScale(DisplayVars displayVars) { - try { - return fontScale.getDouble(displayVars); - } - catch (IllegalAccessException e) { - return 0.0; - } - } - - private static Shape getShape(DisplayVars displayVars) { - try { - return (Shape) shape.get(displayVars); - } - catch (IllegalAccessException e) { - return Shape.Circle; - } - } - - private static int getMinimapWidth(DisplayVars displayVars) { - try { - return minimapWidth.getInt(displayVars); - } - catch (IllegalAccessException e) { - return 1; - } - } - - @Final - @Shadow(remap = false) - private static GridRenderer gridRenderer; - - @Final - @Shadow(remap = false) - private Minecraft mc; - - @Shadow(remap = false) - private DisplayVars dv; - - @Inject(method = "drawOnMapWaypoints", - at = @At(value = "HEAD"), - remap = false, - require = 1, - locals = LocalCapture.CAPTURE_FAILHARD) - private void onBeforeDrawWaypoints(double rotation, CallbackInfo callbackInfo) { - for(LayerManager layerManager : MapState.instance.layers) { - if (layerManager.isLayerActive()) { - if (getShape(dv) == Shape.Circle) { - layerManager.recacheMiniMap((int) mc.thePlayer.posX, (int) mc.thePlayer.posZ, getMinimapWidth(dv)); - } - else { - layerManager.recacheMiniMap((int) mc.thePlayer.posX, (int) mc.thePlayer.posZ, gridRenderer.getWidth(), gridRenderer.getHeight()); - } - } - } - - for(LayerRenderer layerRenderer : JourneyMapState.instance.renderers) { - if(layerRenderer.isLayerActive()) { - for (DrawStep drawStep : layerRenderer.getDrawStepsCachedForRendering()) { - drawStep.draw(0.0D, 0.0D, gridRenderer, getDrawScale(dv), getFontScale(dv), rotation); - } - } - } - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/mixins/journeymap/RenderWaypointBeaconMixin.java b/src/main/java/com/sinthoras/visualprospecting/mixins/journeymap/RenderWaypointBeaconMixin.java deleted file mode 100644 index 4c2faa10..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/mixins/journeymap/RenderWaypointBeaconMixin.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.sinthoras.visualprospecting.mixins.journeymap; - -import com.sinthoras.visualprospecting.integration.journeymap.JourneyMapState; -import com.sinthoras.visualprospecting.integration.journeymap.waypoints.WaypointManager; -import journeymap.client.model.Waypoint; -import journeymap.client.render.ingame.RenderWaypointBeacon; -import net.minecraft.client.Minecraft; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -@Mixin(RenderWaypointBeacon.class) -public class RenderWaypointBeaconMixin { - - @Shadow(remap = false) - static Minecraft mc; - - @Shadow(remap = false) - static void doRender(Waypoint waypoint) { - throw new IllegalStateException("Mixin failed to shadow doRender()"); - } - - @Inject(method = "renderAll", - at = @At(value = "INVOKE", - target = "Ljourneymap/client/waypoint/WaypointStore;instance()Ljourneymap/client/waypoint/WaypointStore;"), - remap = false, - require = 1) - private static void onRenderAll(CallbackInfo callbackInfo) { - for(WaypointManager waypointManager : JourneyMapState.instance.waypointManagers) { - if(waypointManager.hasWaypoint()) { - final Waypoint waypoint = waypointManager.getJmWaypoint(); - if(waypoint.getDimensions().contains(mc.thePlayer.dimension)) { - doRender(waypoint); - } - } - } - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/mixins/journeymap/WaypointManagerMixin.java b/src/main/java/com/sinthoras/visualprospecting/mixins/journeymap/WaypointManagerMixin.java deleted file mode 100644 index 15ed16af..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/mixins/journeymap/WaypointManagerMixin.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.sinthoras.visualprospecting.mixins.journeymap; - -import com.sinthoras.visualprospecting.integration.model.MapState; -import com.sinthoras.visualprospecting.integration.model.layers.LayerManager; -import com.sinthoras.visualprospecting.integration.model.layers.WaypointProviderManager; -import journeymap.client.ui.waypoint.WaypointManager; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; - -@Mixin(WaypointManager.class) -public class WaypointManagerMixin { - - @Inject(method = "toggleItems", at = @At("HEAD"), remap = false, require = 1, locals = LocalCapture.CAPTURE_FAILEXCEPTION) - private void onToggleAllWaypoints(boolean enable, CallbackInfoReturnable callbackInfoReturnable) { - if(enable == false) { - for(LayerManager layer : MapState.instance.layers) { - if(layer instanceof WaypointProviderManager) { - ((WaypointProviderManager) layer).clearActiveWaypoint(); - } - } - } - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/mixins/late/gregtech/MTEAdvSeismicProspectorMixin.java b/src/main/java/com/sinthoras/visualprospecting/mixins/late/gregtech/MTEAdvSeismicProspectorMixin.java new file mode 100644 index 00000000..94b41897 --- /dev/null +++ b/src/main/java/com/sinthoras/visualprospecting/mixins/late/gregtech/MTEAdvSeismicProspectorMixin.java @@ -0,0 +1,119 @@ +package com.sinthoras.visualprospecting.mixins.late.gregtech; + +import static gregtech.api.util.GTUtility.ItemNBT.getNBT; +import static gregtech.api.util.GTUtility.ItemNBT.setNBT; + +import java.text.MessageFormat; +import java.util.List; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Blocks; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; + +import com.sinthoras.visualprospecting.Tags; +import com.sinthoras.visualprospecting.Utils; +import com.sinthoras.visualprospecting.VP; +import com.sinthoras.visualprospecting.database.ServerCache; +import com.sinthoras.visualprospecting.database.UndergroundFluidPosition; + +import gregtech.api.enums.ItemList; +import gregtech.api.enums.Materials; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.metatileentity.implementations.MTEBasicMachine; +import gregtech.api.util.GTUtility; +import gregtech.common.tileentities.machines.basic.MTEAdvSeismicProspector; +import ic2.core.Ic2Items; + +@Mixin(value = MTEAdvSeismicProspector.class, remap = false) +public abstract class MTEAdvSeismicProspectorMixin extends MTEBasicMachine { + + @Shadow(remap = false) + boolean ready = false; + + @Shadow(remap = false) + int radius; + + public MTEAdvSeismicProspectorMixin() { + super(0, "", "", 0, 0, "", 0, 0); + } + + /** + * @author SinTh0r4s + * @reason Redirect game mechanics onto VP database + */ + @Overwrite(remap = false) + @Override + public boolean onRightclick(IGregTechTileEntity igte, EntityPlayer aPlayer) { + if (igte.isServerSide()) { + ItemStack aStack = aPlayer.getCurrentEquippedItem(); + + if (!ready && (GTUtility.consumeItems(aPlayer, aStack, Item.getItemFromBlock(Blocks.tnt), 16) + || GTUtility.consumeItems(aPlayer, aStack, Ic2Items.industrialTnt.getItem(), 8) + || GTUtility.consumeItems(aPlayer, aStack, Materials.Glyceryl, 4) + || GTUtility.consumeItems(aPlayer, aStack, ItemList.Block_Powderbarrel.getItem(), 2))) { + + this.ready = true; + this.mMaxProgresstime = (aPlayer.capabilities.isCreativeMode ? 20 : 800); + + } else if (ready && mMaxProgresstime == 0 + && aStack != null + && aStack.stackSize == 1 + && aStack.getItem() == ItemList.Tool_DataStick.getItem()) { + this.ready = false; + + final NBTTagCompound compound = getNBT(aStack); + compound.setString(Tags.BOOK_TITLE, GTUtility.ItemNBT.getRawProspectionDataName()); + compound.setBoolean(Tags.VISUALPROSPECTING_FLAG, true); + compound.setByte(Tags.PROSPECTION_TIER, mTier); + compound.setInteger(Tags.PROSPECTION_DIMENSION_ID, igte.getWorld().provider.dimensionId); + compound.setInteger(Tags.PROSPECTION_BLOCK_X, igte.getXCoord()); + compound.setInteger(Tags.PROSPECTION_BLOCK_Y, igte.getYCoord()); + compound.setInteger(Tags.PROSPECTION_BLOCK_Z, igte.getZCoord()); + compound.setInteger(Tags.PROSPECTION_ORE_RADIUS, radius); + + final List undergroundFluidPositions = ServerCache.instance + .prospectUndergroundFluidBlockRadius( + aPlayer.worldObj, + igte.getXCoord(), + igte.getZCoord(), + VP.undergroundFluidChunkProspectingBlockRadius); + + compound.setInteger( + Tags.PROSPECTION_NUMBER_OF_UNDERGROUND_FLUID, + undergroundFluidPositions.size()); + + String[] fluidStrings = new String[9]; + final int minUndergroundFluidX = Utils.mapToCornerUndergroundFluidChunkCoord( + Utils.coordBlockToChunk( + igte.getXCoord() - VP.undergroundFluidChunkProspectingBlockRadius)); + final int minUndergroundFluidZ = Utils.mapToCornerUndergroundFluidChunkCoord( + Utils.coordBlockToChunk( + igte.getZCoord() - VP.undergroundFluidChunkProspectingBlockRadius)); + for (UndergroundFluidPosition undergroundFluidPosition : undergroundFluidPositions) { + final int offsetUndergroundFluidX = (Utils.mapToCornerUndergroundFluidChunkCoord( + undergroundFluidPosition.chunkX) - minUndergroundFluidX) >> 3; + final int offsetUndergroundFluidZ = (Utils.mapToCornerUndergroundFluidChunkCoord( + undergroundFluidPosition.chunkZ) - minUndergroundFluidZ) >> 3; + final int undergroundFluidBookId = offsetUndergroundFluidX + offsetUndergroundFluidZ * 3; + fluidStrings[undergroundFluidBookId] = MessageFormat.format( + "{0}: {1}-{2} {3}", + undergroundFluidBookId, + undergroundFluidPosition.getMinProduction(), + undergroundFluidPosition.getMaxProduction(), + undergroundFluidPosition.fluid.getLocalizedName()); + } + + compound.setString(Tags.PROSPECTION_FLUIDS, String.join("|", fluidStrings)); + + setNBT(aStack, compound); + } + } + return true; + } +} diff --git a/src/main/java/com/sinthoras/visualprospecting/mixins/minecraft/ForgeHooksClientMixin.java b/src/main/java/com/sinthoras/visualprospecting/mixins/minecraft/ForgeHooksClientMixin.java deleted file mode 100644 index d7dcb185..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/mixins/minecraft/ForgeHooksClientMixin.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.sinthoras.visualprospecting.mixins.minecraft; - -import net.minecraftforge.client.ForgeHooksClient; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -@SuppressWarnings("UnusedMixin") -@Mixin(value = ForgeHooksClient.class, remap = false) -public class ForgeHooksClientMixin { - - // this is only a mixin because it needs to run before the minecraft window is created - @Inject(method = "createDisplay", - at = @At("HEAD") - ) - private static void enableStencilBuffer(CallbackInfo ci) { - // give me my stencil buffer, forge. - System.setProperty("forge.forceDisplayStencil", "true"); - } - -} diff --git a/src/main/java/com/sinthoras/visualprospecting/mixins/minecraft/ItemEditableBookMixin.java b/src/main/java/com/sinthoras/visualprospecting/mixins/minecraft/ItemEditableBookMixin.java deleted file mode 100644 index cdd249fc..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/mixins/minecraft/ItemEditableBookMixin.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.sinthoras.visualprospecting.mixins.minecraft; - -import com.sinthoras.visualprospecting.VP; -import com.sinthoras.visualprospecting.Tags; -import com.sinthoras.visualprospecting.Utils; -import com.sinthoras.visualprospecting.database.ClientCache; -import com.sinthoras.visualprospecting.database.ServerCache; -import com.sinthoras.visualprospecting.database.UndergroundFluidPosition; -import com.sinthoras.visualprospecting.database.OreVeinPosition; -import com.sinthoras.visualprospecting.network.ProspectingNotification; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.item.ItemEditableBook; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.world.World; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; - -import java.util.List; - -@Mixin(value = ItemEditableBook.class, remap = true) -public class ItemEditableBookMixin { - - @Inject(method = "onItemRightClick", - at = @At("HEAD"), - remap = true, - require = 1, - locals = LocalCapture.CAPTURE_FAILEXCEPTION) - private void onItemRightClick(ItemStack itemStack, World world, EntityPlayer entityPlayer, CallbackInfoReturnable callbackInfoReturnable) { - if(world.isRemote == false) { - final NBTTagCompound compound = itemStack.getTagCompound(); - if(compound.hasKey(Tags.VISUALPROSPECTING_FLAG)) { - final int dimensionId = compound.getInteger(Tags.PROSPECTION_DIMENSION_ID); - final int blockX = compound.getInteger(Tags.PROSPECTION_BLOCK_X); - final int blockZ = compound.getInteger(Tags.PROSPECTION_BLOCK_Z); - final int blockRadius = compound.getInteger(Tags.PROSPECTION_ORE_RADIUS); - final List foundOreVeins = ServerCache.instance.prospectOreBlockRadius(dimensionId, blockX, blockZ, blockRadius); - final List foundUndergroundFluids = ServerCache.instance.prospectUndergroundFluidBlockRadius(world, blockX, blockZ, VP.undergroundFluidChunkProspectingBlockRadius); - if(Utils.isLogicalClient()) { - ClientCache.instance.putOreVeins(foundOreVeins); - ClientCache.instance.putUndergroundFluids(foundUndergroundFluids); - } - else { - VP.network.sendTo(new ProspectingNotification(foundOreVeins, foundUndergroundFluids), (EntityPlayerMP) entityPlayer); - } - } - } - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/mixins/tcnodetracker/GuiMainMixin.java b/src/main/java/com/sinthoras/visualprospecting/mixins/tcnodetracker/GuiMainMixin.java deleted file mode 100644 index c70ffa88..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/mixins/tcnodetracker/GuiMainMixin.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.sinthoras.visualprospecting.mixins.tcnodetracker; - -import com.dyonovan.tcnodetracker.gui.GuiMain; -import com.dyonovan.tcnodetracker.lib.AspectLoc; -import com.sinthoras.visualprospecting.integration.model.layers.ThaumcraftNodeLayerManager; -import com.sinthoras.visualprospecting.integration.model.waypoints.Waypoint; -import net.minecraft.client.gui.GuiButton; -import net.minecraft.client.resources.I18n; -import org.spongepowered.asm.lib.Opcodes; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; - -@Mixin(GuiMain.class) -public class GuiMainMixin { - - @Shadow(remap = false) - private int low; - - @Inject(method = "actionPerformed", - at = @At(value = "FIELD", - target = "Lcom/dyonovan/tcnodetracker/TCNodeTracker;zMarker:I", - opcode = Opcodes.PUTSTATIC, - shift = At.Shift.AFTER, - remap = false), - remap = true, - require = 1, - locals = LocalCapture.CAPTURE_FAILEXCEPTION, - cancellable = true) - private void onWaypointSet(GuiButton button, CallbackInfo callbackInfo, int i) { - final AspectLoc aspect = GuiMain.aspectList.get(low + i); - GuiMain.aspectList.clear(); - ThaumcraftNodeLayerManager.instance.setActiveWaypoint(new Waypoint(aspect.x, aspect.y, aspect.z, aspect.dimID, - I18n.format("visualprospecting.tracked", I18n.format("tile.blockAiry.0.name")), 0xFFFFFF)); - callbackInfo.cancel(); - } - - @Inject(method = "actionPerformed", - at = @At(value = "FIELD", - target = "Lcom/dyonovan/tcnodetracker/TCNodeTracker;doGui:Z", - opcode = Opcodes.PUTSTATIC, - ordinal = 0, - remap = false), - remap = true, - require = 1) - private void onWaypointClear(CallbackInfo callbackInfo) { - ThaumcraftNodeLayerManager.instance.clearActiveWaypoint(); - } - - @Inject(method = "actionPerformed", - at = @At(value = "FIELD", - target = "Lcom/dyonovan/tcnodetracker/TCNodeTracker;doGui:Z", - opcode = Opcodes.PUTSTATIC, - ordinal = 1, - remap = false), - remap = true, - require = 1, - locals = LocalCapture.CAPTURE_FAILEXCEPTION) - private void onWaypointDelete(GuiButton button, CallbackInfo callbackInfo, int i, int k, int j) { - ThaumcraftNodeLayerManager.instance.clearActiveWaypoint(); - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/mixins/xaerosminimap/MinimapRendererMixin.java b/src/main/java/com/sinthoras/visualprospecting/mixins/xaerosminimap/MinimapRendererMixin.java deleted file mode 100644 index c48b614f..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/mixins/xaerosminimap/MinimapRendererMixin.java +++ /dev/null @@ -1,115 +0,0 @@ -package com.sinthoras.visualprospecting.mixins.xaerosminimap; - -import com.sinthoras.visualprospecting.VP; -import com.sinthoras.visualprospecting.integration.model.MapState; -import com.sinthoras.visualprospecting.integration.model.layers.LayerManager; -import com.sinthoras.visualprospecting.integration.xaeroworldmap.XaeroWorldMapState; -import com.sinthoras.visualprospecting.integration.xaeroworldmap.renderers.LayerRenderer; -import com.sinthoras.visualprospecting.integration.xaeroworldmap.rendersteps.RenderStep; -import net.minecraft.client.Minecraft; -import net.minecraftforge.client.MinecraftForgeClient; -import org.lwjgl.opengl.GL11; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.Slice; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; -import xaero.common.XaeroMinimapSession; -import xaero.common.minimap.MinimapProcessor; -import xaero.common.minimap.render.MinimapRenderer; -import xaero.common.settings.ModSettings; - -import java.util.ArrayList; - -@SuppressWarnings("UnusedMixin") -@Mixin(value = MinimapRenderer.class, remap = false) -public class MinimapRendererMixin { - - @Unique private boolean stencilEnabled = true; - - @Shadow protected Minecraft mc; - - @Shadow protected double zoom; - - @Inject(method = "renderMinimap", - at = @At(value = "INVOKE", - target = "Lxaero/common/minimap/waypoints/render/WaypointsGuiRenderer;render(Lxaero/common/XaeroMinimapSession;Lxaero/common/minimap/render/MinimapRendererHelper;DDIIDDFDZFZ)V" - ), - locals = LocalCapture.CAPTURE_FAILEXCEPTION - ) - private void injectDraw(XaeroMinimapSession minimapSession, MinimapProcessor minimap, int x, int y, int width, int height, int scale, int size, - float partial, CallbackInfo ci, ModSettings settings, ArrayList underText, int mapSize, int bufferSize, - float minimapScale, float mapScale, float sizeFix, int shape, boolean lockedNorth, double angle, double ps, double pc, - boolean useWorldMap, int lightLevel, boolean cave, boolean circleShape, int scaledX, int scaledY, int minimapFrameSize, - int circleSides, int frameType, boolean renderFrame, int frameTextureX, int halfFrame, int rightCornerStartX, int specH, - boolean safeMode, double playerX, double playerZ) { - for (LayerManager layerManager : MapState.instance.layers) { - if (layerManager.isLayerActive()) { - if (circleShape) { - layerManager.recacheMiniMap((int) mc.thePlayer.posX, (int) mc.thePlayer.posZ, minimapFrameSize * 2); - } - else { - layerManager.recacheMiniMap((int) mc.thePlayer.posX, (int) mc.thePlayer.posZ, minimapFrameSize * 2, minimapFrameSize * 2); - } - } - } - - if (stencilEnabled) { - GL11.glPushMatrix(); - GL11.glEnable(GL11.GL_STENCIL_TEST); - GL11.glRotated(Math.toDegrees(angle) - 90, 0.0, 0.0, 1.0); - GL11.glScaled(zoom, zoom, 0); - GL11.glStencilFunc(GL11.GL_EQUAL, 1, 1); - for (LayerRenderer renderer : XaeroWorldMapState.instance.renderers) { - if (renderer.isLayerActive()) { - for (RenderStep renderStep : renderer.getRenderSteps()) { - renderStep.draw(null, playerX, playerZ, scale); - } - } - } - GL11.glDisable(GL11.GL_STENCIL_TEST); - GL11.glPopMatrix(); - } - } - - @Inject(method = "renderMinimap", - at = @At(value = "INVOKE", - target = "Lorg/lwjgl/opengl/GL11;glScalef(FFF)V", - shift = At.Shift.AFTER - ), slice = @Slice( - to = @At(value = "INVOKE", - target = "Lxaero/common/minimap/render/MinimapRendererHelper;drawTexturedElipseInsideRectangle(IFFIIFF)V" - ) - ) - ) - private void injectBeginStencil(XaeroMinimapSession minimapSession, MinimapProcessor minimap, int x, int y, int width, int height, int scale, int size, float partial, CallbackInfo ci) { - if (stencilEnabled && MinecraftForgeClient.getStencilBits() == 0) { - stencilEnabled = false; - VP.warn("Could not enable stencils! Xaero's minimap overlays will not render"); - } - // if stencil is not enabled, this code will do nothing - GL11.glEnable(GL11.GL_STENCIL_TEST); - GL11.glStencilFunc(GL11.GL_ALWAYS, 1, 1); - GL11.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_REPLACE); - GL11.glStencilMask(0xFF); - GL11.glClear(GL11.GL_STENCIL_BUFFER_BIT); - } - - @Inject(method = "renderMinimap", - at = @At(value = "INVOKE", - target = "Lxaero/common/minimap/MinimapInterface;usingFBO()Z" - ), slice = @Slice( - from = @At(value = "INVOKE", - target = "Lxaero/common/minimap/render/MinimapRendererHelper;drawTexturedElipseInsideRectangle(IFFIIFF)V" - ) - ) - ) - private void injectEndStencil(XaeroMinimapSession minimapSession, MinimapProcessor minimap, int x, int y, int width, int height, int scale, int size, float partial, CallbackInfo ci) { - GL11.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP); - GL11.glStencilMask(0x00); - GL11.glDisable(GL11.GL_STENCIL_TEST); - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/mixins/xaerosminimap/WaypointsIngameRendererMixin.java b/src/main/java/com/sinthoras/visualprospecting/mixins/xaerosminimap/WaypointsIngameRendererMixin.java deleted file mode 100644 index 4002c4f4..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/mixins/xaerosminimap/WaypointsIngameRendererMixin.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.sinthoras.visualprospecting.mixins.xaerosminimap; - -import com.sinthoras.visualprospecting.integration.xaerominimap.waypoints.WaypointManager; -import com.sinthoras.visualprospecting.integration.xaerominimap.XaeroMiniMapState; -import net.minecraft.client.Minecraft; -import org.objectweb.asm.Opcodes; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.Slice; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import xaero.common.XaeroMinimapSession; -import xaero.common.minimap.waypoints.render.WaypointsIngameRenderer; - -@Mixin(value = WaypointsIngameRenderer.class, remap = false) -public class WaypointsIngameRendererMixin { - - @Inject(method = "render", - at = @At(value = "INVOKE", - target = "Lxaero/common/minimap/waypoints/render/WaypointsIngameRenderer;renderWaypointsList(Ljava/util/Collection;DDDLnet/minecraft/entity/Entity;Lnet/minecraft/client/renderer/Tessellator;DDDDFFDIFFLnet/minecraft/util/Vec3;D)V" - ), - slice = @Slice( - from = @At(value = "FIELD", - target = "Lxaero/common/minimap/waypoints/WaypointsManager;customWaypoints:Ljava/util/Hashtable;", - opcode = Opcodes.GETSTATIC - ) - ) - ) - private void injectPreRenderCustomWaypoints(XaeroMinimapSession sets, float modCustomWaypoints, CallbackInfo ci) { - for (WaypointManager manager : XaeroMiniMapState.instance.waypointManagers) { - if (manager.hasWaypoint()) { - manager.getXWaypoint().notifyDimension(Minecraft.getMinecraft().renderViewEntity.dimension); - } - } - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/mixins/xaerosworldmap/GuiMapMixin.java b/src/main/java/com/sinthoras/visualprospecting/mixins/xaerosworldmap/GuiMapMixin.java deleted file mode 100644 index 137f314b..00000000 --- a/src/main/java/com/sinthoras/visualprospecting/mixins/xaerosworldmap/GuiMapMixin.java +++ /dev/null @@ -1,203 +0,0 @@ -package com.sinthoras.visualprospecting.mixins.xaerosworldmap; - -import com.sinthoras.visualprospecting.VP; -import com.sinthoras.visualprospecting.integration.model.MapState; -import com.sinthoras.visualprospecting.integration.model.layers.LayerManager; -import com.sinthoras.visualprospecting.integration.xaeroworldmap.XaeroWorldMapState; -import com.sinthoras.visualprospecting.integration.xaeroworldmap.buttons.LayerButton; -import com.sinthoras.visualprospecting.integration.xaeroworldmap.buttons.SizedGuiTexturedButton; -import com.sinthoras.visualprospecting.integration.xaeroworldmap.renderers.InteractableLayerRenderer; -import com.sinthoras.visualprospecting.integration.xaeroworldmap.renderers.LayerRenderer; -import com.sinthoras.visualprospecting.integration.xaeroworldmap.rendersteps.RenderStep; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiButton; -import net.minecraft.client.gui.GuiScreen; -import net.minecraft.entity.player.EntityPlayer; -import org.objectweb.asm.Opcodes; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.Slice; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; -import xaero.map.MapProcessor; -import xaero.map.gui.CursorBox; -import xaero.map.gui.GuiMap; -import xaero.map.gui.ScreenBase; -import xaero.map.misc.Misc; - -@SuppressWarnings({"MixinAnnotationTarget", "UnresolvedMixinReference", "UnusedMixin"}) -@Mixin(value = GuiMap.class, remap = false) -public abstract class GuiMapMixin extends ScreenBase { - - @Unique - private int oldMouseX = 0; - @Unique - private int oldMouseY = 0; - @Unique - private long timeLastClick = 0; - - protected GuiMapMixin(GuiScreen parent, GuiScreen escape) { - super(parent, escape); - } - - @Shadow - private double cameraX; - - @Shadow - private double cameraZ; - - @Shadow - private double scale; - - @Shadow - public abstract void addGuiButton(GuiButton b); - - @Shadow - private int screenScale; - - @Inject(method = "", - at = @At("RETURN") - ) - private void injectConstruct(GuiScreen parent, GuiScreen escape, MapProcessor mapProcessor, EntityPlayer player, CallbackInfo ci) { - MapState.instance.layers.forEach(LayerManager::onOpenMap); - } - - // apparently mixin can read the obfuscated names even with the deobf jar loaded. weird - // i guess all the errors mcdev shows here aren't real? - // deobf method = "drawScreen" - @Inject(method = "func_73863_a", - at = @At(value = "INVOKE", - target = "Lorg/lwjgl/opengl/GL11;glPushMatrix()V", - ordinal = 1 - ), - locals = LocalCapture.CAPTURE_FAILEXCEPTION - ) - // why is this method so long. this isnt even 1/5 of the way through and look at how many locals there are already - private void injectPreRender(int scaledMouseX, int scaledMouseY, float partialTicks, CallbackInfo ci, Minecraft mc, long startTime, long passed, double passedScrolls, - int direction, Object var12, boolean mapLoaded, boolean noWorldMapEffect, int mouseXPos, int mouseYPos, double scaleMultiplier, - double oldMousePosZ, double preScale, double fboScale, double secondaryScale, double mousePosX, double mousePosZ, int mouseFromCentreX, - int mouseFromCentreY, double oldMousePosX, int textureLevel, int leveledRegionShift) { - // snap the camera to whole pixel values. works around a rendering issue but causes another when framerate is uncapped - if (mc.gameSettings.limitFramerate < 255 || mc.gameSettings.enableVsync) { - cameraX = Math.round(cameraX * scale) / scale; - cameraZ = Math.round(cameraZ * scale) / scale; - } - - for (LayerRenderer layer : XaeroWorldMapState.instance.renderers) { - if (layer instanceof InteractableLayerRenderer) { - ((InteractableLayerRenderer) layer).updateHovered(mousePosX, mousePosZ, cameraX, cameraZ, scale); - } - } - } - - // deobf method = "drawScreen" - @Inject(method = "func_73863_a", - at = @At(value = "INVOKE", - target = "Lorg/lwjgl/opengl/GL11;glEnable(I)V", - ordinal = 1, - shift = At.Shift.AFTER - ), slice = @Slice( - from = @At(value = "INVOKE", - target = "Lorg/lwjgl/opengl/GL14;glBlendFuncSeparate(IIII)V" - ), - to = @At(value = "INVOKE", - target = "Lxaero/map/mods/SupportXaeroMinimap;renderWaypoints(Lnet/minecraft/client/gui/GuiScreen;DDIIDDDDLjava/util/regex/Pattern;Ljava/util/regex/Pattern;FLxaero/map/mods/gui/Waypoint;Lnet/minecraft/client/Minecraft;Lnet/minecraft/client/gui/ScaledResolution;)Lxaero/map/mods/gui/Waypoint;" - ) - ) - ) - private void injectDraw(int scaledMouseX, int scaledMouseY, float partialTicks, CallbackInfo ci) { - for (LayerManager layerManager : MapState.instance.layers) { - if (layerManager.isLayerActive()) { - // +20s are to work around precision loss from casting to int and right-shifting - layerManager.recacheFullscreenMap((int) cameraX, (int) cameraZ, (int) (mc.displayWidth / scale) + 20, (int) (mc.displayHeight / scale) + 20); - } - } - - for (LayerRenderer renderer : XaeroWorldMapState.instance.renderers) { - if (renderer.isLayerActive()) { - for (RenderStep step : renderer.getRenderSteps()) { - step.draw(this, cameraX, cameraZ, scale); - } - } - } - } - - // deobf method = drawScreen - @Inject(method = "func_73863_a", - at = @At(value = "INVOKE", - target = "Lorg/lwjgl/opengl/GL11;glTranslated(DDD)V" - ), - slice = @Slice( - from = @At(value = "FIELD", - // deobf target = "Lnet/minecraft/client/Minecraft;currentScreen:Lnet/minecraft/client/gui/GuiScreen;" - target = "Lnet/minecraft/client/Minecraft;field_71462_r:Lnet/minecraft/client/gui/GuiScreen;", - opcode = Opcodes.GETFIELD - ), - to = @At(value = "INVOKE", - target = "Lxaero/map/gui/CursorBox;drawBox(IIII)V" - ) - ) - ) - private void injectDrawTooltip(int scaledMouseX, int scaledMouseY, float partialTicks, CallbackInfo ci) { - for (LayerRenderer layer : XaeroWorldMapState.instance.renderers) { - if (layer instanceof InteractableLayerRenderer && layer.isLayerActive()) { - ((InteractableLayerRenderer) layer).drawTooltip(this, scale, screenScale); - } - } - } - - // deobf method = "initGui" - @Inject(method = "func_73866_w_", - at = @At(value = "INVOKE", - target = "Lorg/lwjgl/input/Keyboard;enableRepeatEvents(Z)V" - ) - ) - private void injectInitButtons(CallbackInfo ci) { - for (int i = 0; i < XaeroWorldMapState.instance.buttons.size(); i++) { - LayerButton layerButton = XaeroWorldMapState.instance.buttons.get(i); - SizedGuiTexturedButton button = new SizedGuiTexturedButton(0, height - 20 * (i + 1), - layerButton.textureLocation, (btn) -> layerButton.toggle(), - new CursorBox(layerButton.getButtonTextKey())); - layerButton.setButton(button); - addGuiButton(button); - } - } - - @Inject(method = "onInputPress", - at = @At("TAIL") - ) - private void injectListenKeypress(boolean mouse, int code, CallbackInfoReturnable cir) { - if (Misc.inputMatchesKeyBinding(mouse, code, VP.keyAction)) { - for (LayerRenderer layer : XaeroWorldMapState.instance.renderers) { - if (layer instanceof InteractableLayerRenderer) { - ((InteractableLayerRenderer) layer).doActionKeyPress(); - } - } - } - } - - @Inject(method = "mapClicked", - at = @At("TAIL") - ) - private void injectListenClick(int button, int x, int y, CallbackInfo ci) { - if (button == 0) { - final long timestamp = System.currentTimeMillis(); - final boolean isDoubleClick = x == oldMouseX && y == oldMouseY && timestamp - timeLastClick < 500; - oldMouseX = x; - oldMouseY = y; - timeLastClick = isDoubleClick ? 0 : timestamp; - - if (isDoubleClick) { - for (LayerRenderer layer : XaeroWorldMapState.instance.renderers) { - if (layer instanceof InteractableLayerRenderer && layer.isLayerActive()) { - ((InteractableLayerRenderer) layer).doDoubleClick(); - } - } - } - } - } -} diff --git a/src/main/java/com/sinthoras/visualprospecting/network/ProspectingNotification.java b/src/main/java/com/sinthoras/visualprospecting/network/ProspectingNotification.java index 780e5bfd..4be1da75 100644 --- a/src/main/java/com/sinthoras/visualprospecting/network/ProspectingNotification.java +++ b/src/main/java/com/sinthoras/visualprospecting/network/ProspectingNotification.java @@ -1,21 +1,18 @@ package com.sinthoras.visualprospecting.network; -import com.sinthoras.visualprospecting.VP; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + import com.sinthoras.visualprospecting.database.ClientCache; -import com.sinthoras.visualprospecting.database.UndergroundFluidPosition; import com.sinthoras.visualprospecting.database.OreVeinPosition; -import com.sinthoras.visualprospecting.database.veintypes.VeinTypeCaching; -import cpw.mods.fml.common.network.ByteBufUtils; +import com.sinthoras.visualprospecting.database.UndergroundFluidPosition; +import com.sinthoras.visualprospecting.utils.VPByteBufUtils; + import cpw.mods.fml.common.network.simpleimpl.IMessage; import cpw.mods.fml.common.network.simpleimpl.IMessageHandler; import cpw.mods.fml.common.network.simpleimpl.MessageContext; import io.netty.buffer.ByteBuf; -import net.minecraftforge.fluids.Fluid; -import net.minecraftforge.fluids.FluidRegistry; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; public class ProspectingNotification implements IMessage { @@ -24,9 +21,7 @@ public class ProspectingNotification implements IMessage { private List oreVeins; private List undergroundFluids; - public ProspectingNotification() { - - } + public ProspectingNotification() {} public ProspectingNotification(OreVeinPosition oreVeinPosition) { oreVeins = Collections.singletonList(oreVeinPosition); @@ -38,56 +33,24 @@ public ProspectingNotification(List oreVeins, List getOreVeins() { + return oreVeins; + } + + public List getUndergroundFluids() { + return undergroundFluids; + } + @Override public void fromBytes(ByteBuf buf) { - final int numberOfOreVeins = buf.readInt(); - oreVeins = new ArrayList<>(numberOfOreVeins); - for(int i = 0; i < numberOfOreVeins; i++) { - final int dimensionId = buf.readInt(); - final int chunkX = buf.readInt(); - final int chunkZ = buf.readInt(); - final String oreVeinName = ByteBufUtils.readUTF8String(buf); - oreVeins.add(new OreVeinPosition(dimensionId, chunkX, chunkZ, VeinTypeCaching.getVeinType(oreVeinName))); - } - - final int numberOfUndergroundFluids = buf.readInt(); - undergroundFluids = new ArrayList<>(numberOfUndergroundFluids); - for(int i = 0; i < numberOfUndergroundFluids; i++) { - final int dimensionId = buf.readInt(); - final int chunkX = buf.readInt(); - final int chunkZ = buf.readInt(); - final Fluid fluid = FluidRegistry.getFluid(buf.readInt()); - final int[][] chunks = new int[VP.undergroundFluidSizeChunkX][VP.undergroundFluidSizeChunkZ]; - for(int offsetChunkX = 0; offsetChunkX < VP.undergroundFluidSizeChunkX; offsetChunkX++) - for(int offsetChunkZ = 0; offsetChunkZ < VP.undergroundFluidSizeChunkZ; offsetChunkZ++) { - chunks[offsetChunkX][offsetChunkZ] = buf.readInt(); - } - undergroundFluids.add(new UndergroundFluidPosition(dimensionId, chunkX, chunkZ, fluid, chunks)); - } + oreVeins = VPByteBufUtils.ReadOreVeinPositions(buf); + undergroundFluids = VPByteBufUtils.ReadUndergroundFluidPositions(buf); } @Override public void toBytes(ByteBuf buf) { - buf.writeInt(oreVeins.size()); - for(OreVeinPosition oreVein : oreVeins) { - buf.writeInt(oreVein.dimensionId); - buf.writeInt(oreVein.chunkX); - buf.writeInt(oreVein.chunkZ); - ByteBufUtils.writeUTF8String(buf, oreVein.veinType.name); - } - - buf.writeInt(undergroundFluids.size()); - for(UndergroundFluidPosition undergroundFluid : undergroundFluids) { - buf.writeInt(undergroundFluid.dimensionId); - buf.writeInt(undergroundFluid.chunkX); - buf.writeInt(undergroundFluid.chunkZ); - buf.writeInt(undergroundFluid.fluid.getID()); - for(int offsetChunkX = 0; offsetChunkX < VP.undergroundFluidSizeChunkX; offsetChunkX++) { - for (int offsetChunkZ = 0; offsetChunkZ < VP.undergroundFluidSizeChunkZ; offsetChunkZ++) { - buf.writeInt(undergroundFluid.chunks[offsetChunkX][offsetChunkZ]); - } - } - } + VPByteBufUtils.WriteOreVeinPositions(buf, oreVeins); + VPByteBufUtils.WriteUndergroundFluidPositions(buf, undergroundFluids); } public static class Handler implements IMessageHandler { diff --git a/src/main/java/com/sinthoras/visualprospecting/network/ProspectingRequest.java b/src/main/java/com/sinthoras/visualprospecting/network/ProspectingRequest.java index 21a62092..63881290 100644 --- a/src/main/java/com/sinthoras/visualprospecting/network/ProspectingRequest.java +++ b/src/main/java/com/sinthoras/visualprospecting/network/ProspectingRequest.java @@ -1,23 +1,27 @@ package com.sinthoras.visualprospecting.network; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import net.minecraft.world.World; + +import org.jetbrains.annotations.Nullable; + import com.sinthoras.visualprospecting.Config; import com.sinthoras.visualprospecting.Utils; import com.sinthoras.visualprospecting.database.OreVeinPosition; import com.sinthoras.visualprospecting.database.ServerCache; +import com.sinthoras.visualprospecting.teams.TeamProspectionDispatcher; + +import cpw.mods.fml.common.network.ByteBufUtils; import cpw.mods.fml.common.network.simpleimpl.IMessage; import cpw.mods.fml.common.network.simpleimpl.IMessageHandler; import cpw.mods.fml.common.network.simpleimpl.MessageContext; -import gregtech.common.blocks.GT_Block_Ores_Abstract; -import gregtech.common.blocks.GT_TileEntity_Ores; +import gregtech.api.interfaces.IOreMaterial; +import gregtech.common.ores.OreInfo; +import gregtech.common.ores.OreManager; import io.netty.buffer.ByteBuf; -import net.minecraft.block.Block; -import net.minecraft.tileentity.TileEntity; -import net.minecraft.world.World; - -import java.util.*; - -import static com.sinthoras.visualprospecting.Utils.isSmallOreId; -import static com.sinthoras.visualprospecting.Utils.oreIdToMaterialId; public class ProspectingRequest implements IMessage { @@ -27,23 +31,21 @@ public class ProspectingRequest implements IMessage { private int blockX; private int blockY; private int blockZ; - private short foundOreMetaData; + private IOreMaterial foundOre; - public ProspectingRequest() { - - } + public ProspectingRequest() {} - public ProspectingRequest(int dimensionId, int blockX, int blockY, int blockZ, short foundOreMetaData) { + public ProspectingRequest(int dimensionId, int blockX, int blockY, int blockZ, IOreMaterial foundOre) { this.dimensionId = dimensionId; this.blockX = blockX; this.blockY = blockY; this.blockZ = blockZ; - this.foundOreMetaData = foundOreMetaData; + this.foundOre = foundOre; } public static boolean canSendRequest() { final long timestamp = System.currentTimeMillis(); - if(timestamp - timestampLastRequest > Config.minDelayBetweenVeinRequests) { + if (timestamp - timestampLastRequest > Config.minDelayBetweenVeinRequests) { timestampLastRequest = timestamp; return true; } @@ -56,7 +58,7 @@ public void fromBytes(ByteBuf buf) { blockX = buf.readInt(); blockY = buf.readInt(); blockZ = buf.readInt(); - foundOreMetaData = buf.readShort(); + foundOre = IOreMaterial.findMaterial(ByteBufUtils.readUTF8String(buf)); } @Override @@ -65,7 +67,7 @@ public void toBytes(ByteBuf buf) { buf.writeInt(blockX); buf.writeInt(blockY); buf.writeInt(blockZ); - buf.writeShort(foundOreMetaData); + ByteBufUtils.writeUTF8String(buf, foundOre.getInternalName()); } public static class Handler implements IMessageHandler { @@ -76,53 +78,80 @@ public static class Handler implements IMessageHandler= Config.minDelayBetweenVeinRequests - && isChunkLoaded) { - final Block block = world.getBlock(message.blockX, message.blockY, message.blockZ); - if(block instanceof GT_Block_Ores_Abstract) { - final TileEntity tileEntity = world.getTileEntity(message.blockX, message.blockY, message.blockZ); - if (tileEntity instanceof GT_TileEntity_Ores) { - final short metaData = ((GT_TileEntity_Ores) tileEntity).mMetaData; - if(isSmallOreId(metaData) == false && oreIdToMaterialId(metaData) == message.foundOreMetaData) { - lastRequestPerPlayer.put(uuid, timestamp); - - // Prioritise center vein - final OreVeinPosition centerOreVeinPosition = ServerCache.instance.getOreVein(message.dimensionId, chunkX, chunkZ); - if(centerOreVeinPosition.veinType.containsOre(message.foundOreMetaData)) { - return new ProspectingNotification(centerOreVeinPosition); - } - - // Check if neighboring veins could fit - final int centerChunkX = Utils.mapToCenterOreChunkCoord(chunkX); - final int centerChunkZ = Utils.mapToCenterOreChunkCoord(chunkZ); - for(int offsetChunkX = -3; offsetChunkX <= 3; offsetChunkX += 3) { - for (int offsetChunkZ = -3; offsetChunkZ <= 3; offsetChunkZ += 3) { - if (offsetChunkX != 0 || offsetChunkZ != 0) { - final int neighborChunkX = centerChunkX + offsetChunkX; - final int neighborChunkZ = centerChunkZ + offsetChunkZ; - final int distanceBlocks = Math.max(Math.abs(neighborChunkX - chunkX), Math.abs(neighborChunkZ - chunkZ)); - final OreVeinPosition neighborOreVeinPosition = ServerCache.instance.getOreVein(message.dimensionId, neighborChunkX, neighborChunkZ); - final int maxDistance = ((neighborOreVeinPosition.veinType.blockSize + 16) >> 4) + 1; // Equals to: ceil(blockSize / 16.0) + 1 - if (neighborOreVeinPosition.veinType.containsOre(message.foundOreMetaData) && distanceBlocks <= maxDistance) { - return new ProspectingNotification(neighborOreVeinPosition); - } - } - } - } + + if (ctx.getServerHandler().playerEntity.dimension != message.dimensionId) return null; + // max 32 blocks distance + if (distanceSquared > 32 * 32) return null; + + if (!isChunkLoaded) return null; + + ProspectingNotification response = prospect(message, world); + if (response != null) { + TeamProspectionDispatcher.deliverProspectingResults(ctx.getServerHandler().playerEntity, response); + } + return null; + } + } + + public static @Nullable ProspectingNotification prospect(ProspectingRequest message, World world) { + final int chunkX = Utils.coordBlockToChunk(message.blockX); + final int chunkZ = Utils.coordBlockToChunk(message.blockZ); + + try (OreInfo info = OreManager + .getOreInfo(world, message.blockX, message.blockY, message.blockZ)) { + if (info == null || info.isSmall || info.material != message.foundOre) return null; + + // Prioritise center vein + final OreVeinPosition centerOreVeinPosition = ServerCache.instance + .getOreVein(message.dimensionId, chunkX, chunkZ); + + if (centerOreVeinPosition.veinType.containsOre(message.foundOre)) { + return new ProspectingNotification(centerOreVeinPosition); + } + + // Check if neighboring veins could fit + final int centerChunkX = Utils.mapToCenterOreChunkCoord(chunkX); + final int centerChunkZ = Utils.mapToCenterOreChunkCoord(chunkZ); + + for (int offsetChunkX = -3; offsetChunkX <= 3; offsetChunkX += 3) { + for (int offsetChunkZ = -3; offsetChunkZ <= 3; offsetChunkZ += 3) { + if (offsetChunkX != 0 || offsetChunkZ != 0) { + + final int neighborChunkX = centerChunkX + offsetChunkX; + final int neighborChunkZ = centerChunkZ + offsetChunkZ; + + final int distanceBlocks = Math + .max(Math.abs(neighborChunkX - chunkX), Math.abs(neighborChunkZ - chunkZ)); + + final OreVeinPosition neighborOreVeinPosition = ServerCache.instance + .getOreVein(message.dimensionId, neighborChunkX, neighborChunkZ); + + // Equals to: ceil(blockSize / 16.0) + 1 + final int maxDistance = ((neighborOreVeinPosition.veinType.blockSize + 16) >> 4) + 1; + + if (neighborOreVeinPosition.veinType.containsOre(message.foundOre) + && distanceBlocks <= maxDistance) { + return new ProspectingNotification(neighborOreVeinPosition); } } } } - return null; } + + return null; } } diff --git a/src/main/java/com/sinthoras/visualprospecting/network/ProspectionSharing.java b/src/main/java/com/sinthoras/visualprospecting/network/ProspectionSharing.java index 40fb48c4..605e92fb 100644 --- a/src/main/java/com/sinthoras/visualprospecting/network/ProspectionSharing.java +++ b/src/main/java/com/sinthoras/visualprospecting/network/ProspectionSharing.java @@ -1,46 +1,50 @@ package com.sinthoras.visualprospecting.network; -import com.sinthoras.visualprospecting.Config; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import net.minecraft.entity.player.EntityPlayerMP; + import com.sinthoras.visualprospecting.VP; import com.sinthoras.visualprospecting.database.ClientCache; import com.sinthoras.visualprospecting.database.OreVeinPosition; import com.sinthoras.visualprospecting.database.TransferCache; import com.sinthoras.visualprospecting.database.UndergroundFluidPosition; -import com.sinthoras.visualprospecting.database.veintypes.VeinTypeCaching; -import cpw.mods.fml.common.network.ByteBufUtils; +import com.sinthoras.visualprospecting.teams.TeamProspectionDispatcher; +import com.sinthoras.visualprospecting.utils.VPByteBufUtils; + import cpw.mods.fml.common.network.simpleimpl.IMessage; import cpw.mods.fml.common.network.simpleimpl.IMessageHandler; import cpw.mods.fml.common.network.simpleimpl.MessageContext; import io.netty.buffer.ByteBuf; -import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraftforge.fluids.Fluid; -import net.minecraftforge.fluids.FluidRegistry; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap; public class ProspectionSharing implements IMessage { - private static final int BYTES_OVERHEAD = 2 * Byte.BYTES + 2 * Integer.BYTES; + private static final int BYTES_OVERHEAD = Byte.BYTES + 3 * Byte.BYTES + 2 * Integer.BYTES; final List oreVeins = new ArrayList<>(); final List undergroundFluids = new ArrayList<>(); private int bytesUsed = BYTES_OVERHEAD; boolean isFirstMessage = false; boolean isLastMessage = false; + boolean routeToTeam = false; - public ProspectionSharing() { - - } + public ProspectionSharing() {} public int putOreVeins(List oreVeins) { - final int availableBytes = VP.uploadSizePerPacketInBytes - bytesUsed; - final int maxAddedOreVeins = availableBytes / OreVeinPosition.getMaxBytes(); - final int addedOreVeins = Math.min(oreVeins.size(), maxAddedOreVeins); - this.oreVeins.addAll(oreVeins.subList(0, addedOreVeins)); - bytesUsed += addedOreVeins * OreVeinPosition.getMaxBytes(); + int addedOreVeins = 0; + for (OreVeinPosition vein : oreVeins) { + final int veinByteSize = vein.getPacketBytes(); + + if ((bytesUsed + veinByteSize) > VP.uploadSizePerPacketInBytes) break; + this.oreVeins.add(vein); + bytesUsed += veinByteSize; + addedOreVeins++; + } + return addedOreVeins; } @@ -61,9 +65,12 @@ public void setLastMessage(boolean isLastMessage) { this.isLastMessage = isLastMessage; } - public int getBytes() { - return BYTES_OVERHEAD - + VeinTypeCaching.getLongesOreNameLength() * oreVeins.size() + public void setRouteToTeam(boolean routeToTeam) { + this.routeToTeam = routeToTeam; + } + + public int getPacketBytes() { + return BYTES_OVERHEAD + oreVeins.stream().mapToInt(OreVeinPosition::getPacketBytes).sum() + UndergroundFluidPosition.BYTES * undergroundFluids.size(); } @@ -71,86 +78,57 @@ public int getBytes() { public void fromBytes(ByteBuf buf) { isFirstMessage = buf.readByte() > 0; isLastMessage = buf.readByte() > 0; + routeToTeam = buf.readByte() > 0; - final int numberOfOreVeins = buf.readInt(); - for(int i = 0; i < numberOfOreVeins; i++) { - final int dimensionId = buf.readInt(); - final int chunkX = buf.readInt(); - final int chunkZ = buf.readInt(); - final boolean isDepleted = buf.readByte() > 0; - final String oreVeinName = ByteBufUtils.readUTF8String(buf); - oreVeins.add(new OreVeinPosition(dimensionId, chunkX, chunkZ, VeinTypeCaching.getVeinType(oreVeinName), isDepleted)); - } - - final int numberOfUndergroundFluids = buf.readInt(); - for(int i = 0; i < numberOfUndergroundFluids; i++) { - final int dimensionId = buf.readInt(); - final int chunkX = buf.readInt(); - final int chunkZ = buf.readInt(); - final Fluid fluid = FluidRegistry.getFluid(buf.readInt()); - final int[][] chunks = new int[VP.undergroundFluidSizeChunkX][VP.undergroundFluidSizeChunkZ]; - for(int offsetChunkX = 0; offsetChunkX < VP.undergroundFluidSizeChunkX; offsetChunkX++) - for(int offsetChunkZ = 0; offsetChunkZ< VP.undergroundFluidSizeChunkZ; offsetChunkZ++) { - chunks[offsetChunkX][offsetChunkZ] = buf.readInt(); - } - undergroundFluids.add(new UndergroundFluidPosition(dimensionId, chunkX, chunkZ, fluid, chunks)); - } + oreVeins.addAll(VPByteBufUtils.ReadOreVeinPositions(buf)); + undergroundFluids.addAll(VPByteBufUtils.ReadUndergroundFluidPositions(buf)); } @Override public void toBytes(ByteBuf buf) { buf.writeByte(isFirstMessage ? 1 : 0); buf.writeByte(isLastMessage ? 1 : 0); + buf.writeByte(routeToTeam ? 1 : 0); - buf.writeInt(oreVeins.size()); - for(OreVeinPosition oreVein : oreVeins) { - buf.writeInt(oreVein.dimensionId); - buf.writeInt(oreVein.chunkX); - buf.writeInt(oreVein.chunkZ); - buf.writeByte(oreVein.isDepleted() ? 1 : 0); - ByteBufUtils.writeUTF8String(buf, oreVein.veinType.name); - } - - buf.writeInt(undergroundFluids.size()); - for(UndergroundFluidPosition undergroundFluid : undergroundFluids) { - buf.writeInt(undergroundFluid.dimensionId); - buf.writeInt(undergroundFluid.chunkX); - buf.writeInt(undergroundFluid.chunkZ); - buf.writeInt(undergroundFluid.fluid.getID()); - for(int offsetChunkX = 0; offsetChunkX < VP.undergroundFluidSizeChunkX; offsetChunkX++) { - for (int offsetChunkZ = 0; offsetChunkZ < VP.undergroundFluidSizeChunkZ; offsetChunkZ++) { - buf.writeInt(undergroundFluid.chunks[offsetChunkX][offsetChunkZ]); - } - } - } + VPByteBufUtils.WriteOreVeinPositions(buf, oreVeins); + VPByteBufUtils.WriteUndergroundFluidPositions(buf, undergroundFluids); } public static class ServerHandler implements IMessageHandler { - private static Map> oreVeins = new HashMap<>(); - private static Map> undergroundFluids = new HashMap<>(); + private static final Map> oreVeins = new HashMap<>(); + private static final Map> undergroundFluids = new HashMap<>(); + private static final Object2BooleanOpenHashMap routeToTeam = new Object2BooleanOpenHashMap<>(); @Override public IMessage onMessage(ProspectionSharing message, MessageContext ctx) { final EntityPlayerMP player = ctx.getServerHandler().playerEntity; // Optional todo: Integrate over time for proper checking - if(message.getBytes() > VP.uploadSizePerPacketInBytes) { - player.playerNetServerHandler.kickPlayerFromServer("Do not spam the server! Change your VisualProcessing configuration back to the servers!"); + if (message.getPacketBytes() > VP.uploadSizePerPacketInBytes) { + player.playerNetServerHandler.kickPlayerFromServer( + "Do not spam the server! Change your VisualProcessing configuration back to the servers!"); } - if(message.isFirstMessage) { + if (message.isFirstMessage) { oreVeins.put(player, new ArrayList<>()); undergroundFluids.put(player, new ArrayList<>()); + routeToTeam.put(player, message.routeToTeam); } - if(oreVeins.containsKey(player) == false || undergroundFluids.containsKey(player) == false) { + if (!oreVeins.containsKey(player) || !undergroundFluids.containsKey(player)) { return null; } oreVeins.get(player).addAll(message.oreVeins); undergroundFluids.get(player).addAll(message.undergroundFluids); - if(message.isLastMessage) { - TransferCache.instance.addClientProspectionData(player.getPersistentID().toString(), oreVeins.get(player), undergroundFluids.get(player)); - oreVeins.remove(player); - undergroundFluids.remove(player); + if (message.isLastMessage) { + List veins = oreVeins.remove(player); + List fluids = undergroundFluids.remove(player); + boolean toTeam = routeToTeam.removeBoolean(player); + if (toTeam) { + TeamProspectionDispatcher + .deliverProspectingResults(player, new ProspectingNotification(veins, fluids), false); + } else { + TransferCache.instance.addClientProspectionData(player.getPersistentID().toString(), veins, fluids); + } } return null; } @@ -163,16 +141,16 @@ public static class ClientHandler implements IMessageHandler(); - undergroundFluids= new ArrayList<>(); + if (message.isFirstMessage) { + oreVeins = new ArrayList<>(); + undergroundFluids = new ArrayList<>(); } - if(oreVeins == null || undergroundFluids == null) { + if (oreVeins == null || undergroundFluids == null) { return null; } oreVeins.addAll(message.oreVeins); undergroundFluids.addAll(message.undergroundFluids); - if(message.isLastMessage) { + if (message.isLastMessage) { ClientCache.instance.putOreVeins(oreVeins); ClientCache.instance.putUndergroundFluids(undergroundFluids); } diff --git a/src/main/java/com/sinthoras/visualprospecting/network/RequestTeamUploadMessage.java b/src/main/java/com/sinthoras/visualprospecting/network/RequestTeamUploadMessage.java new file mode 100644 index 00000000..a292d2b5 --- /dev/null +++ b/src/main/java/com/sinthoras/visualprospecting/network/RequestTeamUploadMessage.java @@ -0,0 +1,35 @@ +package com.sinthoras.visualprospecting.network; + +import com.sinthoras.visualprospecting.database.ClientCache; +import com.sinthoras.visualprospecting.task.SnapshotUploadTask; +import com.sinthoras.visualprospecting.task.TaskManager; +import com.sinthoras.visualprospecting.teams.TeamProspectionData; + +import cpw.mods.fml.common.network.simpleimpl.IMessage; +import cpw.mods.fml.common.network.simpleimpl.IMessageHandler; +import cpw.mods.fml.common.network.simpleimpl.MessageContext; +import io.netty.buffer.ByteBuf; + +/** + * Server → client trigger: ask the recipient to push their {@link ClientCache} to the server, routed into their team's + * {@link TeamProspectionData}. + */ +public class RequestTeamUploadMessage implements IMessage { + + public RequestTeamUploadMessage() {} + + @Override + public void fromBytes(ByteBuf buf) {} + + @Override + public void toBytes(ByteBuf buf) {} + + public static class ClientHandler implements IMessageHandler { + + @Override + public IMessage onMessage(RequestTeamUploadMessage message, MessageContext ctx) { + TaskManager.CLIENT_INSTANCE.addTask(new SnapshotUploadTask(true)); + return null; + } + } +} diff --git a/src/main/java/com/sinthoras/visualprospecting/network/TeamCatchupNotification.java b/src/main/java/com/sinthoras/visualprospecting/network/TeamCatchupNotification.java new file mode 100644 index 00000000..533e0165 --- /dev/null +++ b/src/main/java/com/sinthoras/visualprospecting/network/TeamCatchupNotification.java @@ -0,0 +1,47 @@ +package com.sinthoras.visualprospecting.network; + +import java.util.Collections; +import java.util.List; + +import com.sinthoras.visualprospecting.database.ClientCache; +import com.sinthoras.visualprospecting.database.OreVeinPosition; +import com.sinthoras.visualprospecting.database.UndergroundFluidPosition; + +import cpw.mods.fml.common.network.simpleimpl.IMessage; +import cpw.mods.fml.common.network.simpleimpl.IMessageHandler; +import cpw.mods.fml.common.network.simpleimpl.MessageContext; + +/** + * Similar to {@link ProspectingNotification} but for data being sent as recorded team state instead of a live + * prospection event. + *

+ * Used by the team-sharing catch-up paths (player login and post-team-merge). + */ +public class TeamCatchupNotification extends ProspectingNotification { + + public TeamCatchupNotification() { + super(); + } + + public TeamCatchupNotification(List oreVeins, List undergroundFluids) { + super(oreVeins, undergroundFluids); + } + + public static TeamCatchupNotification veins(List oreVeins) { + return new TeamCatchupNotification(oreVeins, Collections.emptyList()); + } + + public static TeamCatchupNotification fluids(List undergroundFluids) { + return new TeamCatchupNotification(Collections.emptyList(), undergroundFluids); + } + + public static class ClientHandler implements IMessageHandler { + + @Override + public IMessage onMessage(TeamCatchupNotification message, MessageContext ctx) { + ClientCache.instance.putOreVeins(message.getOreVeins(), true); + ClientCache.instance.putUndergroundFluids(message.getUndergroundFluids(), true); + return null; + } + } +} diff --git a/src/main/java/com/sinthoras/visualprospecting/network/VeinDepletionMessage.java b/src/main/java/com/sinthoras/visualprospecting/network/VeinDepletionMessage.java new file mode 100644 index 00000000..f6c320da --- /dev/null +++ b/src/main/java/com/sinthoras/visualprospecting/network/VeinDepletionMessage.java @@ -0,0 +1,69 @@ +package com.sinthoras.visualprospecting.network; + +import net.minecraft.entity.player.EntityPlayerMP; + +import com.sinthoras.visualprospecting.database.ClientCache; +import com.sinthoras.visualprospecting.database.OreVeinPosition; +import com.sinthoras.visualprospecting.teams.TeamProspectionDispatcher; + +import cpw.mods.fml.common.network.simpleimpl.IMessage; +import cpw.mods.fml.common.network.simpleimpl.IMessageHandler; +import cpw.mods.fml.common.network.simpleimpl.MessageContext; +import io.netty.buffer.ByteBuf; + +public class VeinDepletionMessage implements IMessage { + + private int dimensionId; + private int chunkX; + private int chunkZ; + private boolean depleted; + + public VeinDepletionMessage() {} + + public VeinDepletionMessage(int dimensionId, int chunkX, int chunkZ, boolean depleted) { + this.dimensionId = dimensionId; + this.chunkX = chunkX; + this.chunkZ = chunkZ; + this.depleted = depleted; + } + + @Override + public void fromBytes(ByteBuf buf) { + dimensionId = buf.readInt(); + chunkX = buf.readInt(); + chunkZ = buf.readInt(); + depleted = buf.readBoolean(); + } + + @Override + public void toBytes(ByteBuf buf) { + buf.writeInt(dimensionId); + buf.writeInt(chunkX); + buf.writeInt(chunkZ); + buf.writeBoolean(depleted); + } + + public static class ServerHandler implements IMessageHandler { + + @Override + public IMessage onMessage(VeinDepletionMessage msg, MessageContext ctx) { + EntityPlayerMP player = ctx.getServerHandler().playerEntity; + TeamProspectionDispatcher + .handleDepletionToggle(player, msg.dimensionId, msg.chunkX, msg.chunkZ, msg.depleted); + return null; + } + } + + public static class ClientHandler implements IMessageHandler { + + @Override + public IMessage onMessage(VeinDepletionMessage msg, MessageContext ctx) { + OreVeinPosition vein = ClientCache.instance.getOreVein(msg.dimensionId, msg.chunkX, msg.chunkZ); + if (vein == null || vein == OreVeinPosition.EMPTY_VEIN) return null; + if (vein.isDepleted() != msg.depleted) { + vein.toggleDepleted(); + } + return null; + } + } +} diff --git a/src/main/java/com/sinthoras/visualprospecting/network/WorldIdNotification.java b/src/main/java/com/sinthoras/visualprospecting/network/WorldIdNotification.java index b6fb9315..a6b3692f 100644 --- a/src/main/java/com/sinthoras/visualprospecting/network/WorldIdNotification.java +++ b/src/main/java/com/sinthoras/visualprospecting/network/WorldIdNotification.java @@ -1,6 +1,7 @@ package com.sinthoras.visualprospecting.network; import com.sinthoras.visualprospecting.database.ClientCache; + import cpw.mods.fml.common.network.ByteBufUtils; import cpw.mods.fml.common.network.simpleimpl.IMessage; import cpw.mods.fml.common.network.simpleimpl.IMessageHandler; @@ -11,9 +12,7 @@ public class WorldIdNotification implements IMessage { private String worldId; - public WorldIdNotification() { - - } + public WorldIdNotification() {} public WorldIdNotification(String worldId) { this.worldId = worldId; diff --git a/src/main/java/com/sinthoras/visualprospecting/task/ITask.java b/src/main/java/com/sinthoras/visualprospecting/task/ITask.java index b93cd694..c6fedc2f 100644 --- a/src/main/java/com/sinthoras/visualprospecting/task/ITask.java +++ b/src/main/java/com/sinthoras/visualprospecting/task/ITask.java @@ -3,8 +3,7 @@ public interface ITask { /* - Is called every game tick - Return true to terminate task + * Is called every game tick Return true to terminate task */ public boolean process(); } diff --git a/src/main/java/com/sinthoras/visualprospecting/task/SnapshotDownloadTask.java b/src/main/java/com/sinthoras/visualprospecting/task/SnapshotDownloadTask.java index d06e6342..a5ffc15c 100644 --- a/src/main/java/com/sinthoras/visualprospecting/task/SnapshotDownloadTask.java +++ b/src/main/java/com/sinthoras/visualprospecting/task/SnapshotDownloadTask.java @@ -1,17 +1,19 @@ package com.sinthoras.visualprospecting.task; +import java.util.List; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.IChatComponent; + import com.sinthoras.visualprospecting.Config; import com.sinthoras.visualprospecting.VP; import com.sinthoras.visualprospecting.database.OreVeinPosition; import com.sinthoras.visualprospecting.database.TransferCache; import com.sinthoras.visualprospecting.database.UndergroundFluidPosition; import com.sinthoras.visualprospecting.network.ProspectionSharing; -import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.util.ChatComponentTranslation; -import net.minecraft.util.EnumChatFormatting; -import net.minecraft.util.IChatComponent; - -import java.util.List; public class SnapshotDownloadTask implements ITask { @@ -29,8 +31,10 @@ public SnapshotDownloadTask(String authorUuid, EntityPlayerMP player) { @Override public boolean process() { + if (!MinecraftServer.getServer().getConfigurationManager().playerEntityList.contains(player)) return true; + final long timestamp = System.currentTimeMillis(); - if(timestamp - lastUpload > 1000 / Config.uploadPacketsPerSecond && listsEmpty() == false) { + if (timestamp - lastUpload > 1000 / Config.uploadPacketsPerSecond && !listsEmpty()) { lastUpload = timestamp; final ProspectionSharing packet = new ProspectionSharing(); @@ -47,8 +51,10 @@ public boolean process() { VP.network.sendTo(packet, player); - if(listsEmpty()) { - final IChatComponent notification = new ChatComponentTranslation("item.visualprospecting.prospectorslog.reading.end", player.getDisplayName()); + if (listsEmpty()) { + final IChatComponent notification = new ChatComponentTranslation( + "item.visualprospecting.prospectorslog.reading.end", + player.getDisplayName()); notification.getChatStyle().setItalic(true); notification.getChatStyle().setColor(EnumChatFormatting.GRAY); player.addChatMessage(notification); diff --git a/src/main/java/com/sinthoras/visualprospecting/task/SnapshotUploadTask.java b/src/main/java/com/sinthoras/visualprospecting/task/SnapshotUploadTask.java index cb12d817..8945f53a 100644 --- a/src/main/java/com/sinthoras/visualprospecting/task/SnapshotUploadTask.java +++ b/src/main/java/com/sinthoras/visualprospecting/task/SnapshotUploadTask.java @@ -1,5 +1,9 @@ package com.sinthoras.visualprospecting.task; +import java.util.List; + +import net.minecraft.client.Minecraft; + import com.sinthoras.visualprospecting.Config; import com.sinthoras.visualprospecting.VP; import com.sinthoras.visualprospecting.database.ClientCache; @@ -7,24 +11,30 @@ import com.sinthoras.visualprospecting.database.UndergroundFluidPosition; import com.sinthoras.visualprospecting.network.ProspectionSharing; -import java.util.List; - public class SnapshotUploadTask implements ITask { private final List oreVeins; private final List undergroundFluids; + private final boolean routeToTeam; private long lastUpload = 0; private boolean firstMessage = true; public SnapshotUploadTask() { + this(false); + } + + public SnapshotUploadTask(boolean routeToTeam) { oreVeins = ClientCache.instance.getAllOreVeins(); undergroundFluids = ClientCache.instance.getAllUndergroundFluids(); + this.routeToTeam = routeToTeam; } @Override public boolean process() { + if (Minecraft.getMinecraft().getNetHandler() == null) return true; + final long timestamp = System.currentTimeMillis(); - if(timestamp - lastUpload > 1000 / Config.uploadPacketsPerSecond && listsEmpty() == false) { + if (timestamp - lastUpload > 1000 / Config.uploadPacketsPerSecond && !listsEmpty()) { lastUpload = timestamp; final ProspectionSharing packet = new ProspectionSharing(); @@ -38,6 +48,7 @@ public boolean process() { firstMessage = false; packet.setLastMessage(listsEmpty()); + packet.setRouteToTeam(routeToTeam); VP.network.sendToServer(packet); } diff --git a/src/main/java/com/sinthoras/visualprospecting/task/TaskManager.java b/src/main/java/com/sinthoras/visualprospecting/task/TaskManager.java index 7fda2a27..4224e757 100644 --- a/src/main/java/com/sinthoras/visualprospecting/task/TaskManager.java +++ b/src/main/java/com/sinthoras/visualprospecting/task/TaskManager.java @@ -3,9 +3,15 @@ import java.util.ArrayList; import java.util.List; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; + public class TaskManager { - public static final TaskManager instance = new TaskManager(); + // Both instances are initialized in the sided proxy + @SideOnly(Side.CLIENT) + public static TaskManager CLIENT_INSTANCE; + public static TaskManager SERVER_INSTANCE; private final List tasks = new ArrayList<>(); diff --git a/src/main/java/com/sinthoras/visualprospecting/teams/TeamCatchupHandler.java b/src/main/java/com/sinthoras/visualprospecting/teams/TeamCatchupHandler.java new file mode 100644 index 00000000..2f5033cd --- /dev/null +++ b/src/main/java/com/sinthoras/visualprospecting/teams/TeamCatchupHandler.java @@ -0,0 +1,199 @@ +package com.sinthoras.visualprospecting.teams; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.world.World; +import net.minecraftforge.common.DimensionManager; + +import com.gtnewhorizon.gtnhlib.eventbus.EventBusSubscriber; +import com.gtnewhorizon.gtnhlib.teams.Team; +import com.gtnewhorizon.gtnhlib.teams.TeamEvents.TeamMergeEvent; +import com.gtnewhorizon.gtnhlib.teams.TeamManager; +import com.sinthoras.visualprospecting.Config; +import com.sinthoras.visualprospecting.Utils; +import com.sinthoras.visualprospecting.VP; +import com.sinthoras.visualprospecting.database.OreVeinPosition; +import com.sinthoras.visualprospecting.database.ServerCache; +import com.sinthoras.visualprospecting.database.UndergroundFluidPosition; +import com.sinthoras.visualprospecting.database.veintypes.VeinType; +import com.sinthoras.visualprospecting.network.TeamCatchupNotification; + +import cpw.mods.fml.common.eventhandler.EventPriority; +import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import cpw.mods.fml.common.gameevent.PlayerEvent.PlayerChangedDimensionEvent; +import cpw.mods.fml.common.gameevent.PlayerEvent.PlayerLoggedInEvent; +import cpw.mods.fml.common.gameevent.PlayerEvent.PlayerLoggedOutEvent; +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; +import it.unimi.dsi.fastutil.ints.IntSet; +import it.unimi.dsi.fastutil.longs.LongIterator; +import it.unimi.dsi.fastutil.longs.LongSet; + +/** + * Lazily push the team's prospection record to a player, one dimension at a time, as they enter it. Only catchup once + * per dimension per session as the rest is done live. + *

+ * Allow for player to catch up to what happened in the team when they were offline. + *

+ * Data is split into type and sent in batched notifications. + */ +@EventBusSubscriber +public final class TeamCatchupHandler { + + // Caps for one ProspectingNotification: (With ton of margin) + // ~20 B per vein -> 5000 veins ~= 100 KB per packet + // ~272 B per fluid -> 400 fluids ~= 109 KB per packet + private static final int VEINS_PER_PACKET = 5000; + private static final int FLUIDS_PER_PACKET = 400; + + private static final Map SENT_DIMS = new HashMap<>(); + + private TeamCatchupHandler() {} + + // Need to run after GTNHLib or we may have no team to load + @SubscribeEvent(priority = EventPriority.LOWEST) + public static void onPlayerLogin(PlayerLoggedInEvent event) { + if (!Config.enableTeamSharing) return; + if (!(event.player instanceof EntityPlayerMP playerMP)) return; + SENT_DIMS.remove(playerMP.getUniqueID()); + + sendDimIfNeeded(playerMP, playerMP.dimension); + } + + @SubscribeEvent + public static void onPlayerChangedDimension(PlayerChangedDimensionEvent event) { + if (!Config.enableTeamSharing) return; + if (!(event.player instanceof EntityPlayerMP playerMP)) return; + + sendDimIfNeeded(playerMP, event.toDim); + } + + @SubscribeEvent + public static void onPlayerLogout(PlayerLoggedOutEvent event) { + if (!(event.player instanceof EntityPlayerMP playerMP)) return; + SENT_DIMS.remove(playerMP.getUniqueID()); + } + + @SubscribeEvent + public static void onTeamMerge(TeamMergeEvent event) { + if (!Config.enableTeamSharing) return; + + Team surviving = event.surviving; + if (surviving == null) return; + + TeamProspectionData data = (TeamProspectionData) surviving.getData(TeamProspectionData.DATA_KEY); + if (data == null) return; + markRepairedTeamDataDirty(surviving, data); + + // Update every player with new merged data, only for dimensions they visited since login + TeamManager.forEachOnlineTeamMember(surviving, member -> { + VP.LOG.debug("[onTeamMerge] Sending catchup to: {}", member); + + IntSet alreadySent = SENT_DIMS.get(member.getUniqueID()); + if (alreadySent == null || alreadySent.isEmpty()) return; + + alreadySent.forEach((int dim) -> { + sendDimVeins(member, data, dim); + sendDimFluids(member, data, dim); + }); + }); + } + + private static void sendDimIfNeeded(EntityPlayerMP player, int dim) { + UUID uuid = player.getUniqueID(); + IntSet sent = SENT_DIMS.computeIfAbsent(uuid, k -> new IntOpenHashSet()); + if (!sent.add(dim)) return; + + Team team = TeamManager.getTeamByPlayer(uuid); + if (team == null) return; + TeamProspectionData data = (TeamProspectionData) team.getData(TeamProspectionData.DATA_KEY); + if (data == null) return; + markRepairedTeamDataDirty(team, data); + + sendDimVeins(player, data, dim); + sendDimFluids(player, data, dim); + } + + private static void sendDimVeins(EntityPlayerMP player, TeamProspectionData data, int dim) { + LongSet veinKeys = data.getDiscoveredVeinKeys(dim); + if (veinKeys.isEmpty()) return; + + LongSet depletedKeys = data.getDepletedVeinKeys(dim); + List batch = new ArrayList<>(Math.min(VEINS_PER_PACKET, veinKeys.size())); + + for (LongIterator it = veinKeys.iterator(); it.hasNext();) { + long key = it.nextLong(); + int chunkX = TeamProspectionData.veinChunkX(key); + int chunkZ = TeamProspectionData.veinChunkZ(key); + + OreVeinPosition vein = ServerCache.instance.getOreVein(dim, chunkX, chunkZ); + if (vein == null || vein.veinType == VeinType.NO_VEIN) { + continue; + } + + boolean teamDepleted = depletedKeys.contains(key); + if (vein.isDepleted() != teamDepleted) { + vein = new OreVeinPosition(dim, chunkX, chunkZ, vein.veinType, teamDepleted, vein.getSource()); + } + batch.add(vein); + + if (batch.size() >= VEINS_PER_PACKET) { + flushVeins(player, batch); + batch = new ArrayList<>(VEINS_PER_PACKET); + } + } + flushVeins(player, batch); + } + + private static void flushVeins(EntityPlayerMP player, List batch) { + if (batch.isEmpty()) return; + VP.network.sendTo(TeamCatchupNotification.veins(batch), player); + } + + private static void sendDimFluids(EntityPlayerMP player, TeamProspectionData data, int dim) { + LongSet fluidKeys = data.getDiscoveredFluidKeys(dim); + if (fluidKeys.isEmpty()) return; + + World world = DimensionManager.getWorld(dim); + // Can't retrieve fluid if dim not loaded. + if (world == null) return; + + List batch = new ArrayList<>(Math.min(FLUIDS_PER_PACKET, fluidKeys.size())); + + for (LongIterator it = fluidKeys.iterator(); it.hasNext();) { + long key = it.nextLong(); + int chunkX = TeamProspectionData.fluidChunkX(key); + int chunkZ = TeamProspectionData.fluidChunkZ(key); + int blockX = Utils.coordChunkToBlock(chunkX); + int blockZ = Utils.coordChunkToBlock(chunkZ); + + List foundUndergroundFluids = ServerCache.instance + .prospectUndergroundFluidBlockRadius(world, blockX, blockZ, 0); + for (UndergroundFluidPosition fp : foundUndergroundFluids) { + if (!fp.isProspected()) continue; + batch.add(fp); + if (batch.size() >= FLUIDS_PER_PACKET) { + flushFluids(player, batch); + batch = new ArrayList<>(FLUIDS_PER_PACKET); + } + } + } + flushFluids(player, batch); + } + + private static void flushFluids(EntityPlayerMP player, List batch) { + if (batch.isEmpty()) return; + VP.network.sendTo(TeamCatchupNotification.fluids(batch), player); + } + + private static void markRepairedTeamDataDirty(Team team, TeamProspectionData data) { + if (data.consumeRepairedVeinCoordinates()) { + team.markDirty(); + VP.LOG.warn("Repaired ore vein coordinates in team prospection data for the current oregen pattern."); + } + } +} diff --git a/src/main/java/com/sinthoras/visualprospecting/teams/TeamProspectionData.java b/src/main/java/com/sinthoras/visualprospecting/teams/TeamProspectionData.java new file mode 100644 index 00000000..b948a874 --- /dev/null +++ b/src/main/java/com/sinthoras/visualprospecting/teams/TeamProspectionData.java @@ -0,0 +1,387 @@ +package com.sinthoras.visualprospecting.teams; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.Arrays; +import java.util.Base64; +import java.util.UUID; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.common.util.Constants; + +import com.gtnewhorizon.gtnhlib.teams.ITeamData; +import com.gtnewhorizon.gtnhlib.teams.Team; +import com.gtnewhorizon.gtnhlib.teams.TeamDataTransferReason; +import com.sinthoras.visualprospecting.Config; +import com.sinthoras.visualprospecting.Utils; +import com.sinthoras.visualprospecting.VP; + +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; +import it.unimi.dsi.fastutil.ints.IntSet; +import it.unimi.dsi.fastutil.longs.LongOpenHashSet; +import it.unimi.dsi.fastutil.longs.LongSet; +import it.unimi.dsi.fastutil.longs.LongSets; + +/** + * Per-team prospection record. Tracks which ore veins / underground fluids a team has discovered, which veins are + * depleted, per dimension. + *

+ * Only coordinate keys are stored. Full {@code OreVeinPosition} / {@code UndergroundFluidPosition} infos (vein types, + * fluid amounts) are recovered from {@code ServerCache}. + *

+ * The vein/fluid/depleted state is stored into a blob as a single gzipped base64 {@code NBTTagString}. + *

+ * On server restart the data sent by GTNHLib is not expanded until first access. + */ +public class TeamProspectionData implements ITeamData { + + public static final String DATA_KEY = "visualprospecting"; + + private static final String TAG_VERSION = "version"; + private static final String TAG_BLOB = "blob"; + + private static final int FORMAT_VERSION = 1; + + // Team Data - Raw + private byte[] pendingBlob; + // Team Data - Expanded + private Int2ObjectMap discoveredVeins; + private Int2ObjectMap discoveredFluids; + private Int2ObjectMap depletedVeins; + // Team Data - Status + private boolean expanded; + private boolean repairedVeinCoordinates; + private byte[] unknownVersionBlob; + private int unknownVersion; + + // Lazy team data expansion + private void ensureExpanded() { + if (expanded) return; + + discoveredVeins = new Int2ObjectOpenHashMap<>(); + discoveredFluids = new Int2ObjectOpenHashMap<>(); + depletedVeins = new Int2ObjectOpenHashMap<>(); + if (pendingBlob != null) { + try { + decodeBlobInto(pendingBlob, discoveredVeins, discoveredFluids, depletedVeins); + } catch (IOException e) { + VP.LOG.error("Failed to decode team prospection data.", e); + } + pendingBlob = null; + } + repairedVeinCoordinates = normalizeVeinCoordinates(discoveredVeins) | normalizeVeinCoordinates(depletedVeins); + expanded = true; + } + + public boolean consumeRepairedVeinCoordinates() { + ensureExpanded(); + boolean repaired = repairedVeinCoordinates; + repairedVeinCoordinates = false; + return repaired; + } + + private static boolean normalizeVeinCoordinates(Int2ObjectMap veins) { + boolean repaired = false; + for (LongSet keys : veins.values()) { + long[] oldKeys = keys.toLongArray(); + boolean changed = false; + for (long key : oldKeys) { + if (veinChunkX(key) != Utils.mapToCenterOreChunkCoord(veinChunkX(key)) + || veinChunkZ(key) != Utils.mapToCenterOreChunkCoord(veinChunkZ(key))) { + changed = true; + break; + } + } + if (!changed) continue; + + keys.clear(); + for (long key : oldKeys) { + keys.add( + packVein( + Utils.mapToCenterOreChunkCoord(veinChunkX(key)), + Utils.mapToCenterOreChunkCoord(veinChunkZ(key)))); + } + repaired = true; + } + return repaired; + } + + // Pack an Ore vein position + static long packVein(int chunkX, int chunkZ) { + return ((long) chunkX << 32) | (chunkZ & 0xFFFFFFFFL); + } + + static int veinChunkX(long key) { + return (int) (key >>> 32); + } + + static int veinChunkZ(long key) { + return (int) key; + } + + // Pack an Underground Fluids position. They are on a 8x8 pattern so we can divide by 8 + static long packFluid(int chunkX, int chunkZ) { + return ((long) (chunkX >> 3) << 32) | ((chunkZ >> 3) & 0xFFFFFFFFL); + } + + static int fluidChunkX(long key) { + return ((int) (key >>> 32)) << 3; + } + + static int fluidChunkZ(long key) { + return ((int) key) << 3; + } + + // Add ore vein to team record + public boolean addVein(int dim, int chunkX, int chunkZ) { + ensureExpanded(); + return getOrCreate(discoveredVeins, dim).add(packVein(chunkX, chunkZ)); + } + + // Add underground fluid to team record + public boolean addFluid(int dim, int chunkX, int chunkZ) { + ensureExpanded(); + return getOrCreate(discoveredFluids, dim).add(packFluid(chunkX, chunkZ)); + } + + // Record an ore vein as depleted + public boolean setVeinDepleted(int dim, int chunkX, int chunkZ, boolean depleted) { + ensureExpanded(); + long key = packVein(chunkX, chunkZ); + if (depleted) { + return getOrCreate(depletedVeins, dim).add(key); + } + LongSet set = depletedVeins.get(dim); + return set != null && set.remove(key); + } + + // Delete all data for a team + public void clear() { + pendingBlob = null; + unknownVersionBlob = null; + unknownVersion = 0; + discoveredVeins = new Int2ObjectOpenHashMap<>(); + discoveredFluids = new Int2ObjectOpenHashMap<>(); + depletedVeins = new Int2ObjectOpenHashMap<>(); + repairedVeinCoordinates = false; + expanded = true; + } + + public LongSet getDiscoveredVeinKeys(int dim) { + ensureExpanded(); + return discoveredVeins.getOrDefault(dim, LongSets.EMPTY_SET); + } + + public LongSet getDiscoveredFluidKeys(int dim) { + ensureExpanded(); + return discoveredFluids.getOrDefault(dim, LongSets.EMPTY_SET); + } + + public LongSet getDepletedVeinKeys(int dim) { + ensureExpanded(); + return depletedVeins.getOrDefault(dim, LongSets.EMPTY_SET); + } + + public boolean isVeinDiscovered(int dim, int chunkX, int chunkZ) { + ensureExpanded(); + LongSet set = discoveredVeins.get(dim); + return set != null && set.contains(packVein(chunkX, chunkZ)); + } + + public boolean isVeinDepleted(int dim, int chunkX, int chunkZ) { + ensureExpanded(); + LongSet set = depletedVeins.get(dim); + return set != null && set.contains(packVein(chunkX, chunkZ)); + } + + public IntSet knownDimensions() { + ensureExpanded(); + IntSet dims = new IntOpenHashSet(); + dims.addAll(discoveredVeins.keySet()); + dims.addAll(discoveredFluids.keySet()); + dims.addAll(depletedVeins.keySet()); + return dims; + } + + @Override + public void writeToNBT(NBTTagCompound tag) { + // In case of unknown FORMAT_VERSION we write OG data untouched + if (unknownVersionBlob != null) { + tag.setInteger(TAG_VERSION, unknownVersion); + tag.setString(TAG_BLOB, Base64.getEncoder().encodeToString(unknownVersionBlob)); + return; + } + + byte[] blob; + if (expanded) { + blob = encodeBlob(discoveredVeins, discoveredFluids, depletedVeins); + } else if (pendingBlob != null) { + blob = pendingBlob; + } else return; + if (blob.length == 0) return; + tag.setInteger(TAG_VERSION, FORMAT_VERSION); + tag.setString(TAG_BLOB, Base64.getEncoder().encodeToString(blob)); + } + + @Override + public void readFromNBT(NBTTagCompound tag) { + expanded = false; + pendingBlob = null; + unknownVersionBlob = null; + unknownVersion = 0; + discoveredVeins = null; + discoveredFluids = null; + depletedVeins = null; + repairedVeinCoordinates = false; + + if (!tag.hasKey(TAG_BLOB, Constants.NBT.TAG_STRING)) return; + int version = tag.getInteger(TAG_VERSION); + String b64 = tag.getString(TAG_BLOB); + if (b64.isEmpty()) return; + + byte[] raw; + try { + raw = Base64.getDecoder().decode(b64); + } catch (IllegalArgumentException e) { + VP.LOG.error("Team prospection data blob is not valid base64. Starting empty", e); + return; + } + if (version != FORMAT_VERSION) { + VP.LOG.error( + "Team prospection data is format version {}, this supports v{}. New changes won't be recorded.", + version, + FORMAT_VERSION); + unknownVersion = version; + unknownVersionBlob = raw; + return; + } + pendingBlob = raw; + } + + @Override + public void mergeData(Team consumed, Team surviving, ITeamData oldTeamData) { + if (oldTeamData instanceof TeamProspectionData other) { + ensureExpanded(); + other.ensureExpanded(); + mergeFrom(other); + } + } + + @Override + public void transferData(Team prevTeam, Team newTeam, UUID playerId, ITeamData prevTeamData, + TeamDataTransferReason reason) { + if (!(prevTeamData instanceof TeamProspectionData prev)) return; + if (reason == TeamDataTransferReason.JoinedNewTeam && !Config.keepProspectionOnTeamLeave) return; + ensureExpanded(); + prev.ensureExpanded(); + mergeFrom(prev); + } + + private void mergeFrom(TeamProspectionData other) { + mergeMap(other.discoveredVeins, discoveredVeins); + mergeMap(other.discoveredFluids, discoveredFluids); + mergeMap(other.depletedVeins, depletedVeins); + } + + private static void mergeMap(Int2ObjectMap source, Int2ObjectMap target) { + if (source == null) return; + for (Int2ObjectMap.Entry entry : source.int2ObjectEntrySet()) { + int dim = entry.getIntKey(); + getOrCreate(target, dim).addAll(entry.getValue()); + } + } + + private static LongSet getOrCreate(Int2ObjectMap map, int dim) { + return map.computeIfAbsent(dim, value -> new LongOpenHashSet()); + } + + /** + * Data structure used:
+ *  int numDims
+ *  for each dim:
+ *    int dimId
+ *    int veinCount
+ *    long veinKeys[veinCount]
+ *    int fluidCount;
+ *    long fluidKeys[fluidCount]
+ *    int depletedCount
+ *    long depletedKeys[depletedCount]
+ *
+ * Data is sorted to improve gzip efficiency + */ + private static byte[] encodeBlob(Int2ObjectMap veins, Int2ObjectMap fluids, + Int2ObjectMap depleted) { + IntSet allDims = new IntOpenHashSet(); + if (veins != null) allDims.addAll(veins.keySet()); + if (fluids != null) allDims.addAll(fluids.keySet()); + if (depleted != null) allDims.addAll(depleted.keySet()); + + int[] dimsToWrite = allDims.intStream() + .filter(d -> !isEmpty(veins, d) || !isEmpty(fluids, d) || !isEmpty(depleted, d)).sorted().toArray(); + if (dimsToWrite.length == 0) return EMPTY_BYTES; + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + try (GZIPOutputStream gz = new GZIPOutputStream(bos); DataOutputStream out = new DataOutputStream(gz)) { + out.writeInt(dimsToWrite.length); + for (int dim : dimsToWrite) { + out.writeInt(dim); + writeLongSet(out, veins == null ? null : veins.get(dim)); + writeLongSet(out, fluids == null ? null : fluids.get(dim)); + writeLongSet(out, depleted == null ? null : depleted.get(dim)); + } + } catch (IOException e) { + throw new UncheckedIOException("Failed to encode team prospection blob", e); + } + return bos.toByteArray(); + } + + private static boolean isEmpty(Int2ObjectMap map, int dim) { + if (map == null) return true; + LongSet set = map.get(dim); + return set == null || set.isEmpty(); + } + + private static void writeLongSet(DataOutputStream out, LongSet set) throws IOException { + if (set == null || set.isEmpty()) { + out.writeInt(0); + return; + } + long[] sorted = set.toLongArray(); + Arrays.sort(sorted); + out.writeInt(sorted.length); + for (long k : sorted) { + out.writeLong(k); + } + } + + private static void decodeBlobInto(byte[] blob, Int2ObjectMap veinsOut, Int2ObjectMap fluidsOut, + Int2ObjectMap depletedOut) throws IOException { + try (GZIPInputStream gz = new GZIPInputStream(new ByteArrayInputStream(blob)); + DataInputStream in = new DataInputStream(gz)) { + int numDims = in.readInt(); + for (int i = 0; i < numDims; i++) { + int dim = in.readInt(); + readIntoSet(in, getOrCreate(veinsOut, dim)); + readIntoSet(in, getOrCreate(fluidsOut, dim)); + readIntoSet(in, getOrCreate(depletedOut, dim)); + } + } + } + + private static void readIntoSet(DataInputStream in, LongSet target) throws IOException { + int count = in.readInt(); + for (int i = 0; i < count; i++) { + target.add(in.readLong()); + } + } + + private static final byte[] EMPTY_BYTES = new byte[0]; +} diff --git a/src/main/java/com/sinthoras/visualprospecting/teams/TeamProspectionDispatcher.java b/src/main/java/com/sinthoras/visualprospecting/teams/TeamProspectionDispatcher.java new file mode 100644 index 00000000..fe059260 --- /dev/null +++ b/src/main/java/com/sinthoras/visualprospecting/teams/TeamProspectionDispatcher.java @@ -0,0 +1,133 @@ +package com.sinthoras.visualprospecting.teams; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import net.minecraft.entity.player.EntityPlayerMP; + +import com.gtnewhorizon.gtnhlib.teams.Team; +import com.gtnewhorizon.gtnhlib.teams.TeamManager; +import com.sinthoras.visualprospecting.Config; +import com.sinthoras.visualprospecting.VP; +import com.sinthoras.visualprospecting.database.OreVeinPosition; +import com.sinthoras.visualprospecting.database.UndergroundFluidPosition; +import com.sinthoras.visualprospecting.database.veintypes.VeinType; +import com.sinthoras.visualprospecting.network.ProspectingNotification; +import com.sinthoras.visualprospecting.network.VeinDepletionMessage; + +/** + * Entry point for delivering prospection results from the server to the player. + *

+ * If team sharing is enabled, store results in the team's {@link TeamProspectionData} then broadcast to online + * teammates. + */ +public final class TeamProspectionDispatcher { + + private TeamProspectionDispatcher() {} + + /** + * Send prospection results to {@code player}, then route through team sharing. + * + * @param player the player whose action produced these results + * @param oreVeins ore veins to deliver. May be empty; must not be {@code null}. + * @param fluids underground fluid positions to deliver. May be empty; must not be {@code null}. + */ + public static void deliverProspectingResults(EntityPlayerMP player, List oreVeins, + List fluids) { + deliverProspectingResults(player, new ProspectingNotification(oreVeins, fluids), true); + } + + public static void deliverProspectingResults(EntityPlayerMP player, ProspectingNotification notification) { + deliverProspectingResults(player, notification, true); + } + + public static void deliverProspectingResults(EntityPlayerMP player, ProspectingNotification notification, + boolean notifySelf) { + if (player == null || player.playerNetServerHandler == null) return; + + if (notifySelf) { + VP.network.sendTo(notification, player); + } + + if (!Config.enableTeamSharing) return; + + Team team = TeamManager.getTeamByPlayer(player.getUniqueID()); + if (team == null) return; + TeamProspectionData data = (TeamProspectionData) team.getData(TeamProspectionData.DATA_KEY); + if (data == null) return; + + // Update the team's record + List newVeins = filterNewVeins(data, notification.getOreVeins()); + List newFluids = filterNewFluids(data, notification.getUndergroundFluids()); + if (newVeins.isEmpty() && newFluids.isEmpty()) return; + + team.markDirty(); + + // No one to broadcast to if solo team + if (team.getMembers().size() <= 1) return; + + // Broadcast to other online teammates. + ProspectingNotification broadcast = new ProspectingNotification(newVeins, newFluids); + TeamManager.forEachOnlineTeamMember(team, member -> { + if (!member.getUniqueID().equals(player.getUniqueID())) { + VP.network.sendTo(broadcast, member); + } + }); + } + + /** + * Manage vein depletion toggle sent by {@code player} from a map GUI. + *

+ * Toggles are only accepted for veins the team has actually discovered. + */ + public static void handleDepletionToggle(EntityPlayerMP player, int dim, int chunkX, int chunkZ, boolean depleted) { + if (!Config.enableTeamSharing) return; + + Team team = TeamManager.getTeamByPlayer(player.getUniqueID()); + if (team == null) return; + TeamProspectionData data = (TeamProspectionData) team.getData(TeamProspectionData.DATA_KEY); + if (data == null) return; + + // Require the vein to be in the team's discovered set. + if (!data.isVeinDiscovered(dim, chunkX, chunkZ)) return; + + // Stop here if the team's depletion state already matches. + if (!data.setVeinDepleted(dim, chunkX, chunkZ, depleted)) return; + + team.markDirty(); + + // Broadcast to other online teammates. + VeinDepletionMessage broadcast = new VeinDepletionMessage(dim, chunkX, chunkZ, depleted); + TeamManager.forEachOnlineTeamMember(team, member -> { + if (!member.getUniqueID().equals(player.getUniqueID())) { + VP.network.sendTo(broadcast, member); + } + }); + } + + private static List filterNewVeins(TeamProspectionData data, List veins) { + if (veins == null || veins.isEmpty()) return Collections.emptyList(); + List newVeins = new ArrayList<>(veins.size()); + for (OreVeinPosition v : veins) { + if (v == null || v.veinType == VeinType.NO_VEIN) continue; + if (data.addVein(v.dimensionId, v.chunkX, v.chunkZ)) { + newVeins.add(v); + } + } + return newVeins; + } + + private static List filterNewFluids(TeamProspectionData data, + List fluids) { + if (fluids == null || fluids.isEmpty()) return Collections.emptyList(); + List newFluids = new ArrayList<>(fluids.size()); + for (UndergroundFluidPosition f : fluids) { + if (f == null || !f.isProspected()) continue; + if (data.addFluid(f.dimensionId, f.chunkX, f.chunkZ)) { + newFluids.add(f); + } + } + return newFluids; + } +} diff --git a/src/main/java/com/sinthoras/visualprospecting/utils/VPByteBufUtils.java b/src/main/java/com/sinthoras/visualprospecting/utils/VPByteBufUtils.java new file mode 100644 index 00000000..96ad3ba1 --- /dev/null +++ b/src/main/java/com/sinthoras/visualprospecting/utils/VPByteBufUtils.java @@ -0,0 +1,103 @@ +package com.sinthoras.visualprospecting.utils; + +import java.util.ArrayList; +import java.util.List; + +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidRegistry; + +import com.sinthoras.visualprospecting.VP; +import com.sinthoras.visualprospecting.database.OreVeinPosition; +import com.sinthoras.visualprospecting.database.UndergroundFluidPosition; +import com.sinthoras.visualprospecting.database.VeinSource; +import com.sinthoras.visualprospecting.database.veintypes.VeinTypeCaching; + +import cpw.mods.fml.common.network.ByteBufUtils; +import io.netty.buffer.ByteBuf; + +public final class VPByteBufUtils { + + public static void WriteOreVeinPosition(ByteBuf buf, OreVeinPosition oreVeinPosition) { + buf.writeInt(oreVeinPosition.dimensionId); + buf.writeInt(oreVeinPosition.chunkX); + buf.writeInt(oreVeinPosition.chunkZ); + buf.writeBoolean(oreVeinPosition.isDepleted()); + buf.writeByte(oreVeinPosition.getSourceByte()); + ByteBufUtils.writeUTF8String(buf, oreVeinPosition.veinType.name); + } + + public static OreVeinPosition ReadOreVeinPosition(ByteBuf buf) { + final int dimId = buf.readInt(); + final int chunkX = buf.readInt(); + final int chunkZ = buf.readInt(); + final boolean isDepleted = buf.readBoolean(); + final VeinSource source = VeinSource.fromByte(buf.readByte()); + final String veinName = ByteBufUtils.readUTF8String(buf); + + return new OreVeinPosition(dimId, chunkX, chunkZ, VeinTypeCaching.getVeinType(veinName), isDepleted, source); + } + + public static void WriteUndergroundFluidPosition(ByteBuf buf, UndergroundFluidPosition undergroundFluidPosition) { + buf.writeInt(undergroundFluidPosition.dimensionId); + buf.writeInt(undergroundFluidPosition.chunkX); + buf.writeInt(undergroundFluidPosition.chunkZ); + ByteBufUtils.writeUTF8String(buf, undergroundFluidPosition.fluid.getName()); + for (int offsetChunkX = 0; offsetChunkX < VP.undergroundFluidSizeChunkX; offsetChunkX++) { + for (int offsetChunkZ = 0; offsetChunkZ < VP.undergroundFluidSizeChunkZ; offsetChunkZ++) { + buf.writeInt(undergroundFluidPosition.chunks[offsetChunkX][offsetChunkZ]); + } + } + } + + public static UndergroundFluidPosition ReadUndergroundFluidPosition(ByteBuf buf) { + final int dimId = buf.readInt(); + final int chunkX = buf.readInt(); + final int chunkZ = buf.readInt(); + final Fluid fluid = FluidRegistry.getFluid(ByteBufUtils.readUTF8String(buf)); + final int[][] chunks = new int[VP.undergroundFluidSizeChunkX][VP.undergroundFluidSizeChunkZ]; + for (int offsetChunkX = 0; offsetChunkX < VP.undergroundFluidSizeChunkX; offsetChunkX++) { + for (int offsetChunkZ = 0; offsetChunkZ < VP.undergroundFluidSizeChunkZ; offsetChunkZ++) { + chunks[offsetChunkX][offsetChunkZ] = buf.readInt(); + } + } + + return new UndergroundFluidPosition(dimId, chunkX, chunkZ, fluid, chunks); + } + + public static void WriteOreVeinPositions(ByteBuf buf, List oreVeinPositions) { + buf.writeInt(oreVeinPositions.size()); + for (OreVeinPosition oreVeinPosition : oreVeinPositions) { + WriteOreVeinPosition(buf, oreVeinPosition); + } + } + + public static List ReadOreVeinPositions(ByteBuf buf) { + List oreVeinPositions = new ArrayList<>(); + + final int oreVeinCount = buf.readInt(); + for (int i = 0; i < oreVeinCount; i++) { + oreVeinPositions.add(ReadOreVeinPosition(buf)); + } + + return oreVeinPositions; + } + + public static void WriteUndergroundFluidPositions(ByteBuf buf, + List undergroundFluidPositions) { + buf.writeInt(undergroundFluidPositions.size()); + for (UndergroundFluidPosition undergroundFluidPosition : undergroundFluidPositions) { + WriteUndergroundFluidPosition(buf, undergroundFluidPosition); + } + } + + public static List ReadUndergroundFluidPositions(ByteBuf buf) { + List undergroundFluidPositions = new ArrayList<>(); + + final int undergroundFluidCount = buf.readInt(); + for (int i = 0; i < undergroundFluidCount; i++) { + undergroundFluidPositions.add(ReadUndergroundFluidPosition(buf)); + } + + return undergroundFluidPositions; + } +} diff --git a/src/main/java/com/sinthoras/visualprospecting/utils/VPSaveCleanupUtils.java b/src/main/java/com/sinthoras/visualprospecting/utils/VPSaveCleanupUtils.java new file mode 100644 index 00000000..be4e78fe --- /dev/null +++ b/src/main/java/com/sinthoras/visualprospecting/utils/VPSaveCleanupUtils.java @@ -0,0 +1,41 @@ +package com.sinthoras.visualprospecting.utils; + +import java.io.File; +import java.io.FileInputStream; + +import net.minecraft.nbt.CompressedStreamTools; +import net.minecraft.nbt.NBTTagCompound; + +import com.sinthoras.visualprospecting.database.WorldIdHandler; + +public final class VPSaveCleanupUtils { + + /** + * The name of save folder is created in {@link WorldIdHandler} by combining world name and a randomly generated + * UUID. The value is then saved into visualprospecting.dat file so we have to go read it there. + * + * @param mcDataDir Full path to Minecraft instance + * @param worldFolder Directory name for world we want the VP worldId of + * @return worldId VP value for this world + */ + public static String getVisualProspectingWorldId(File mcDataDir, String worldFolder) { + File vpDat = mcDataDir.toPath().resolve("saves").resolve(worldFolder).resolve("data") + .resolve("visualprospecting.dat").toFile(); + if (!vpDat.exists()) { + return null; + } + + try (FileInputStream vpFIS = new FileInputStream(vpDat)) { + NBTTagCompound vpNBT = CompressedStreamTools.readCompressed(vpFIS); + if (vpNBT.hasKey("data")) { + NBTTagCompound data = vpNBT.getCompoundTag("data"); + if (data.hasKey("wId")) { + return data.getString("wId"); + } + } + return null; + } catch (Exception e) { + return null; + } + } +} diff --git a/src/main/resources/META-INF/visualprospecting_at.cfg b/src/main/resources/META-INF/visualprospecting_at.cfg index 942b84ee..4c143b92 100644 --- a/src/main/resources/META-INF/visualprospecting_at.cfg +++ b/src/main/resources/META-INF/visualprospecting_at.cfg @@ -1 +1,2 @@ -public net.minecraft.server.MinecraftServer field_71298_S # userMessage \ No newline at end of file +public net.minecraft.nbt.NBTTagCompound field_74784_a #tagMap +public net.minecraft.nbt.NBTTagList field_74747_a #taglist \ No newline at end of file diff --git a/src/main/resources/assets/gregtech/lang/en_US.lang b/src/main/resources/assets/gregtech/lang/en_US.lang deleted file mode 100644 index 5bd457b6..00000000 --- a/src/main/resources/assets/gregtech/lang/en_US.lang +++ /dev/null @@ -1,93 +0,0 @@ -ore.mix.naquadah=Naquadah -ore.mix.lignite=Lignite -ore.mix.coal=Coal -ore.mix.magnetite=Magnetite -ore.mix.gold=Gold -ore.mix.iron=Iron -ore.mix.cassiterite=Cassiterite -ore.mix.tetrahedrite=Tetrahedrite -ore.mix.netherquartz=Nether Quartz -ore.mix.sulfur=Sulfur -ore.mix.copper=Copper -ore.mix.bauxite=Bauxite -ore.mix.salts=Salts -ore.mix.redstone=Redstone -ore.mix.soapstone=Soapstone -ore.mix.nickel=Nickel -ore.mix.platinum=Platinum -ore.mix.pitchblende=Pitchblende -ore.mix.monazite=Monazite -ore.mix.molybdenum=Molybdenum -ore.mix.tungstate=Tungstate -ore.mix.sapphire=Sapphire -ore.mix.manganese=Manganese -ore.mix.quartz=Quartz -ore.mix.diamond=Diamond -ore.mix.olivine=Olivine -ore.mix.apatite=Apatite -ore.mix.galena=Galena -ore.mix.lapis=Lapis -ore.mix.beryllium=Beryllium -ore.mix.uranium=Uranium -ore.mix.oilsand=Oilsands - -ore.mix.neutronium=Neutronium -ore.mix.aquaignis=Aqua and Ignis -ore.mix.terraaer=Terra and Aer -ore.mix.perditioordo=Perdito and Ordo -ore.mix.coppertin=Vermiculite -ore.mix.titaniumchrome=Ilmenite -ore.mix.mineralsand=Mineralsand -ore.mix.garnettin=Garnettin -ore.mix.kaolinitezeolite=Kaolinite -ore.mix.mica=Mica -ore.mix.dolomite=Dolomite -ore.mix.platinumchrome=Palladium -ore.mix.iridiummytryl=Iridium -ore.mix.osmium=Osmium -ore.mix.saltpeterelectrotine=Electrotine -ore.mix.desh=Desh -ore.mix.draconium=Draconium -ore.mix.quantium=Quantum -ore.mix.callistoice=Callisto Ice -ore.mix.mytryl=Mithril -ore.mix.ledox=Ledox -ore.mix.oriharukon=Oriharukon -ore.mix.blackplutonium=Black Plutonium -ore.mix.infusedgold=Infused Gold -ore.mix.niobium=Niobium -ore.mix.tungstenirons=Tungsten -ore.mix.uraniumgtnh=Thorium -ore.mix.vanadiumgold=Vanadium -ore.mix.netherstar=NetherStar -ore.mix.garnet=Garnet -ore.mix.rareearth=Rare Earths -ore.mix.richnuclear=Plutonium -ore.mix.heavypentele=Arsenic -ore.mix.europa=Magnesite -ore.mix.europacore=Chrome -ore.mix.secondlanthanid=Samarium -ore.mix.quartzspace=Quartz -ore.mix.rutile=Rutile -ore.mix.tfgalena=Cryolite -ore.mix.luvtantalite=Pyrolusite - -ore.mix.ross128.Thorianit=Thorianit -ore.mix.ross128.carbon=Graphite -ore.mix.ross128.bismuth=Bismuth -ore.mix.ross128.TurmalinAlkali=Olenit -ore.mix.ross128.Roquesit=Roquesit -ore.mix.ross128.Tungstate=Scheelite -ore.mix.ross128.CopperSulfits=Djurleit -ore.mix.ross128.Forsterit=Forsterit -ore.mix.ross128.Hedenbergit=Hedenbergit -ore.mix.ross128.RedZircon=Red Zircon - -ore.mix.ross128ba.tib=Tiberium -ore.mix.ross128ba.Tungstate=Scheelite -ore.mix.ross128ba.bart=BArTiMaEuSNeK -ore.mix.ross128ba.TurmalinAlkali=Olenit -ore.mix.ross128ba.Amethyst=Amethyst -ore.mix.ross128ba.CopperSulfits=Djurleit -ore.mix.ross128ba.RedZircon=Red Zircon -ore.mix.ross128ba.Fluorspar=Fluorspar diff --git a/src/main/resources/assets/gregtech/lang/zh_CN.lang b/src/main/resources/assets/gregtech/lang/zh_CN.lang deleted file mode 100644 index fc63c48e..00000000 --- a/src/main/resources/assets/gregtech/lang/zh_CN.lang +++ /dev/null @@ -1,93 +0,0 @@ -ore.mix.naquadah=硅岩 -ore.mix.lignite=褐煤 -ore.mix.coal=煤 -ore.mix.magnetite=磁铁 -ore.mix.gold=金 -ore.mix.iron=铁 -ore.mix.cassiterite=锡石 -ore.mix.tetrahedrite=黝铜 -ore.mix.netherquartz=下界石英 -ore.mix.sulfur=硫 -ore.mix.copper=铜 -ore.mix.bauxite=铝土 -ore.mix.salts=盐 -ore.mix.redstone=红石 -ore.mix.soapstone=皂石 -ore.mix.nickel=镍 -ore.mix.platinum=铂 -ore.mix.pitchblende=沥青铀 -ore.mix.monazite=独居石 -ore.mix.molybdenum=钼 -ore.mix.tungstate=钨酸锂 -ore.mix.sapphire=蓝宝石 -ore.mix.manganese=锰 -ore.mix.quartz=石英 -ore.mix.diamond=钻石 -ore.mix.olivine=橄榄石 -ore.mix.apatite=磷灰石 -ore.mix.galena=方铅 -ore.mix.lapis=青金石 -ore.mix.beryllium=铍 -ore.mix.uranium=铀 -ore.mix.oilsand=油砂 - -ore.mix.neutronium=中子锭 -ore.mix.aquaignis=水/火蕴魔石 -ore.mix.terraaer=地/风蕴魔石 -ore.mix.perditioordo=混沌/秩序蕴魔石 -ore.mix.coppertin=蛭石 -ore.mix.titaniumchrome=钛铁 -ore.mix.mineralsand=玄武岩矿砂 -ore.mix.garnettin=石榴石/锡石 -ore.mix.kaolinitezeolite=高岭石 -ore.mix.mica=云母 -ore.mix.dolomite=白云石 -ore.mix.platinumchrome=钯 -ore.mix.iridiummytryl=铱 -ore.mix.osmium=锇 -ore.mix.saltpeterelectrotine=蓝石 -ore.mix.desh=戴斯 -ore.mix.draconium=龙 -ore.mix.quantium=量子合金 -ore.mix.callistoice=木卫四冰 -ore.mix.mytryl=秘银 -ore.mix.ledox=深铅 -ore.mix.oriharukon=奥利哈钢 -ore.mix.blackplutonium=黑钚 -ore.mix.infusedgold=注魔金 -ore.mix.niobium=铌 -ore.mix.tungstenirons=钨 -ore.mix.uraniumgtnh=钍 -ore.mix.vanadiumgold=钒 -ore.mix.netherstar=下界之星 -ore.mix.garnet=石榴石 -ore.mix.rareearth=稀土 -ore.mix.richnuclear=钚 -ore.mix.heavypentele=砷 -ore.mix.europa=菱镁 -ore.mix.europacore=铬 -ore.mix.secondlanthanid=钐 -ore.mix.quartzspace=石英 -ore.mix.rutile=金红石 -ore.mix.tfgalena=冰晶石 -ore.mix.luvtantalite=软锰 - -ore.mix.ross128.Thorianit=方钍石 -ore.mix.ross128.carbon=石墨 -ore.mix.ross128.bismuth=铋 -ore.mix.ross128.TurmalinAlkali=铝电气石 -ore.mix.ross128.Roquesit=硫铟铜 -ore.mix.ross128.Tungstate=白钨矿 -ore.mix.ross128.CopperSulfits=久辉铜 -ore.mix.ross128.Forsterit=镁橄榄石 -ore.mix.ross128.Hedenbergit=钙铁辉石 -ore.mix.ross128.RedZircon=红锆石 - -ore.mix.ross128ba.tib=泰伯利亚 -ore.mix.ross128ba.Tungstate=白钨矿 -ore.mix.ross128ba.bart=BArTiMaEuSNeK -ore.mix.ross128ba.TurmalinAlkali=铝电气石 -ore.mix.ross128ba.Amethyst=紫水晶 -ore.mix.ross128ba.CopperSulfits=久辉铜 -ore.mix.ross128ba.RedZircon=红锆石 -ore.mix.ross128ba.Fluorspar=氟石 diff --git a/src/main/resources/assets/journeymap/icon/theme/Vault/icon/nodes.png b/src/main/resources/assets/journeymap/icon/theme/Vault/icon/nodes.png deleted file mode 100644 index dd9193dc..00000000 Binary files a/src/main/resources/assets/journeymap/icon/theme/Vault/icon/nodes.png and /dev/null differ diff --git a/src/main/resources/assets/journeymap/icon/theme/Vault/icon/oreveins.png b/src/main/resources/assets/journeymap/icon/theme/Vault/icon/oreveins.png deleted file mode 100644 index 08cf2ea2..00000000 Binary files a/src/main/resources/assets/journeymap/icon/theme/Vault/icon/oreveins.png and /dev/null differ diff --git a/src/main/resources/assets/journeymap/icon/theme/Vault/icon/undergroundfluid.png b/src/main/resources/assets/journeymap/icon/theme/Vault/icon/undergroundfluid.png deleted file mode 100644 index ee4dc50b..00000000 Binary files a/src/main/resources/assets/journeymap/icon/theme/Vault/icon/undergroundfluid.png and /dev/null differ diff --git a/src/main/resources/assets/journeymap/icon/theme/Victorian/icon/nodes.png b/src/main/resources/assets/journeymap/icon/theme/Victorian/icon/nodes.png deleted file mode 100644 index dd9193dc..00000000 Binary files a/src/main/resources/assets/journeymap/icon/theme/Victorian/icon/nodes.png and /dev/null differ diff --git a/src/main/resources/assets/journeymap/icon/theme/Victorian/icon/undergroundfluid.png b/src/main/resources/assets/journeymap/icon/theme/Victorian/icon/undergroundfluid.png deleted file mode 100644 index ee4dc50b..00000000 Binary files a/src/main/resources/assets/journeymap/icon/theme/Victorian/icon/undergroundfluid.png and /dev/null differ diff --git a/src/main/resources/assets/visualprospecting/lang/de_DE.lang b/src/main/resources/assets/visualprospecting/lang/de_DE.lang new file mode 100644 index 00000000..f2da359b --- /dev/null +++ b/src/main/resources/assets/visualprospecting/lang/de_DE.lang @@ -0,0 +1,40 @@ +visualprospecting.vein.prospected=Du hast eine %s-Ader gefunden %s! +visualprospecting.vein.contents=Du könntest %s finden +visualprospecting.veins.prospected=Du hast %s neue Erzadern gefunden! +visualprospecting.undergroundfluid.prospected.onlynew=Du hast %s neue Ölfelder gefunden! +visualprospecting.undergroundfluid.prospected.onlyupdated=Du hast %s Ölfelder vermessen! +visualprospecting.undergroundfluid.prospected.newandupdated=Du hast %s Ölfinder neu gefunden und %s vermessen! +visualprospecting.button.orevein=GT-Erzadern anzeigen +visualprospecting.button.undergroundfluid=GT-Gas/Flüssigkeitsadern anzeigen +visualprospecting.command.vpadmin.servercache.rebuild.start=Cache-wiederherstellen auf dem Server beginnen +visualprospecting.command.vpadmin.servercache.rebuild.failure=Konnte Weltdaten nicht laden, um Cache wiederherstellen! +visualprospecting.command.vpadmin.servercache.rebuild.confirmation=Wiederherstellen erfolgreich! +visualprospecting.command.vpadmin.servercache.rebuildspawn.start=Wiederherstellen des GT-Erzader-Cache um den Spawnpunkt beginnen +visualprospecting.command.vpadmin.servercache.rebuildspawn.failure=Konnte Weltdaten nicht laden, um Spawncache wiederherstellen! +visualprospecting.command.vpadmin.servercache.rebuildspawn.confirmation=Wiederherstellen erfolgreich! +visualprospecting.iswaypoint=Als Wegpunkt aktiv +visualprospecting.depleted=ERSCHÖPFT +visualprospecting.empty=Leer +visualprospecting.key.toggleore.name=Erzoverlay (de)aktivieren +visualprospecting.key.togglefluid.name=Flüssigkeitsoverlay (de)aktivieren +visualprospecting.key.action.category=VisualProspecting +item.visualprospecting.prospectorslog.name=Goldsucher-Tagebuch +item.visualprospecting.prospectorslog.author=Autor: %s +item.visualprospecting.prospectorslog.empty=Item benutzen, um es aufzufüllen. +item.visualprospecting.prospectorslog.creation.begin=Ein Goldsucher-Tagebuch wurde geschrieben. +item.visualprospecting.prospectorslog.creation.fail0=[Failed]: Verdammt! Kaffee drübergekippt... +item.visualprospecting.prospectorslog.creation.fail1=[Failed]: Es scheint so, als kannst du deine eigenen Wörter nicht mehr lesen... +item.visualprospecting.prospectorslog.creation.fail2=[Failed]: Hätte ich mal nicht wie ein Arzt geschrieben! Unlesbar... +item.visualprospecting.prospectorslog.creation.fail3=[Failed]: Wo ist denn meine Brille? Ohne kann ich nicht schreiben! +item.visualprospecting.prospectorslog.creation.fail4=[Failed]: Herobrine hat meine Notizen geklaut! +item.visualprospecting.prospectorslog.reading.fail0=[Failed]: Riecht wie gute, Kaffee, aber ich kann es nicht lesen! +item.visualprospecting.prospectorslog.reading.fail1=[Failed]: Kommt mir Spanisch vor! +item.visualprospecting.prospectorslog.reading.fail2=[Failed]: Was?!? Das war leer!? +item.visualprospecting.prospectorslog.reading.fail3=[Failed]: Äääh... Keine Chance. Kann ich nicht entziffern! +item.visualprospecting.prospectorslog.reading.fail4=[Failed]: Habe ich einen Dr. in Geografie? NEIN! +item.visualprospecting.prospectorslog.reading.begin=Das ist mal ein Wälzer. Das zu lesen könnte eine Weile dauern. +item.visualprospecting.prospectorslog.reading.end=Goldsucher-Tagebuch von %s durchgelesen +visualprospecting.node.deletehint=Mit %s als erschöpft markieren +visualprospecting.tracked=Nachverfolgt: %s +visualprospecting.command.client.resetcache.usage=Löscht deinen gesamten Fortschritt beim Entdecken von Erz- und Flüssigkeitsadern +visualprospecting.command.client.resetcache.confirmation=Fortschritt gelöscht! \ No newline at end of file diff --git a/src/main/resources/assets/visualprospecting/lang/en_US.lang b/src/main/resources/assets/visualprospecting/lang/en_US.lang index 728401e2..59fba23f 100644 --- a/src/main/resources/assets/visualprospecting/lang/en_US.lang +++ b/src/main/resources/assets/visualprospecting/lang/en_US.lang @@ -6,35 +6,77 @@ visualprospecting.undergroundfluid.prospected.onlyupdated=You updated %s oil fie visualprospecting.undergroundfluid.prospected.newandupdated=You found %s new and updated %s oil fields! visualprospecting.button.orevein=Show GT Ore Veins visualprospecting.button.undergroundfluid=Show GT Underground Fluids -visualprospecting.button.nodes=Show TC Nodes -visualprospecting.button.resetprogress=Reset VisualProspecting -visualprospecting.button.resetprogress.tooltip=Reset found ore veins and underground fluids for this player only -visualprospecting.resetprogress.title=Delete VisualProspecting Progress -visualprospecting.resetprogress.confirm=Yes, i am sure! -visualprospecting.resetprogress.cancel=Cancel -visualprospecting.resetprogress.prompt=Delete player progress on ore veins and underground fluids? -visualprospecting.resetprogress.command=Deletes all your progress on ore veins and undergrond fluids -visualprospecting.resetprogress.confirmation=Progress deleted! visualprospecting.iswaypoint=Active as Waypoint visualprospecting.depleted=DEPLETED -visualprospecting.depleted.toggle=Toggle depleted with %s -visualprospecting.key.action.name=Action button +visualprospecting.empty=Empty +visualprospecting.key.toggleore.name=Toggle ore overlay +visualprospecting.key.togglefluid.name=Toggle fluid overlay visualprospecting.key.action.category=VisualProspecting item.visualprospecting.prospectorslog.name=Prospector's Log item.visualprospecting.prospectorslog.author=Author: %s item.visualprospecting.prospectorslog.empty=Use item to fill it. item.visualprospecting.prospectorslog.creation.begin=A Prospector's Log has been written. item.visualprospecting.prospectorslog.creation.fail0=[Failed]: Damn! I spilled coffee over it... -item.visualprospecting.prospectorslog.creation.fail1=[Failed]: It seem's that you cannot read your own words... +item.visualprospecting.prospectorslog.creation.fail1=[Failed]: It seems that you cannot read your own words... item.visualprospecting.prospectorslog.creation.fail2=[Failed]: Why did I write that in eldritch? I cannot read eldritch! -item.visualprospecting.prospectorslog.creation.fail3=[Failed]: Where did I put my glasses? I can't write without! +item.visualprospecting.prospectorslog.creation.fail3=[Failed]: Where did I put my glasses? I can't write without them! item.visualprospecting.prospectorslog.creation.fail4=[Failed]: Herobrine stole my notes! item.visualprospecting.prospectorslog.reading.fail0=[Failed]: It smells like good coffee, but i cannot read it! item.visualprospecting.prospectorslog.reading.fail1=[Failed]: I need to improve my Russian before I can read that! item.visualprospecting.prospectorslog.reading.fail2=[Failed]: What?!? That was empty? -item.visualprospecting.prospectorslog.reading.fail3=[Failed]: Yeah..No. I can't decipher that! +item.visualprospecting.prospectorslog.reading.fail3=[Failed]: Yeah... No. I can't decipher that! item.visualprospecting.prospectorslog.reading.fail4=[Failed]: Do I have a Ph.D. in Geography? NOPE! -item.visualprospecting.prospectorslog.reading.begin=Those are quite some pages. It might take some time to fully read it. +item.visualprospecting.prospectorslog.reading.begin=That's quite a lot of pages. It might take some time to fully read it. item.visualprospecting.prospectorslog.reading.end=I finished the Prospector's Log of %s -visualprospecting.node.deletehint=Remove with %s +visualprospecting.node.deletehint=Mark depleted with %s visualprospecting.tracked=Tracked: %s +visualprospecting.command.console_needs_player=Must specify a player when running command from console. +visualprospecting.command.console_forbidden=This command cannot be run from the console. +visualprospecting.command.player_offline=Player not found or not online: %s +visualprospecting.command.sharing_disabled=Team sharing is disabled in the server config. +visualprospecting.command.client.resetcache.usage=/vp_client_cache_reset: Deletes local cache of all your ore veins and underground fluids +visualprospecting.command.client.resetcache.confirmation=Local cache deleted. It may still reconstruct partially from team cache on next logon if they are enabled. +visualprospecting.command.vp.root.usage=/vp +visualprospecting.command.vp.team.usage=/vp team info [detailed]: Inspect your own team's prospection record. +visualprospecting.command.vp.team.no_team=%s is not in any team. +visualprospecting.command.vp.team.no_data=Team has no prospection data attached. +visualprospecting.command.vp.team.info.header=Team "%s" (%s members, %s online) +visualprospecting.command.vp.team.info.empty= No discoveries recorded. +visualprospecting.command.vp.team.info.summary= Across %s dim(s): %s veins (%s depleted), %s fluids +visualprospecting.command.vp.team.info.dim_line= Dim %s: %s veins (%s depleted), %s fluids +visualprospecting.command.vpadmin.root.usage=/vp_admin : VisualProspecting admin commands. +visualprospecting.command.vpadmin.team.usage=/vp_admin team : Admin operations on a player's team prospection record. +visualprospecting.command.vpadmin.team.info.usage=/vp_admin team info [detailed]: Inspect a player's team prospection record. +visualprospecting.command.vpadmin.team.clear.usage=/vp_admin team clear : Delete the player's team prospection record. +visualprospecting.command.vpadmin.team.clear.success=Cleared team "%s": Removed %s veins and %s fluids. +visualprospecting.command.vpadmin.team.upload.usage=/vp_admin team upload : Request the player's client to push its local cache into the team's record. +visualprospecting.command.vpadmin.team.upload.requested=Requested upload of %s's local cache into team "%s". +visualprospecting.command.vpadmin.servercache.usage=/vp_admin servercache : Server vein cache operations. +visualprospecting.command.vpadmin.servercache.rebuild.usage=/vp_admin servercache rebuild : Rebuild the GT vein cache from world save files. +visualprospecting.command.vpadmin.servercache.no_server=Server instance not found. +visualprospecting.command.vpadmin.servercache.rebuild.start=Started rebuilding the GT vein cache on the server +visualprospecting.command.vpadmin.servercache.rebuild.failure=Could not load world save files to build vein cache! +visualprospecting.command.vpadmin.servercache.rebuild.confirmation=Redo success! +visualprospecting.command.vpadmin.servercache.rebuildspawn.start=Started rebuilding the GT vein cache near spawn on the server +visualprospecting.command.vpadmin.servercache.rebuildspawn.failure=Could not load world save files to build vein cache near spawn! +visualprospecting.command.vpadmin.servercache.rebuildspawn.confirmation=Redo success! +visualprospecting.command.vpadmin.debug.usage=/vp_admin debug : Inspect the server's stored prospection data. +visualprospecting.command.vpadmin.debug.vein.header=Ore chunk [%s, %s] in DIM %s (%s) +visualprospecting.command.vpadmin.debug.vein.type= [Cache] Vein: %s (%s) +visualprospecting.command.vpadmin.debug.vein.source= Source: %s +visualprospecting.command.vpadmin.debug.vein.cache_none= [Cache] No vein recorded. +visualprospecting.command.vpadmin.debug.analyze.title= [Analysis] Live ore analysis +visualprospecting.command.vpadmin.debug.analyze.error= [Analysis] Failed to read live chunk: %s +visualprospecting.command.vpadmin.debug.analyze.no_ores= [Analysis] No natural ores found in this chunk. +visualprospecting.command.vpadmin.debug.analyze.stats= Ores: %s blocks, %s materials, Y %s-%s +visualprospecting.command.vpadmin.debug.analyze.ore_line= %s x%s +visualprospecting.command.vpadmin.debug.analyze.dominant= Dominant: %s (x%s) +visualprospecting.command.vpadmin.debug.analyze.no_dim_veins= Note: dimension "%s" has no registered ore veins. +visualprospecting.command.vpadmin.debug.analyze.candidates_none= No candidate veins for the dominant ore. +visualprospecting.command.vpadmin.debug.analyze.candidates_header= Candidates for %s (%s): +visualprospecting.command.vpadmin.debug.analyze.candidate_line= %s [full=%s, oneMissing=%s] +visualprospecting.command.vpadmin.debug.analyze.match_summary= Summary: %s full, %s one-missing +visualprospecting.command.vpadmin.debug.analyze.resolved= Resolved (detailed): %s (%s) +visualprospecting.command.vpadmin.debug.analyze.resolved_none= Resolved (detailed): none +visualprospecting.command.vpadmin.debug.analyze.fastpath= Fast-path (ChunkAnalysis): %s +visualprospecting.command.vpadmin.debug.analyze.fastpath_ambiguous= Fast-path (ChunkAnalysis): ambiguous, needs detailed analysis diff --git a/src/main/resources/assets/visualprospecting/lang/zh_CN.lang b/src/main/resources/assets/visualprospecting/lang/zh_CN.lang index 69a3868b..8d93f7eb 100644 --- a/src/main/resources/assets/visualprospecting/lang/zh_CN.lang +++ b/src/main/resources/assets/visualprospecting/lang/zh_CN.lang @@ -6,19 +6,11 @@ visualprospecting.undergroundfluid.prospected.onlyupdated=你更新了 %s 流体 visualprospecting.undergroundfluid.prospected.newandupdated=你找到了 %s 新流体井并更新了 %s 流体井! visualprospecting.button.orevein=显示GT矿脉 visualprospecting.button.undergroundfluid=显示GT地下流体 -visualprospecting.button.nodes=显示灵气节点 -visualprospecting.button.resetprogress=重置VisualProspecting -visualprospecting.button.resetprogress.tooltip=仅重置此玩家发现的矿脉与地下流体 -visualprospecting.resetprogress.title=删除VisualProspecting记录 -visualprospecting.resetprogress.confirm=是的,我确定! -visualprospecting.resetprogress.cancel=取消 -visualprospecting.resetprogress.prompt=删除玩家矿脉与地下流体记录? -visualprospecting.resetprogress.command=删除你的所有矿脉与地下流体记录 -visualprospecting.resetprogress.confirmation=记录已删除! +visualprospecting.command.vpadmin.servercache.rebuild.start=开始重新缓存GT矿脉 +visualprospecting.command.vpadmin.servercache.rebuild.failure=无法加载世界存档以构建矿脉缓存 +visualprospecting.command.vpadmin.servercache.rebuild.confirmation=重新缓存成功! visualprospecting.iswaypoint=作为导航点激活 visualprospecting.depleted=枯竭 -visualprospecting.depleted.toggle=切换为枯竭阈值: %s -visualprospecting.key.action.name=激活按钮 visualprospecting.key.action.category=VisualProspecting item.visualprospecting.prospectorslog.name=探矿记录 item.visualprospecting.prospectorslog.author=作者: %s @@ -38,3 +30,5 @@ item.visualprospecting.prospectorslog.reading.begin=有相当多页.可能得多 item.visualprospecting.prospectorslog.reading.end=我读完了 %s 的探矿记录 visualprospecting.node.deletehint=删除 %s visualprospecting.tracked=追踪: %s +visualprospecting.command.client.resetcache.usage=删除你的所有矿脉与地下流体记录 +visualprospecting.command.client.resetcache.confirmation=记录已删除! \ No newline at end of file diff --git a/src/main/resources/assets/visualprospecting/textures/depleted.png b/src/main/resources/assets/visualprospecting/textures/depleted.png index 6c7dbc6d..05b31f6e 100644 Binary files a/src/main/resources/assets/visualprospecting/textures/depleted.png and b/src/main/resources/assets/visualprospecting/textures/depleted.png differ diff --git a/src/main/resources/assets/journeymap/icon/theme/Victorian/icon/oreveins.png b/src/main/resources/assets/visualprospecting/textures/icons/oreveins.png similarity index 91% rename from src/main/resources/assets/journeymap/icon/theme/Victorian/icon/oreveins.png rename to src/main/resources/assets/visualprospecting/textures/icons/oreveins.png index 08cf2ea2..8775a1b3 100644 Binary files a/src/main/resources/assets/journeymap/icon/theme/Victorian/icon/oreveins.png and b/src/main/resources/assets/visualprospecting/textures/icons/oreveins.png differ diff --git a/src/main/resources/assets/visualprospecting/textures/icons/undergroundfluid.png b/src/main/resources/assets/visualprospecting/textures/icons/undergroundfluid.png new file mode 100644 index 00000000..b6756852 Binary files /dev/null and b/src/main/resources/assets/visualprospecting/textures/icons/undergroundfluid.png differ diff --git a/src/main/resources/assets/visualprospecting/textures/items/prospectorslog.png b/src/main/resources/assets/visualprospecting/textures/items/prospectorslog.png index 117aacce..a670ec05 100644 Binary files a/src/main/resources/assets/visualprospecting/textures/items/prospectorslog.png and b/src/main/resources/assets/visualprospecting/textures/items/prospectorslog.png differ diff --git a/src/main/resources/assets/visualprospecting/textures/node_marked.png b/src/main/resources/assets/visualprospecting/textures/node_marked.png index 68c4cef3..0eed536b 100644 Binary files a/src/main/resources/assets/visualprospecting/textures/node_marked.png and b/src/main/resources/assets/visualprospecting/textures/node_marked.png differ diff --git a/src/main/resources/assets/visualprospecting/textures/node_unmarked.png b/src/main/resources/assets/visualprospecting/textures/node_unmarked.png index 20e583b2..59a275e0 100644 Binary files a/src/main/resources/assets/visualprospecting/textures/node_unmarked.png and b/src/main/resources/assets/visualprospecting/textures/node_unmarked.png differ diff --git a/src/main/resources/assets/xaeroworldmap/textures/nodes.png b/src/main/resources/assets/xaeroworldmap/textures/nodes.png deleted file mode 100644 index 7a1b5154..00000000 Binary files a/src/main/resources/assets/xaeroworldmap/textures/nodes.png and /dev/null differ diff --git a/src/main/resources/assets/xaeroworldmap/textures/oreveins.png b/src/main/resources/assets/xaeroworldmap/textures/oreveins.png deleted file mode 100644 index 1d10b72b..00000000 Binary files a/src/main/resources/assets/xaeroworldmap/textures/oreveins.png and /dev/null differ diff --git a/src/main/resources/assets/xaeroworldmap/textures/undergroundfluid.png b/src/main/resources/assets/xaeroworldmap/textures/undergroundfluid.png deleted file mode 100644 index 921c2595..00000000 Binary files a/src/main/resources/assets/xaeroworldmap/textures/undergroundfluid.png and /dev/null differ diff --git a/src/main/resources/mixins.visualprospecting.early.json b/src/main/resources/mixins.visualprospecting.early.json new file mode 100644 index 00000000..e5e57325 --- /dev/null +++ b/src/main/resources/mixins.visualprospecting.early.json @@ -0,0 +1,8 @@ +{ + "required": true, + "minVersion": "0.7.11", + "package": "com.sinthoras.visualprospecting.mixins.early", + "refmap": "mixins.visualprospecting.refmap.json", + "target": "@env(DEFAULT)", + "compatibilityLevel": "JAVA_8" +} diff --git a/src/main/resources/mixins.visualprospecting.json b/src/main/resources/mixins.visualprospecting.json new file mode 100644 index 00000000..f7ff5a1e --- /dev/null +++ b/src/main/resources/mixins.visualprospecting.json @@ -0,0 +1,8 @@ +{ + "required": true, + "minVersion": "0.7.11", + "package": "com.sinthoras.visualprospecting.mixins", + "refmap": "mixins.visualprospecting.refmap.json", + "target": "@env(DEFAULT)", + "compatibilityLevel": "JAVA_8" +} diff --git a/src/main/resources/mixins.visualprospecting.late.json b/src/main/resources/mixins.visualprospecting.late.json new file mode 100644 index 00000000..78c68a5e --- /dev/null +++ b/src/main/resources/mixins.visualprospecting.late.json @@ -0,0 +1,8 @@ +{ + "required": true, + "minVersion": "0.7.11", + "package": "com.sinthoras.visualprospecting.mixins.late", + "refmap": "mixins.visualprospecting.refmap.json", + "target": "@env(DEFAULT)", + "compatibilityLevel": "JAVA_8" +}