diff --git a/.github/actions/build_setup/action.yml b/.github/actions/build_setup/action.yml index a19adcdf6a..c5b8b2204e 100644 --- a/.github/actions/build_setup/action.yml +++ b/.github/actions/build_setup/action.yml @@ -41,3 +41,8 @@ runs: notifications jdks wrapper + + # run gradlew jar to set up necessary files before shadow can break them in the Build step + - name: Pre-build + shell: bash + run: ./gradlew jar --build-cache diff --git a/build.gradle b/build.gradle index e9f03fdced..523c646e53 100644 --- a/build.gradle +++ b/build.gradle @@ -1,3 +1,5 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar + plugins { id 'java-library' id 'org.jetbrains.kotlin.jvm' @@ -6,7 +8,7 @@ plugins { id 'maven-publish' alias libs.plugins.modDevGradle alias libs.plugins.machete // automatic jar compressing on build - //alias libs.plugins.shadow + alias libs.plugins.shadow alias libs.plugins.spotless alias libs.plugins.lombok } @@ -46,17 +48,27 @@ configurations { extraImplementation.extendsFrom(implementation) extraRuntimeOnly.extendsFrom(runtimeOnly).transitive(false) extraRuntimeClasspath.transitive(false) + includedLibrary { + canBeConsumed = false + canBeResolved = true + } } obfuscation { createRemappingConfiguration(configurations.extraRuntimeOnly) } +tasks.register('slimJar', ShadowJar) { + from sourceSets.main.output +} + +final refmapFile = mixin.add sourceSets.main, "gtceu.refmap.json" + apply from: "$rootDir/gradle/scripts/moddevgradle.gradle" apply from: "$rootDir/gradle/scripts/repositories.gradle" +apply from: "$rootDir/gradle/scripts/jars.gradle" apply from: "$rootDir/dependencies.gradle" apply from: "$rootDir/gradle/scripts/resources.gradle" -apply from: "$rootDir/gradle/scripts/jars.gradle" apply from: "$rootDir/gradle/scripts/publishing.gradle" apply from: "$rootDir/gradle/scripts/spotless.gradle" @@ -67,6 +79,17 @@ generateModMetadata.doFirst { mkdir('run/data') } +afterEvaluate { + tasks.withType(ShadowJar).configureEach { + from(refmapFile) + configurations = [project.configurations.includedLibrary] + minimize() + relocate("org.jgrapht", "${maven_group}.repack.org.jgrapht") + relocate("org.jheaps", "${maven_group}.repack.org.jheaps") + relocate("org.apfloat", "${maven_group}.repack.org.apfloat") + } +} + tasks.withType(JavaCompile) { options.encoding = "UTF-8" } diff --git a/dependencies.gradle b/dependencies.gradle index 22de67e18f..dfcc443395 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -1,6 +1,12 @@ dependencies { compileOnly 'org.jetbrains:annotations:24.1.0' + // Shadowed Dependencies + // Packages that aren't mods, and thus need to be included in the release files. + + api(includedLibrary('org.jgrapht:jgrapht-core:1.5.2')) // JGraphT 1.5.2 + additionalRuntimeClasspath('org.jgrapht:jgrapht-core:1.5.2') + // LDLib modApi(forge.ldlib.forge) { transitive = false } jarJar(forge.ldlib.forge) @@ -50,7 +56,7 @@ dependencies { modCompileOnly(forge.rhino) // Shimmer - modCompileOnly(forge.shimmer.forge) { transitive = false } + modImplementation(forge.shimmer.forge) { transitive = false } modCompileOnly("maven.modrinth:embeddium:0.3.19+mc1.20.1") modCompileOnly("maven.modrinth:oculus:1.20.1-1.7.0") diff --git a/gradle/scripts/jars.gradle b/gradle/scripts/jars.gradle index 0100328a1a..b6313f9471 100644 --- a/gradle/scripts/jars.gradle +++ b/gradle/scripts/jars.gradle @@ -1,12 +1,36 @@ -tasks.register('slimJar', Jar) { +// hack to avoid a bug in Shadow: https://github.com/GradleUp/shadow/issues/111 +tasks.register('shadowBugWorkaround', Jar) { + dependsOn jarJar + from jarJar.outputs +} + +shadowJar { + from tasks.shadowBugWorkaround + configurations = [project.configurations.includedLibrary] + archiveClassifier = "dev" +} + +slimJar { + configurations = [project.configurations.includedLibrary] archiveClassifier = "dev-slim" - from sourceSets.main.output } obfuscation { reobfuscate(tasks.named('slimJar'), sourceSets.main) { archiveClassifier = "slim" } + reobfuscate(tasks.named('shadowJar'), sourceSets.main) { + archiveClassifier = '' + } +} + +jar { + archiveClassifier = "dev-no-shadow" + archiveClassifier.convention("dev-no-shadow") +} + +reobfJar { + archiveClassifier = "no-shadow" } tasks.register('sourcesJar', Jar) { @@ -15,15 +39,15 @@ tasks.register('sourcesJar', Jar) { archiveClassifier = "sources" } -jar.archiveClassifier = "dev" -reobfJar.archiveClassifier = "" - base { archivesName = "${project.name}-${libs.versions.minecraft.get()}" } afterEvaluate { tasks.withType(org.gradle.jvm.tasks.Jar).configureEach { + exclude ".cache/*" + duplicatesStrategy = DuplicatesStrategy.INCLUDE + destinationDirectory = file('build/libs/') manifest.attributes([ 'MixinConfigs': 'gtceu.mixins.json', diff --git a/gradle/scripts/moddevgradle.gradle b/gradle/scripts/moddevgradle.gradle index 17ecb11344..cfc3c68562 100644 --- a/gradle/scripts/moddevgradle.gradle +++ b/gradle/scripts/moddevgradle.gradle @@ -1,6 +1,4 @@ - mixin { - add sourceSets.main, "gtceu.refmap.json" config 'gtceu.mixins.json' } diff --git a/gradle/scripts/publishing.gradle b/gradle/scripts/publishing.gradle index 76e7650e0a..61db621b85 100644 --- a/gradle/scripts/publishing.gradle +++ b/gradle/scripts/publishing.gradle @@ -1,5 +1,6 @@ artifacts { - archives tasks.reobfJar + archives tasks.shadowJar + archives tasks.reobfShadowJar archives tasks.reobfSlimJar archives tasks.sourcesJar } @@ -8,7 +9,7 @@ publishing { publications { mavenJava(MavenPublication) { groupId = project.maven_group - artifactId = project.archivesBaseName + artifactId = base.archivesName version = project.version from components.java diff --git a/gradle/scripts/resources.gradle b/gradle/scripts/resources.gradle index e89f53a031..38a2899563 100644 --- a/gradle/scripts/resources.gradle +++ b/gradle/scripts/resources.gradle @@ -52,3 +52,9 @@ var generateModMetadata = tasks.register("generateModMetadata", ProcessResources sourceSets.main.resources.srcDir generateModMetadata // To avoid having to run "generateModMetadata" manually, make it run on every project reload neoForge.ideSyncTask generateModMetadata + +afterEvaluate { + tasks.withType(ProcessResources).configureEach { + duplicatesStrategy = DuplicatesStrategy.INCLUDE + } +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0d1842103b..e1adfb4938 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/settings.gradle b/settings.gradle index 70cbca9c82..dad0e42813 100644 --- a/settings.gradle +++ b/settings.gradle @@ -49,7 +49,7 @@ dependencyResolutionManagement { // Libs def quiltMappingsVersion = "5" // https://lambdaurora.dev/tools/import_quilt.html def parchmentVersion = "2023.09.03" // https://parchmentmc.org/docs/getting-started - def shadowVersion = "7.1.2" + def shadowVersion = "8.3.0" def spotlessVersion = "6.25.0" def modDevGradleVersion = "2.0.52-beta" def vineFlowerVersion = "1.+" @@ -157,7 +157,7 @@ dependencyResolutionManagement { library("minecraft", "com.mojang", "minecraft").versionRef(minecraft) def shadow = version("shadow", shadowVersion) - plugin("shadow", "com.github.johnrengelman.shadow").versionRef(shadow) + plugin("shadow", "com.gradleup.shadow").versionRef(shadow) def spotless = version("spotless", spotlessVersion) plugin("spotless", "com.diffplug.spotless").versionRef(spotless) diff --git a/src/generated/resources/assets/gtceu/blockstates/oil_heavy.json b/src/generated/resources/assets/gtceu/blockstates/heavy_oil.json similarity index 50% rename from src/generated/resources/assets/gtceu/blockstates/oil_heavy.json rename to src/generated/resources/assets/gtceu/blockstates/heavy_oil.json index 89572bbc66..58eb6297d3 100644 --- a/src/generated/resources/assets/gtceu/blockstates/oil_heavy.json +++ b/src/generated/resources/assets/gtceu/blockstates/heavy_oil.json @@ -1,7 +1,7 @@ { "variants": { "": { - "model": "gtceu:block/oil_heavy" + "model": "gtceu:block/heavy_oil" } } } \ No newline at end of file diff --git a/src/generated/resources/assets/gtceu/blockstates/oil_light.json b/src/generated/resources/assets/gtceu/blockstates/light_oil.json similarity index 50% rename from src/generated/resources/assets/gtceu/blockstates/oil_light.json rename to src/generated/resources/assets/gtceu/blockstates/light_oil.json index 7da636112e..da76726bc2 100644 --- a/src/generated/resources/assets/gtceu/blockstates/oil_light.json +++ b/src/generated/resources/assets/gtceu/blockstates/light_oil.json @@ -1,7 +1,7 @@ { "variants": { "": { - "model": "gtceu:block/oil_light" + "model": "gtceu:block/light_oil" } } } \ No newline at end of file diff --git a/src/generated/resources/assets/gtceu/blockstates/oil_medium.json b/src/generated/resources/assets/gtceu/blockstates/oil_medium.json deleted file mode 100644 index 810ef09ee0..0000000000 --- a/src/generated/resources/assets/gtceu/blockstates/oil_medium.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "variants": { - "": { - "model": "gtceu:block/oil_medium" - } - } -} \ No newline at end of file diff --git a/src/generated/resources/assets/gtceu/blockstates/raw_oil.json b/src/generated/resources/assets/gtceu/blockstates/raw_oil.json new file mode 100644 index 0000000000..c741baaffd --- /dev/null +++ b/src/generated/resources/assets/gtceu/blockstates/raw_oil.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "gtceu:block/raw_oil" + } + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/gtceu/lang/en_ud.json b/src/generated/resources/assets/gtceu/lang/en_ud.json index 62aa34ef46..848ce72414 100644 --- a/src/generated/resources/assets/gtceu/lang/en_ud.json +++ b/src/generated/resources/assets/gtceu/lang/en_ud.json @@ -14,7 +14,7 @@ "behavior.portable_scanner.debug_lag_count": "˙ɹǝʌɹǝS ǝɥʇ uo )sɯ%s uɐɥʇ ɹǝbuoן buıʞɐʇ buıɥʇʎuɐ( sbuıuɹɐM ǝʞıdS bɐꞀ %s pǝsnɐƆ", "behavior.portable_scanner.debug_machine": "%s :ᗡI-ɐʇǝW", "behavior.portable_scanner.debug_machine_invalid": "¡pıןɐʌuı ", - "behavior.portable_scanner.debug_machine_invalid_null=invalid! MetaTileEntity =": "¡ןןnu ", + "behavior.portable_scanner.debug_machine_invalid_null": "¡ןןnu = ʎʇıʇuƎǝןı⟘ɐʇǝW ¡pıןɐʌuı", "behavior.portable_scanner.debug_machine_valid": "pıןɐʌ ", "behavior.portable_scanner.divider": "=========================", "behavior.portable_scanner.energy_container_in": "Ɐ %s ʇɐ ∩Ǝ )%s( %s :NI xɐW", @@ -50,6 +50,7 @@ "behavior.portable_scanner.tank": "%s ᗺɯ %s / ᗺɯ %s :%s ʞuɐ⟘", "behavior.portable_scanner.tanks_empty": "ʎʇdɯƎ sʞuɐ⟘ ןןⱯ", "behavior.portable_scanner.team_name": "ɹ§%s :ǝɯɐN ɯɐǝ⟘ᄅ§", + "behavior.portable_scanner.temperature": "ʞ%s :ǝɹnʇɐɹǝdɯǝ⟘", "behavior.portable_scanner.workable_consumption": "Ɐ %s ʇɐ ʇ/∩Ǝ %s :sǝs∩ ʎןqɐqoɹԀ", "behavior.portable_scanner.workable_production": "Ɐ %s ʇɐ ʇ/∩Ǝ %s :sǝɔnpoɹԀ ʎןqɐqoɹԀ", "behavior.portable_scanner.workable_progress": "s %s / s %s :ssǝɹboɹԀ", @@ -534,6 +535,8 @@ "block.gtceu.large_solidifier": "ʎɐɹɹⱯ uoıʇɐɔıɟıpıןoS ǝbɹɐꞀ", "block.gtceu.large_wiremill": "ʎɹoʇɔɐℲ ǝɹıM ǝbɹɐꞀ", "block.gtceu.laser_hazard_sign_block": "ʞɔoןᗺ ubıS pɹɐzɐH ɹǝsɐꞀ", + "block.gtceu.laser_pipe": "ǝdıԀ ɹǝsɐꞀ", + "block.gtceu.laser_reflector_pipe": "ǝdıԀ ɹoʇɔǝןɟǝᴚ ɹǝsɐꞀ", "block.gtceu.laser_safe_engraving_casing": "buısɐƆ buıʌɐɹbuƎ ǝɟɐS-ɹǝsɐꞀ", "block.gtceu.light_blue_borderless_lamp": "dɯɐꞀ ssǝןɹǝpɹoᗺ ǝnןᗺ ʇɥbıꞀ", "block.gtceu.light_blue_lamp": "dɯɐꞀ ǝnןᗺ ʇɥbıꞀ", @@ -892,14 +895,13 @@ "block.gtceu.noise_hazard_sign_block": "ʞɔoןᗺ ubıS pɹɐzɐH ǝsıoN", "block.gtceu.nonconducting_casing": "buısɐƆ buıʇɔnpuoɔuoN", "block.gtceu.normal_duct_pipe": "ǝdıԀ ʇɔnᗡ ןɐɯɹoN", - "block.gtceu.normal_laser_pipe": "ǝdıԀ ɹǝsɐꞀ ןɐɯɹoN", "block.gtceu.normal_laser_pipe.tooltip": "sǝuıן ʇɥbıɐɹʇs uı ㄥ§ssoן ouɟ§ ɥʇıʍ ɹǝʍod buıʇʇıɯsuɐɹ⟘ㄥ§", - "block.gtceu.normal_optical_pipe": "ǝןqɐƆ ɹǝqıℲ ןɐɔıʇdO", "block.gtceu.normal_optical_pipe.tooltip": "ㄥ§ɐʇɐᗡ ɥɔɹɐǝsǝᴚɟ§ ɹo ㄥ§uoıʇɐʇndɯoƆɟ§ buıʇʇıɯsuɐɹ⟘ㄥ§", "block.gtceu.object_holder": "ɹǝpןoH ʇɔǝظqO", "block.gtceu.oil_heavy": "ןıO ʎʌɐǝH", "block.gtceu.oil_light": "ןıO ʇɥbıꞀ", "block.gtceu.oil_medium": "ןıO ʍɐᴚ", + "block.gtceu.optical_fiber_cable": "ǝןqɐƆ ɹǝqıℲ ןɐɔıʇdO", "block.gtceu.opv_1024a_laser_source_hatch": "ɥɔʇɐH ǝɔɹnoS ɹǝsɐꞀ ɹ§Ɐǝ§ㄣᄅ0'Ɩ ɹ§ΛdOן§6§", "block.gtceu.opv_1024a_laser_target_hatch": "ɥɔʇɐH ʇǝbɹɐ⟘ ɹǝsɐꞀ ɹ§Ɐǝ§ㄣᄅ0'Ɩ ɹ§ΛdOן§6§", "block.gtceu.opv_16a_energy_converter": "ɹǝʇɹǝʌuoƆ ʎbɹǝuƎ ɹ§Ɐǝ§9Ɩ ɹ§ΛdOן§6§", @@ -1736,6 +1738,7 @@ "config.gtceu.option.direction": "uoıʇɔǝɹıp", "config.gtceu.option.disableManualCompression": "uoıssǝɹdɯoƆןɐnuɐWǝןqɐsıp", "config.gtceu.option.doBedrockOres": "sǝɹOʞɔoɹpǝᗺop", + "config.gtceu.option.doDatafixers": "sɹǝxıɟɐʇɐᗡop", "config.gtceu.option.doSuperflatOres": "sǝɹOʇɐןɟɹǝdnSop", "config.gtceu.option.doesExplosionDamagesTerrain": "uıɐɹɹǝ⟘sǝbɐɯɐᗡuoısoןdxƎsǝop", "config.gtceu.option.dumpAssets": "sʇǝssⱯdɯnp", @@ -1818,6 +1821,7 @@ "config.gtceu.option.oreVeinRandomOffset": "ʇǝsɟɟOɯopuɐᴚuıǝΛǝɹo", "config.gtceu.option.oreVeins": "suıǝΛǝɹo", "config.gtceu.option.ownerOPBypass": "ssɐdʎᗺԀOɹǝuʍo", + "config.gtceu.option.preventAnimatedCables": "sǝןqɐƆpǝʇɐɯıuⱯʇuǝʌǝɹd", "config.gtceu.option.prospectorEnergyUseMultiplier": "ɹǝıןdıʇןnWǝs∩ʎbɹǝuƎɹoʇɔǝdsoɹd", "config.gtceu.option.recipeProgressLowEnergy": "ʎbɹǝuƎʍoꞀssǝɹboɹԀǝdıɔǝɹ", "config.gtceu.option.recipes": "sǝdıɔǝɹ", @@ -1883,6 +1887,7 @@ "config.jade.plugin_gtceu.me_pattern_buffer_proxy": "oɟuI ʎxoɹԀ ɹǝɟɟnᗺ uɹǝʇʇɐԀ ]nƎƆ⟘⅁[", "config.jade.plugin_gtceu.multiblock_structure": "ǝɹnʇɔnɹʇS ʞɔoןᗺıʇןnW ]nƎƆ⟘⅁[", "config.jade.plugin_gtceu.parallel_info": "oɟuI ןǝןןɐɹɐԀ ]nƎƆ⟘⅁[", + "config.jade.plugin_gtceu.pipe": "oɟuI ǝdıԀ ]nƎƆ⟘⅁[", "config.jade.plugin_gtceu.primitive_pump": "oɟuI dɯnԀ ǝʌıʇıɯıɹԀ ]nƎƆ⟘⅁[", "config.jade.plugin_gtceu.recipe_logic_provider": "ɔıboꞀ ǝdıɔǝᴚ ]nƎƆ⟘⅁[", "config.jade.plugin_gtceu.recipe_output_info": "oɟuI ʇndʇnO ǝdıɔǝᴚ ]nƎƆ⟘⅁[", @@ -1935,14 +1940,6 @@ "cover.conveyor.blocks_input.disabled.1": "pǝןqɐsıᗡɔ§", "cover.conveyor.blocks_input.enabled.0": "˙ǝdıd oʇuı ʎɹoʇuǝʌuı ǝɥʇ ɯoɹɟ sɯǝʇı ןןnd oʇ ʇǝs sı ɹǝʌoɔ uǝɥʍ pǝʇɹǝsuı ǝq ʇou ןןıʍ sɯǝʇı 'pǝןqɐuǝ ɟI", "cover.conveyor.blocks_input.enabled.1": "pǝןqɐuƎɐ§", - "cover.conveyor.distribution.insert_first.0": "ʎʇıɹoıɹԀq§ :ǝpoW uoıʇnqıɹʇsıᗡ", - "cover.conveyor.distribution.insert_first.1": "˙puıɟ uɐɔ ʇı ʎʇıɹoıɹd ʇsǝɥbıɥ ǝɥʇ ɥʇıʍ ʎɹoʇuǝʌuı ʇsɹıɟ ǝɥʇ oʇuı ʇɹǝsuı ןןıMㄥ§", - "cover.conveyor.distribution.insert_first.2": "˙ɥʇɐd ɐ ɟo ʎʇıɹoıɹd ǝɥʇ ɹǝʍoן sǝdıd ɯǝʇı ǝʌıʇɔıɹʇsǝᴚㄥ§", - "cover.conveyor.distribution.round_robin_global.0": "uıqoᴚ punoᴚq§ :ǝpoW uoıʇnqıɹʇsıᗡ", - "cover.conveyor.distribution.round_robin_global.1": "sǝıɹoʇuǝʌuı pǝʇɔǝuuoɔ ssoɹɔɐ ʎןןɐnbǝ sɯǝʇı sʇıןdSㄥ§", - "cover.conveyor.distribution.round_robin_prio.0": "ʎʇıɹoıɹԀ ɥʇıʍ uıqoᴚ punoᴚq§ :ǝpoW uoıʇnqıɹʇsıᗡ", - "cover.conveyor.distribution.round_robin_prio.1": "˙ʇsɹıɟ sǝıʇıɹoıɹd ɹǝɥbıɥ sɹǝpısuoɔ puɐ sǝıɹoʇuǝʌuı pǝʇɔǝuuoɔ ssoɹɔɐ sɯǝʇı ʇıןds oʇ sǝıɹ⟘ㄥ§", - "cover.conveyor.distribution.round_robin_prio.2": "˙ɥʇɐd ɐ ɟo ʎʇıɹoıɹd ǝɥʇ ɹǝʍoן sǝdıd ɯǝʇı ǝʌıʇɔıɹʇsǝᴚㄥ§", "cover.conveyor.item_filter.title": "ɹǝʇןıℲ ɯǝʇI", "cover.conveyor.mode": "%s :ǝpoW", "cover.conveyor.mode.export": "ʇɹodxƎ :ǝpoW", @@ -1985,6 +1982,14 @@ "cover.fluid_regulator.transfer_mode.description.1": "˙pǝʌoɯ ǝq ʇ,uoʍ spınןɟ 'ǝzıs uoıʇɹod uɐɥʇ ssǝן sı spınןɟ ɟo ʇunoɯɐ ɟI ˙uoʇʇnq sıɥʇ ɥʇɐǝuɹǝpun ʍopuıʍ ǝɥʇ uı pǝıɟıɔǝds suoıʇɹod uı spınןɟ ʎןddns ןןıʍ ɹǝʌoɔ 'ǝpoɯ sıɥʇ uı - ɹ§ʇɔɐxƎ ʎןddnSǝ§", "cover.fluid_regulator.transfer_mode.description.2": "˙pǝɹınbǝɹ ɟı spınןɟ ɟo ʇunoɯɐ ןɐuoıʇıppɐ buıʎןddns 'ʎɹoʇuǝʌuı uoıʇɐuıʇsǝp ǝɥʇ uı spınןɟ ɟo ʇunoɯɐ pǝıɟıɔǝds dǝǝʞ ןןıʍ ɹǝʌoɔ 'ǝpoɯ sıɥʇ uı - ɹ§ʇɔɐxƎ dǝǝʞǝ§", "cover.fluid_regulator.transfer_mode.description.3": "˙00Ɩ ʎq ʎןdıʇןnɯ ןןıʍ ʞɔıןɔ ןɹʇɔ puɐ 0Ɩ ʎq sʇunoɯɐ ǝsɐǝɹɔǝp/ǝsɐǝɹɔuı ʎןdıʇןnɯ ןןıʍ ʞɔıןɔ ʇɟıɥs :dı⟘ㄥ§", + "cover.generic.distribution.equalized.0": "ɹ§uoıʇnqıɹʇsıᗡ ןɐnbƎq§", + "cover.generic.distribution.equalized.1": "˙uoıʇɐɹǝdo ɹǝd ʇunoɯɐ ǝɯɐs ǝɥʇ ʎq suoıʇɐuıʇsǝp ןןɐ sןןıℲㄥ§", + "cover.generic.distribution.equalized.2": "˙ʎןbuıɹɐds ǝs∩ ˙ǝʌısuǝdxǝ ʎןןɐuoıʇɐʇndɯoɔ ǝq ʎɐWɔ§", + "cover.generic.distribution.flood.0": "ɹ§ʇɹǝsuI pooןℲq§", + "cover.generic.distribution.flood.1": "˙ǝzıןɐnbǝ ʇou sǝop puɐ sǝıʇıɹoıɹd ɹıǝɥʇ uo pǝsɐq suoıʇɐuıʇsǝp sןןıℲㄥ§", + "cover.generic.distribution.name": "ǝpoW uoıʇnqıɹʇsıᗡ", + "cover.generic.distribution.round_robin.0": "ɹ§uıqoᴚ punoᴚq§", + "cover.generic.distribution.round_robin.1": "˙ǝzıןɐnbǝ ʇou sǝop ʇnq 'ɹǝpɹo pǝxıɟ ɐ uı suoıʇɐuıʇsǝp sןןıℲㄥ§", "cover.item.voiding.advanced.title": "sbuıʇʇǝS buıpıoΛ ɯǝʇI pǝɔuɐʌpⱯ", "cover.item.voiding.title": "sbuıʇʇǝS buıpıoΛ ɯǝʇI", "cover.item_filter.ignore_damage.disabled": "ǝbɐɯɐᗡ ʇɔǝdsǝᴚ", @@ -2126,6 +2131,10 @@ "fluid.tile.lava": "ɐʌɐꞀ", "fluid.tile.water": "ɹǝʇɐM", "fluid_cell.empty": "ʎʇdɯƎ", + "gregtech.top.pipe.amperage": ":s / ǝbɐɹǝdɯⱯ ǝbɐɹǝʌⱯ", + "gregtech.top.pipe.fluid_last": ":pınןℲ ʇsɐꞀ", + "gregtech.top.pipe.item_last": ":ɯǝʇI ʇsɐꞀ", + "gregtech.top.pipe.voltage": ":s / ǝbɐʇןoΛ ǝbɐɹǝʌⱯ", "gtceu.air_scrubber": "ɹǝqqnɹɔS ɹıⱯ", "gtceu.alloy_blast_smelter": "ɹǝʇןǝɯS ʇsɐןᗺ ʎoןןⱯ", "gtceu.alloy_smelter": "ɹǝʇןǝɯS ʎoןןⱯ", @@ -2259,7 +2268,8 @@ "gtceu.fluid_pipe.channels": "%dɟ§ :sןǝuuɐɥƆǝ§", "gtceu.fluid_pipe.cryo_proof": "sɔıuǝboʎɹƆ ǝןpuɐɥ uɐƆ9§", "gtceu.fluid_pipe.gas_proof": "sǝsɐ⅁ ǝןpuɐɥ uɐƆ9§", - "gtceu.fluid_pipe.max_temperature": "ʞ %dɟ§ :ʇıɯıꞀ ǝɹnʇɐɹǝdɯǝ⟘ɔ§", + "gtceu.fluid_pipe.max_temperature": "ʞ %sɟ§ :ǝɹnʇɐɹǝdɯǝ⟘ xɐWɔ§", + "gtceu.fluid_pipe.min_temperature": "ʞ %sɟ§ :ǝɹnʇɐɹǝdɯǝ⟘ uıWɔ§", "gtceu.fluid_pipe.not_gas_proof": "¡ʞɐǝן ʎɐɯ sǝsɐ⅁ㄣ§", "gtceu.fluid_pipe.plasma_proof": "sɐɯsɐןԀ ןןɐ ǝןpuɐɥ uɐƆ9§", "gtceu.fluid_solidifier": "ɹǝıɟıpıןoS pınןℲ", @@ -2381,7 +2391,6 @@ "gtceu.item_filter.empty_item": ")ɯǝʇI oN( ʎʇdɯƎ", "gtceu.item_filter.footer": "ǝpıɹɹǝʌo oʇ ɯǝʇı ɥʇıʍ ʞɔıןƆǝ§", "gtceu.item_list.item_stored": "%d :pǝɹoʇSㄥ§", - "gtceu.item_pipe.priority": "%dɟ§ :ʎʇıɹoıɹԀ6§", "gtceu.jade.cleaned_this_second": "s/%s :pɹɐzɐɥ pǝuɐǝןƆ", "gtceu.jade.energy_stored": "∩Ǝ %d / %d", "gtceu.jade.progress_computation": "∩MƆ %s / %s", @@ -3375,6 +3384,9 @@ "gtceu.oc.tooltip.4": "ƆO ʇɔǝɟɹǝԀ ʎq ǝbuɐɥɔ oʇ ʇɟıɥS pןoH", "gtceu.ore_washer": "ɹǝɥsɐM ǝɹO", "gtceu.packer": "ɹǝʞɔɐԀ", + "gtceu.pipe.fluid_pipe": "ǝdıԀ pınןℲp§", + "gtceu.pipe.item_pipe": "ǝdıԀ ɯǝʇIp§", + "gtceu.pipe.priority": "%dɟ§ :ʎʇıɹoıɹԀ6§", "gtceu.plasma_generator": "ɹoʇɐɹǝuǝ⅁ ɐɯsɐןԀ", "gtceu.polarizer": "ɹǝzıɹɐןoԀ", "gtceu.primitive_blast_furnace": "ǝɔɐuɹnℲ ʇsɐןᗺ ǝʌıʇıɯıɹԀ", @@ -3542,6 +3554,8 @@ "gtceu.top.mode.export": "buıʇɹodxƎ", "gtceu.top.mode.import": "buıʇɹodɯI", "gtceu.top.obstructed_structure": "pǝʇɔnɹʇsqO ǝɹnʇɔnɹʇS", + "gtceu.top.pipe.fluid_last": " :pınןℲ ʇsɐꞀ", + "gtceu.top.pipe.item_last": " :ɯǝʇI ʇsɐꞀ", "gtceu.top.primitive_pump_production": "s/ᗺɯ %s :uoıʇɔnpoɹԀ", "gtceu.top.progress_computation": "∩MƆ %s / ", "gtceu.top.progress_sec": "s %s / ", @@ -3636,6 +3650,7 @@ "item.gtceu.advanced_item_voiding_cover": "ɹǝʌoƆ buıpıoΛ ɯǝʇI pǝɔuɐʌpⱯ", "item.gtceu.advanced_item_voiding_cover.tooltip.0": "˙ㄥ§ɹǝʌoƆɟ§ sɐ ןoɹʇuoɔ ʇunoɯɐ ɥʇıʍ ㄥ§sɯǝʇIɟ§ spıoΛㄥ§", "item.gtceu.advanced_item_voiding_cover.tooltip.1": "˙ʇuǝɯǝɔɐןd ɹǝʇɟɐ ㄥ§ʇǝןןɐW ʇɟoSɟ§ ɥʇıʍ ǝʇɐʌıʇɔⱯ", + "item.gtceu.advanced_nanomuscle_chestplate": "ǝʇɐןdʇsǝɥƆ ǝʇınS ™ǝןɔsnWouɐN pǝɔuɐʌpⱯ", "item.gtceu.advanced_power_thruster": "ɹǝʇsnɹɥ⟘ ɹǝʍoԀ pǝɔuɐʌpⱯ", "item.gtceu.advanced_quarktech_chestplate": "ǝʇɐןdʇsǝɥƆ ǝʇınS ™ɥɔǝ⟘ʞɹɐnὉ pǝɔuɐʌpⱯ", "item.gtceu.advanced_smd_capacitor": "ɹoʇıɔɐdɐƆ ᗡWS pǝɔuɐʌpⱯ", @@ -3658,7 +3673,6 @@ "item.gtceu.anvil_casting_mold": ")ןıʌuⱯ( pןoW buıʇsɐƆ", "item.gtceu.anvil_casting_mold.tooltip": "sןıʌuⱯ buıdɐɥs ɹoɟ pןoWㄥ§", "item.gtceu.ash_dust": "sǝɥsⱯ", - "item.gtceu.avanced_nanomuscle_chestplate": "ǝʇɐןdʇsǝɥƆ ǝʇınS ™ǝןɔsnWouɐN pǝɔuɐʌpⱯ", "item.gtceu.axe_extruder_mold.tooltip": "sǝxⱯ buıʞɐɯ ɹoɟ ǝdɐɥS ɹǝpnɹʇxƎㄥ§", "item.gtceu.ball_casting_mold": ")ןןɐᗺ( pןoW buıʇsɐƆ", "item.gtceu.ball_casting_mold.tooltip": "sןןɐᗺ buıʞɐɯ ɹoɟ pןoWㄥ§", @@ -3782,6 +3796,7 @@ "item.gtceu.data_stick": "ʞɔıʇS ɐʇɐᗡ", "item.gtceu.data_stick.tooltip": "ǝbɐɹoʇS ɐʇɐᗡ ʎʇıɔɐdɐƆ ʍoꞀ Ɐㄥ§", "item.gtceu.diamond_grinding_head": "pɐǝH buıpuıɹ⅁ puoɯɐıᗡ", + "item.gtceu.dielectric_laser_mirror": "ɹoɹɹıW ɹǝsɐꞀ ɔıɹʇɔǝןǝıᗡ", "item.gtceu.diode": "ǝpoıᗡ", "item.gtceu.diode.tooltip": "ʇuǝuodɯoƆ ɔıuoɹʇɔǝןƎ ɔısɐᗺㄥ§", "item.gtceu.doge_coin": "uıoƆ ǝboᗡ", @@ -4887,7 +4902,6 @@ "material.gtceu.goethite": "ǝʇıɥʇǝo⅁", "material.gtceu.gold": "pןo⅁", "material.gtceu.granite": "ǝʇıuɐɹ⅁", - "material.gtceu.granite_red": "pǝᴚ ǝʇıuɐɹ⅁", "material.gtceu.granitic_mineral_sand": "puɐS ןɐɹǝuıW ɔıʇıuɐɹ⅁", "material.gtceu.graphene": "ǝuǝɥdɐɹ⅁", "material.gtceu.graphite": "ǝʇıɥdɐɹ⅁", @@ -4903,6 +4917,7 @@ "material.gtceu.hastelloy_c_276": "9ㄥᄅ-Ɔ ʎoןןǝʇsɐH", "material.gtceu.hastelloy_x": "X ʎoןןǝʇsɐH", "material.gtceu.heavy_fuel": "ןǝnℲ ʎʌɐǝH", + "material.gtceu.heavy_oil": "ןıO ʎʌɐǝH", "material.gtceu.helium": "ɯnıןǝH", "material.gtceu.helium_3": "Ɛ ɯnıןǝH", "material.gtceu.hematite": "ǝʇıʇɐɯǝH", @@ -4965,6 +4980,7 @@ "material.gtceu.light_blue_dye": "ǝʎᗡ ǝnןᗺ ʇɥbıꞀ", "material.gtceu.light_fuel": "ןǝnℲ ʇɥbıꞀ", "material.gtceu.light_gray_dye": "ǝʎᗡ ʎɐɹ⅁ ʇɥbıꞀ", + "material.gtceu.light_oil": "ןıO ʇɥbıꞀ", "material.gtceu.lightly_hydro_cracked_gas": "sɐ⅁ pǝʞɔɐɹƆ-oɹpʎH ʎןʇɥbıꞀ", "material.gtceu.lightly_hydro_cracked_heavy_fuel": "ןǝnℲ ʎʌɐǝH pǝʞɔɐɹƆ-oɹpʎH ʎןʇɥbıꞀ", "material.gtceu.lightly_hydro_cracked_light_fuel": "ןǝnℲ ʇɥbıꞀ pǝʞɔɐɹƆ-oɹpʎH ʎןʇɥbıꞀ", @@ -5060,9 +5076,6 @@ "material.gtceu.octane": "ǝuɐʇɔO", "material.gtceu.oganesson": "uossǝuɐbO", "material.gtceu.oil": "ןıO", - "material.gtceu.oil_heavy": "ןıO ʎʌɐǝH", - "material.gtceu.oil_light": "ןıO ʇɥbıꞀ", - "material.gtceu.oil_medium": "ןıO ʍɐᴚ", "material.gtceu.oilsands": "spuɐsןıO", "material.gtceu.olivine": "ǝuıʌıןO", "material.gtceu.opal": "ןɐdO", @@ -5090,7 +5103,7 @@ "material.gtceu.platinum_group_sludge": "ǝbpnןS dnoɹ⅁ ɯnuıʇɐןԀ", "material.gtceu.platinum_raw": "ʍɐᴚ ɯnuıʇɐןԀ", "material.gtceu.platinum_sludge_residue": "ǝnpısǝᴚ ǝbpnןS ɯnuıʇɐןԀ", - "material.gtceu.plutonium": "ɯnıuoʇnןԀ", + "material.gtceu.plutonium_239": "6Ɛᄅ ɯnıuoʇnןԀ", "material.gtceu.plutonium_241": "Ɩㄣᄅ ɯnıuoʇnןԀ", "material.gtceu.pollucite": "ǝʇıɔnןןoԀ", "material.gtceu.polonium": "ɯnıuoןoԀ", @@ -5138,12 +5151,14 @@ "material.gtceu.raw_brine": "ǝuıɹᗺ ʍɐᴚ", "material.gtceu.raw_gasoline": "ǝuıןosɐ⅁ ʍɐᴚ", "material.gtceu.raw_growth_medium": "ɯnıpǝW ɥʇʍoɹ⅁ ʍɐᴚ", + "material.gtceu.raw_oil": "ןıO ʍɐᴚ", "material.gtceu.raw_rubber": "ɹǝqqnᴚ ʍɐᴚ", "material.gtceu.raw_styrene_butadiene_rubber": "ɹǝqqnᴚ ǝuǝıpɐʇnᗺ ǝuǝɹʎʇS ʍɐᴚ", "material.gtceu.realgar": "ɹɐbןɐǝᴚ", "material.gtceu.red_alloy": "ʎoןןⱯ pǝᴚ", "material.gtceu.red_dye": "ǝʎᗡ pǝᴚ", "material.gtceu.red_garnet": "ʇǝuɹɐ⅁ pǝᴚ", + "material.gtceu.red_granite": "ǝʇıuɐɹ⅁ pǝᴚ", "material.gtceu.red_steel": "ןǝǝʇS pǝᴚ", "material.gtceu.redrock": "ʞɔoɹpǝᴚ", "material.gtceu.redstone": "ǝuoʇspǝᴚ", @@ -5274,8 +5289,8 @@ "material.gtceu.tungstic_acid": "pıɔⱯ ɔıʇsbun⟘", "material.gtceu.ultimet": "ʇǝɯıʇן∩", "material.gtceu.uraninite": "ǝʇıuıuɐɹ∩", - "material.gtceu.uranium": "ɯnıuɐɹ∩", "material.gtceu.uranium_235": "ϛƐᄅ ɯnıuɐɹ∩", + "material.gtceu.uranium_238": "8Ɛᄅ ɯnıuɐɹ∩", "material.gtceu.uranium_hexafluoride": "ǝpıɹonןɟɐxǝH ɯnıuɐɹ∩", "material.gtceu.uranium_rhodium_dinaquadide": "ǝpıpɐnbɐuıᗡ ɯnıpoɥᴚ ɯnıuɐɹ∩", "material.gtceu.uranium_triplatinum": "ɯnuıʇɐןdıɹ⟘ ɯnıuɐɹ∩", @@ -5465,21 +5480,20 @@ "tagprefix.marble": "ǝɹO %s ǝןqɹɐW", "tagprefix.netherrack": "ǝɹO %s ɹǝɥʇǝN", "tagprefix.nugget": "ʇǝbbnN %s", - "tagprefix.pipe_huge_fluid": "ǝdıԀ pınןℲ %s ǝbnH", - "tagprefix.pipe_huge_item": "ǝdıԀ ɯǝʇI %s ǝbnH", - "tagprefix.pipe_huge_restrictive": "ǝdıԀ ɯǝʇI %s ǝʌıʇɔıɹʇsǝᴚ ǝbnH", - "tagprefix.pipe_large_fluid": "ǝdıԀ pınןℲ %s ǝbɹɐꞀ", - "tagprefix.pipe_large_item": "ǝdıԀ ɯǝʇI %s ǝbɹɐꞀ", - "tagprefix.pipe_large_restrictive": "ǝdıԀ ɯǝʇI %s ǝʌıʇɔıɹʇsǝᴚ ǝbɹɐꞀ", - "tagprefix.pipe_nonuple_fluid": "ǝdıԀ pınןℲ %s ǝןdnuoN", - "tagprefix.pipe_normal_fluid": "ǝdıԀ pınןℲ %s ןɐɯɹoN", - "tagprefix.pipe_normal_item": "ǝdıԀ ɯǝʇI %s ןɐɯɹoN", - "tagprefix.pipe_normal_restrictive": "ǝdıԀ ɯǝʇI %s ǝʌıʇɔıɹʇsǝᴚ ןɐɯɹoN", - "tagprefix.pipe_quadruple_fluid": "ǝdıԀ pınןℲ %s ǝןdnɹpɐnὉ", - "tagprefix.pipe_small_fluid": "ǝdıԀ pınןℲ %s ןןɐɯS", - "tagprefix.pipe_small_item": "ǝdıԀ ɯǝʇI %s ןןɐɯS", - "tagprefix.pipe_small_restrictive": "ǝdıԀ ɯǝʇI %s ǝʌıʇɔıɹʇsǝᴚ ןןɐɯS", - "tagprefix.pipe_tiny_fluid": "ǝdıԀ pınןℲ %s ʎuı⟘", + "tagprefix.pipe_huge": "ǝdıԀ %s ǝbnH", + "tagprefix.pipe_huge_restrictive": "ǝdıԀ %s ǝʌıʇɔıɹʇsǝᴚ ǝbnH", + "tagprefix.pipe_large": "ǝdıԀ %s ǝbɹɐꞀ", + "tagprefix.pipe_large_restrictive": "ǝdıԀ %s ǝʌıʇɔıɹʇsǝᴚ ǝbɹɐꞀ", + "tagprefix.pipe_nonuple": "ǝdıԀ %s ǝןdnuoN", + "tagprefix.pipe_nonuple_restrictive": "ǝdıԀ %s ǝʌıʇɔıɹʇsǝᴚ ǝןdnuoN", + "tagprefix.pipe_normal": "ǝdıԀ %s ןɐɯɹoN", + "tagprefix.pipe_normal_restrictive": "ǝdıԀ %s ǝʌıʇɔıɹʇsǝᴚ ןɐɯɹoN", + "tagprefix.pipe_quadruple": "ǝdıԀ %s ǝןdnɹpɐnὉ", + "tagprefix.pipe_quadruple_restrictive": "ǝdıԀ %s ǝʌıʇɔıɹʇsǝᴚ ǝןdnɹpɐnὉ", + "tagprefix.pipe_small": "ǝdıԀ %s ןןɐɯS", + "tagprefix.pipe_small_restrictive": "ǝdıԀ %s ǝʌıʇɔıɹʇsǝᴚ ןןɐɯS", + "tagprefix.pipe_tiny": "ǝdıԀ %s ʎuı⟘", + "tagprefix.pipe_tiny_restrictive": "ǝdıԀ %s ǝʌıʇɔıɹʇsǝᴚ ʎuı⟘", "tagprefix.planks": "sʞuɐןԀ %s", "tagprefix.plate": "ǝʇɐןԀ %s", "tagprefix.polymer.dense_plate": "ʇǝǝɥS %s ǝsuǝᗡ", diff --git a/src/generated/resources/assets/gtceu/lang/en_us.json b/src/generated/resources/assets/gtceu/lang/en_us.json index de3228bc33..6d30615336 100644 --- a/src/generated/resources/assets/gtceu/lang/en_us.json +++ b/src/generated/resources/assets/gtceu/lang/en_us.json @@ -14,7 +14,7 @@ "behavior.portable_scanner.debug_lag_count": "Caused %s Lag Spike Warnings (anything taking longer than %sms) on the Server.", "behavior.portable_scanner.debug_machine": "Meta-ID: %s", "behavior.portable_scanner.debug_machine_invalid": " invalid!", - "behavior.portable_scanner.debug_machine_invalid_null=invalid! MetaTileEntity =": " null!", + "behavior.portable_scanner.debug_machine_invalid_null": "invalid! MetaTileEntity = null!", "behavior.portable_scanner.debug_machine_valid": " valid", "behavior.portable_scanner.divider": "=========================", "behavior.portable_scanner.energy_container_in": "Max IN: %s (%s) EU at %s A", @@ -50,6 +50,7 @@ "behavior.portable_scanner.tank": "Tank %s: %s mB / %s mB %s", "behavior.portable_scanner.tanks_empty": "All Tanks Empty", "behavior.portable_scanner.team_name": "§2Team Name: %s§r", + "behavior.portable_scanner.temperature": "Temperature: %sK", "behavior.portable_scanner.workable_consumption": "Probably Uses: %s EU/t at %s A", "behavior.portable_scanner.workable_production": "Probably Produces: %s EU/t at %s A", "behavior.portable_scanner.workable_progress": "Progress: %s s / %s s", @@ -534,6 +535,8 @@ "block.gtceu.large_solidifier": "Large Solidification Array", "block.gtceu.large_wiremill": "Large Wire Factory", "block.gtceu.laser_hazard_sign_block": "Laser Hazard Sign Block", + "block.gtceu.laser_pipe": "Laser Pipe", + "block.gtceu.laser_reflector_pipe": "Laser Reflector Pipe", "block.gtceu.laser_safe_engraving_casing": "Laser-Safe Engraving Casing", "block.gtceu.light_blue_borderless_lamp": "Light Blue Borderless Lamp", "block.gtceu.light_blue_lamp": "Light Blue Lamp", @@ -892,14 +895,13 @@ "block.gtceu.noise_hazard_sign_block": "Noise Hazard Sign Block", "block.gtceu.nonconducting_casing": "Nonconducting Casing", "block.gtceu.normal_duct_pipe": "Normal Duct Pipe", - "block.gtceu.normal_laser_pipe": "Normal Laser Pipe", "block.gtceu.normal_laser_pipe.tooltip": "§7Transmitting power with §fno loss§7 in straight lines", - "block.gtceu.normal_optical_pipe": "Optical Fiber Cable", "block.gtceu.normal_optical_pipe.tooltip": "§7Transmitting §fComputation§7 or §fResearch Data§7", "block.gtceu.object_holder": "Object Holder", "block.gtceu.oil_heavy": "Heavy Oil", "block.gtceu.oil_light": "Light Oil", "block.gtceu.oil_medium": "Raw Oil", + "block.gtceu.optical_fiber_cable": "Optical Fiber Cable", "block.gtceu.opv_1024a_laser_source_hatch": "§9§lOpV§r 1,024§eA§r Laser Source Hatch", "block.gtceu.opv_1024a_laser_target_hatch": "§9§lOpV§r 1,024§eA§r Laser Target Hatch", "block.gtceu.opv_16a_energy_converter": "§9§lOpV§r 16§eA§r Energy Converter", @@ -1736,6 +1738,7 @@ "config.gtceu.option.direction": "direction", "config.gtceu.option.disableManualCompression": "disableManualCompression", "config.gtceu.option.doBedrockOres": "doBedrockOres", + "config.gtceu.option.doDatafixers": "doDatafixers", "config.gtceu.option.doSuperflatOres": "doSuperflatOres", "config.gtceu.option.doesExplosionDamagesTerrain": "doesExplosionDamagesTerrain", "config.gtceu.option.dumpAssets": "dumpAssets", @@ -1818,6 +1821,7 @@ "config.gtceu.option.oreVeinRandomOffset": "oreVeinRandomOffset", "config.gtceu.option.oreVeins": "oreVeins", "config.gtceu.option.ownerOPBypass": "ownerOPBypass", + "config.gtceu.option.preventAnimatedCables": "preventAnimatedCables", "config.gtceu.option.prospectorEnergyUseMultiplier": "prospectorEnergyUseMultiplier", "config.gtceu.option.recipeProgressLowEnergy": "recipeProgressLowEnergy", "config.gtceu.option.recipes": "recipes", @@ -1883,6 +1887,7 @@ "config.jade.plugin_gtceu.me_pattern_buffer_proxy": "[GTCEu] Pattern Buffer Proxy Info", "config.jade.plugin_gtceu.multiblock_structure": "[GTCEu] MultiBlock Structure", "config.jade.plugin_gtceu.parallel_info": "[GTCEu] Parallel Info", + "config.jade.plugin_gtceu.pipe": "[GTCEu] Pipe Info", "config.jade.plugin_gtceu.primitive_pump": "[GTCEu] Primitive Pump Info", "config.jade.plugin_gtceu.recipe_logic_provider": "[GTCEu] Recipe Logic", "config.jade.plugin_gtceu.recipe_output_info": "[GTCEu] Recipe Output Info", @@ -1935,14 +1940,6 @@ "cover.conveyor.blocks_input.disabled.1": "§cDisabled", "cover.conveyor.blocks_input.enabled.0": "If enabled, items will not be inserted when cover is set to pull items from the inventory into pipe.", "cover.conveyor.blocks_input.enabled.1": "§aEnabled", - "cover.conveyor.distribution.insert_first.0": "Distribution Mode: §bPriority", - "cover.conveyor.distribution.insert_first.1": "§7Will insert into the first inventory with the highest priority it can find.", - "cover.conveyor.distribution.insert_first.2": "§7Restrictive item pipes lower the priority of a path.", - "cover.conveyor.distribution.round_robin_global.0": "Distribution Mode: §bRound Robin", - "cover.conveyor.distribution.round_robin_global.1": "§7Splits items equally across connected inventories", - "cover.conveyor.distribution.round_robin_prio.0": "Distribution Mode: §bRound Robin with Priority", - "cover.conveyor.distribution.round_robin_prio.1": "§7Tries to split items across connected inventories and considers higher priorities first.", - "cover.conveyor.distribution.round_robin_prio.2": "§7Restrictive item pipes lower the priority of a path.", "cover.conveyor.item_filter.title": "Item Filter", "cover.conveyor.mode": "Mode: %s", "cover.conveyor.mode.export": "Mode: Export", @@ -1985,6 +1982,14 @@ "cover.fluid_regulator.transfer_mode.description.1": "§eSupply Exact§r - in this mode, cover will supply fluids in portions specified in the window underneath this button. If amount of fluids is less than portion size, fluids won't be moved.", "cover.fluid_regulator.transfer_mode.description.2": "§eKeep Exact§r - in this mode, cover will keep specified amount of fluids in the destination inventory, supplying additional amount of fluids if required.", "cover.fluid_regulator.transfer_mode.description.3": "§7Tip: shift click will multiply increase/decrease amounts by 10 and ctrl click will multiply by 100.", + "cover.generic.distribution.equalized.0": "§bEqual Distribution§r", + "cover.generic.distribution.equalized.1": "§7Fills all destinations by the same amount per operation.", + "cover.generic.distribution.equalized.2": "§cMay be computationally expensive. Use sparingly.", + "cover.generic.distribution.flood.0": "§bFlood Insert§r", + "cover.generic.distribution.flood.1": "§7Fills destinations based on their priorities and does not equalize.", + "cover.generic.distribution.name": "Distribution Mode", + "cover.generic.distribution.round_robin.0": "§bRound Robin§r", + "cover.generic.distribution.round_robin.1": "§7Fills destinations in a fixed order, but does not equalize.", "cover.item.voiding.advanced.title": "Advanced Item Voiding Settings", "cover.item.voiding.title": "Item Voiding Settings", "cover.item_filter.ignore_damage.disabled": "Respect Damage", @@ -2126,6 +2131,10 @@ "fluid.tile.lava": "Lava", "fluid.tile.water": "Water", "fluid_cell.empty": "Empty", + "gregtech.top.pipe.amperage": "Average Amperage / s:", + "gregtech.top.pipe.fluid_last": "Last Fluid:", + "gregtech.top.pipe.item_last": "Last Item:", + "gregtech.top.pipe.voltage": "Average Voltage / s:", "gtceu.air_scrubber": "Air Scrubber", "gtceu.alloy_blast_smelter": "Alloy Blast Smelter", "gtceu.alloy_smelter": "Alloy Smelter", @@ -2259,7 +2268,8 @@ "gtceu.fluid_pipe.channels": "§eChannels: §f%d", "gtceu.fluid_pipe.cryo_proof": "§6Can handle Cryogenics", "gtceu.fluid_pipe.gas_proof": "§6Can handle Gases", - "gtceu.fluid_pipe.max_temperature": "§cTemperature Limit: §f%d K", + "gtceu.fluid_pipe.max_temperature": "§cMax Temperature: §f%s K", + "gtceu.fluid_pipe.min_temperature": "§cMin Temperature: §f%s K", "gtceu.fluid_pipe.not_gas_proof": "§4Gases may leak!", "gtceu.fluid_pipe.plasma_proof": "§6Can handle all Plasmas", "gtceu.fluid_solidifier": "Fluid Solidifier", @@ -2381,7 +2391,6 @@ "gtceu.item_filter.empty_item": "Empty (No Item)", "gtceu.item_filter.footer": "§eClick with item to override", "gtceu.item_list.item_stored": "§7Stored: %d", - "gtceu.item_pipe.priority": "§9Priority: §f%d", "gtceu.jade.cleaned_this_second": "Cleaned hazard: %s/s", "gtceu.jade.energy_stored": "%d / %d EU", "gtceu.jade.progress_computation": "%s / %s CWU", @@ -3375,6 +3384,9 @@ "gtceu.oc.tooltip.4": "Hold Shift to change by Perfect OC", "gtceu.ore_washer": "Ore Washer", "gtceu.packer": "Packer", + "gtceu.pipe.fluid_pipe": "§dFluid Pipe", + "gtceu.pipe.item_pipe": "§dItem Pipe", + "gtceu.pipe.priority": "§9Priority: §f%d", "gtceu.plasma_generator": "Plasma Generator", "gtceu.polarizer": "Polarizer", "gtceu.primitive_blast_furnace": "Primitive Blast Furnace", @@ -3542,6 +3554,8 @@ "gtceu.top.mode.export": "Exporting", "gtceu.top.mode.import": "Importing", "gtceu.top.obstructed_structure": "Structure Obstructed", + "gtceu.top.pipe.fluid_last": "Last Fluid: ", + "gtceu.top.pipe.item_last": "Last Item: ", "gtceu.top.primitive_pump_production": "Production: %s mB/s", "gtceu.top.progress_computation": " / %s CWU", "gtceu.top.progress_sec": " / %s s", @@ -3636,6 +3650,7 @@ "item.gtceu.advanced_item_voiding_cover": "Advanced Item Voiding Cover", "item.gtceu.advanced_item_voiding_cover.tooltip.0": "§7Voids §fItems§7 with amount control as §fCover§7.", "item.gtceu.advanced_item_voiding_cover.tooltip.1": "Activate with §fSoft Mallet§7 after placement.", + "item.gtceu.advanced_nanomuscle_chestplate": "Advanced NanoMuscle™ Suite Chestplate", "item.gtceu.advanced_power_thruster": "Advanced Power Thruster", "item.gtceu.advanced_quarktech_chestplate": "Advanced QuarkTech™ Suite Chestplate", "item.gtceu.advanced_smd_capacitor": "Advanced SMD Capacitor", @@ -3658,7 +3673,6 @@ "item.gtceu.anvil_casting_mold": "Casting Mold (Anvil)", "item.gtceu.anvil_casting_mold.tooltip": "§7Mold for shaping Anvils", "item.gtceu.ash_dust": "Ashes", - "item.gtceu.avanced_nanomuscle_chestplate": "Advanced NanoMuscle™ Suite Chestplate", "item.gtceu.axe_extruder_mold.tooltip": "§7Extruder Shape for making Axes", "item.gtceu.ball_casting_mold": "Casting Mold (Ball)", "item.gtceu.ball_casting_mold.tooltip": "§7Mold for making Balls", @@ -3782,6 +3796,7 @@ "item.gtceu.data_stick": "Data Stick", "item.gtceu.data_stick.tooltip": "§7A Low Capacity Data Storage", "item.gtceu.diamond_grinding_head": "Diamond Grinding Head", + "item.gtceu.dielectric_laser_mirror": "Dielectric Laser Mirror", "item.gtceu.diode": "Diode", "item.gtceu.diode.tooltip": "§7Basic Electronic Component", "item.gtceu.doge_coin": "Doge Coin", @@ -4887,7 +4902,6 @@ "material.gtceu.goethite": "Goethite", "material.gtceu.gold": "Gold", "material.gtceu.granite": "Granite", - "material.gtceu.granite_red": "Granite Red", "material.gtceu.granitic_mineral_sand": "Granitic Mineral Sand", "material.gtceu.graphene": "Graphene", "material.gtceu.graphite": "Graphite", @@ -4903,6 +4917,7 @@ "material.gtceu.hastelloy_c_276": "Hastelloy C-276", "material.gtceu.hastelloy_x": "Hastelloy X", "material.gtceu.heavy_fuel": "Heavy Fuel", + "material.gtceu.heavy_oil": "Heavy Oil", "material.gtceu.helium": "Helium", "material.gtceu.helium_3": "Helium 3", "material.gtceu.hematite": "Hematite", @@ -4965,6 +4980,7 @@ "material.gtceu.light_blue_dye": "Light Blue Dye", "material.gtceu.light_fuel": "Light Fuel", "material.gtceu.light_gray_dye": "Light Gray Dye", + "material.gtceu.light_oil": "Light Oil", "material.gtceu.lightly_hydro_cracked_gas": "Lightly Hydro-Cracked Gas", "material.gtceu.lightly_hydro_cracked_heavy_fuel": "Lightly Hydro-Cracked Heavy Fuel", "material.gtceu.lightly_hydro_cracked_light_fuel": "Lightly Hydro-Cracked Light Fuel", @@ -5060,9 +5076,6 @@ "material.gtceu.octane": "Octane", "material.gtceu.oganesson": "Oganesson", "material.gtceu.oil": "Oil", - "material.gtceu.oil_heavy": "Heavy Oil", - "material.gtceu.oil_light": "Light Oil", - "material.gtceu.oil_medium": "Raw Oil", "material.gtceu.oilsands": "Oilsands", "material.gtceu.olivine": "Olivine", "material.gtceu.opal": "Opal", @@ -5090,7 +5103,7 @@ "material.gtceu.platinum_group_sludge": "Platinum Group Sludge", "material.gtceu.platinum_raw": "Platinum Raw", "material.gtceu.platinum_sludge_residue": "Platinum Sludge Residue", - "material.gtceu.plutonium": "Plutonium", + "material.gtceu.plutonium_239": "Plutonium 239", "material.gtceu.plutonium_241": "Plutonium 241", "material.gtceu.pollucite": "Pollucite", "material.gtceu.polonium": "Polonium", @@ -5138,12 +5151,14 @@ "material.gtceu.raw_brine": "Raw Brine", "material.gtceu.raw_gasoline": "Raw Gasoline", "material.gtceu.raw_growth_medium": "Raw Growth Medium", + "material.gtceu.raw_oil": "Raw Oil", "material.gtceu.raw_rubber": "Raw Rubber", "material.gtceu.raw_styrene_butadiene_rubber": "Raw Styrene Butadiene Rubber", "material.gtceu.realgar": "Realgar", "material.gtceu.red_alloy": "Red Alloy", "material.gtceu.red_dye": "Red Dye", "material.gtceu.red_garnet": "Red Garnet", + "material.gtceu.red_granite": "Red Granite", "material.gtceu.red_steel": "Red Steel", "material.gtceu.redrock": "Redrock", "material.gtceu.redstone": "Redstone", @@ -5274,8 +5289,8 @@ "material.gtceu.tungstic_acid": "Tungstic Acid", "material.gtceu.ultimet": "Ultimet", "material.gtceu.uraninite": "Uraninite", - "material.gtceu.uranium": "Uranium", "material.gtceu.uranium_235": "Uranium 235", + "material.gtceu.uranium_238": "Uranium 238", "material.gtceu.uranium_hexafluoride": "Uranium Hexafluoride", "material.gtceu.uranium_rhodium_dinaquadide": "Uranium Rhodium Dinaquadide", "material.gtceu.uranium_triplatinum": "Uranium Triplatinum", @@ -5465,21 +5480,20 @@ "tagprefix.marble": "Marble %s Ore", "tagprefix.netherrack": "Nether %s Ore", "tagprefix.nugget": "%s Nugget", - "tagprefix.pipe_huge_fluid": "Huge %s Fluid Pipe", - "tagprefix.pipe_huge_item": "Huge %s Item Pipe", - "tagprefix.pipe_huge_restrictive": "Huge Restrictive %s Item Pipe", - "tagprefix.pipe_large_fluid": "Large %s Fluid Pipe", - "tagprefix.pipe_large_item": "Large %s Item Pipe", - "tagprefix.pipe_large_restrictive": "Large Restrictive %s Item Pipe", - "tagprefix.pipe_nonuple_fluid": "Nonuple %s Fluid Pipe", - "tagprefix.pipe_normal_fluid": "Normal %s Fluid Pipe", - "tagprefix.pipe_normal_item": "Normal %s Item Pipe", - "tagprefix.pipe_normal_restrictive": "Normal Restrictive %s Item Pipe", - "tagprefix.pipe_quadruple_fluid": "Quadruple %s Fluid Pipe", - "tagprefix.pipe_small_fluid": "Small %s Fluid Pipe", - "tagprefix.pipe_small_item": "Small %s Item Pipe", - "tagprefix.pipe_small_restrictive": "Small Restrictive %s Item Pipe", - "tagprefix.pipe_tiny_fluid": "Tiny %s Fluid Pipe", + "tagprefix.pipe_huge": "Huge %s Pipe", + "tagprefix.pipe_huge_restrictive": "Huge Restrictive %s Pipe", + "tagprefix.pipe_large": "Large %s Pipe", + "tagprefix.pipe_large_restrictive": "Large Restrictive %s Pipe", + "tagprefix.pipe_nonuple": "Nonuple %s Pipe", + "tagprefix.pipe_nonuple_restrictive": "Nonuple Restrictive %s Pipe", + "tagprefix.pipe_normal": "Normal %s Pipe", + "tagprefix.pipe_normal_restrictive": "Normal Restrictive %s Pipe", + "tagprefix.pipe_quadruple": "Quadruple %s Pipe", + "tagprefix.pipe_quadruple_restrictive": "Quadruple Restrictive %s Pipe", + "tagprefix.pipe_small": "Small %s Pipe", + "tagprefix.pipe_small_restrictive": "Small Restrictive %s Pipe", + "tagprefix.pipe_tiny": "Tiny %s Pipe", + "tagprefix.pipe_tiny_restrictive": "Tiny Restrictive %s Pipe", "tagprefix.planks": "%s Planks", "tagprefix.plate": "%s Plate", "tagprefix.polymer.dense_plate": "Dense %s Sheet", diff --git a/src/generated/resources/assets/gtceu/models/block/heavy_oil.json b/src/generated/resources/assets/gtceu/models/block/heavy_oil.json new file mode 100644 index 0000000000..b74b02b070 --- /dev/null +++ b/src/generated/resources/assets/gtceu/models/block/heavy_oil.json @@ -0,0 +1,5 @@ +{ + "textures": { + "particle": "gtceu:block/fluids/fluid.heavy_oil" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/gtceu/models/block/light_oil.json b/src/generated/resources/assets/gtceu/models/block/light_oil.json new file mode 100644 index 0000000000..a6ff95ff32 --- /dev/null +++ b/src/generated/resources/assets/gtceu/models/block/light_oil.json @@ -0,0 +1,5 @@ +{ + "textures": { + "particle": "gtceu:block/fluids/fluid.light_oil" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/gtceu/models/block/oil_heavy.json b/src/generated/resources/assets/gtceu/models/block/oil_heavy.json deleted file mode 100644 index 2e6953bb4a..0000000000 --- a/src/generated/resources/assets/gtceu/models/block/oil_heavy.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "textures": { - "particle": "gtceu:block/fluids/fluid.oil_heavy" - } -} \ No newline at end of file diff --git a/src/generated/resources/assets/gtceu/models/block/oil_light.json b/src/generated/resources/assets/gtceu/models/block/oil_light.json deleted file mode 100644 index 0cbd722a2b..0000000000 --- a/src/generated/resources/assets/gtceu/models/block/oil_light.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "textures": { - "particle": "gtceu:block/fluids/fluid.oil_light" - } -} \ No newline at end of file diff --git a/src/generated/resources/assets/gtceu/models/block/oil_medium.json b/src/generated/resources/assets/gtceu/models/block/oil_medium.json deleted file mode 100644 index 8f7f4602f7..0000000000 --- a/src/generated/resources/assets/gtceu/models/block/oil_medium.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "textures": { - "particle": "gtceu:block/fluids/fluid.oil_medium" - } -} \ No newline at end of file diff --git a/src/generated/resources/assets/gtceu/models/block/raw_oil.json b/src/generated/resources/assets/gtceu/models/block/raw_oil.json new file mode 100644 index 0000000000..7e11b4e6d6 --- /dev/null +++ b/src/generated/resources/assets/gtceu/models/block/raw_oil.json @@ -0,0 +1,5 @@ +{ + "textures": { + "particle": "gtceu:block/fluids/fluid.raw_oil" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/gtceu/models/item/advanced_nanomuscle_chestplate.json b/src/generated/resources/assets/gtceu/models/item/advanced_nanomuscle_chestplate.json new file mode 100644 index 0000000000..baa2533e81 --- /dev/null +++ b/src/generated/resources/assets/gtceu/models/item/advanced_nanomuscle_chestplate.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "gtceu:item/advanced_nanomuscle_chestplate" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/gtceu/models/item/avanced_nanomuscle_chestplate.json b/src/generated/resources/assets/gtceu/models/item/dielectric_laser_mirror.json similarity index 52% rename from src/generated/resources/assets/gtceu/models/item/avanced_nanomuscle_chestplate.json rename to src/generated/resources/assets/gtceu/models/item/dielectric_laser_mirror.json index eb85c6d7e3..32ac94cbee 100644 --- a/src/generated/resources/assets/gtceu/models/item/avanced_nanomuscle_chestplate.json +++ b/src/generated/resources/assets/gtceu/models/item/dielectric_laser_mirror.json @@ -1,6 +1,6 @@ { "parent": "minecraft:item/generated", "textures": { - "layer0": "gtceu:item/avanced_nanomuscle_chestplate" + "layer0": "gtceu:item/dielectric_laser_mirror" } } \ No newline at end of file diff --git a/src/generated/resources/data/ae2/tags/items/p2p_attunements/fluid_p2p_tunnel.json b/src/generated/resources/data/ae2/tags/items/p2p_attunements/fluid_p2p_tunnel.json index 3defc70d88..01f941ebf7 100644 --- a/src/generated/resources/data/ae2/tags/items/p2p_attunements/fluid_p2p_tunnel.json +++ b/src/generated/resources/data/ae2/tags/items/p2p_attunements/fluid_p2p_tunnel.json @@ -52,7 +52,7 @@ "gtceu:oxygen_plasma_bucket", "gtceu:palladium_bucket", "gtceu:platinum_bucket", - "gtceu:plutonium_bucket", + "gtceu:plutonium_239_bucket", "gtceu:plutonium_241_bucket", "gtceu:potassium_bucket", "gtceu:radon_bucket", @@ -67,7 +67,7 @@ "gtceu:titanium_bucket", "gtceu:tritium_bucket", "gtceu:tungsten_bucket", - "gtceu:uranium_bucket", + "gtceu:uranium_238_bucket", "gtceu:uranium_235_bucket", "gtceu:vanadium_bucket", "gtceu:xenon_bucket", @@ -312,9 +312,9 @@ "gtceu:raw_growth_medium_bucket", "gtceu:sterilized_growth_medium_bucket", "gtceu:oil_bucket", - "gtceu:oil_heavy_bucket", - "gtceu:oil_medium_bucket", - "gtceu:oil_light_bucket", + "gtceu:heavy_oil_bucket", + "gtceu:raw_oil_bucket", + "gtceu:light_oil_bucket", "gtceu:natural_gas_bucket", "gtceu:bacteria_bucket", "gtceu:bacterial_sludge_bucket", diff --git a/src/generated/resources/data/forge/tags/blocks/mineable/wire_cutter.json b/src/generated/resources/data/forge/tags/blocks/mineable/wire_cutter.json index 0bd103754d..048db8a64c 100644 --- a/src/generated/resources/data/forge/tags/blocks/mineable/wire_cutter.json +++ b/src/generated/resources/data/forge/tags/blocks/mineable/wire_cutter.json @@ -1,6 +1,7 @@ { "values": [ - "gtceu:normal_laser_pipe", - "gtceu:normal_optical_pipe" + "gtceu:laser_pipe", + "gtceu:laser_reflector_pipe", + "gtceu:optical_fiber_cable" ] } \ No newline at end of file diff --git a/src/generated/resources/data/forge/tags/items/armors/chestplates.json b/src/generated/resources/data/forge/tags/items/armors/chestplates.json index aad91e93fd..9435ea1359 100644 --- a/src/generated/resources/data/forge/tags/items/armors/chestplates.json +++ b/src/generated/resources/data/forge/tags/items/armors/chestplates.json @@ -7,7 +7,7 @@ "gtceu:liquid_fuel_jetpack", "gtceu:electric_jetpack", "gtceu:advanced_electric_jetpack", - "gtceu:avanced_nanomuscle_chestplate", + "gtceu:advanced_nanomuscle_chestplate", "gtceu:advanced_quarktech_chestplate" ] } \ No newline at end of file diff --git a/src/generated/resources/data/gtceu/loot_tables/blocks/normal_laser_pipe.json b/src/generated/resources/data/gtceu/loot_tables/blocks/laser_pipe.json similarity index 75% rename from src/generated/resources/data/gtceu/loot_tables/blocks/normal_laser_pipe.json rename to src/generated/resources/data/gtceu/loot_tables/blocks/laser_pipe.json index d14a70d62b..a81c438ddb 100644 --- a/src/generated/resources/data/gtceu/loot_tables/blocks/normal_laser_pipe.json +++ b/src/generated/resources/data/gtceu/loot_tables/blocks/laser_pipe.json @@ -11,11 +11,11 @@ "entries": [ { "type": "minecraft:item", - "name": "gtceu:normal_laser_pipe" + "name": "gtceu:laser_pipe" } ], "rolls": 1.0 } ], - "random_sequence": "gtceu:blocks/normal_laser_pipe" + "random_sequence": "gtceu:blocks/laser_pipe" } \ No newline at end of file diff --git a/src/generated/resources/data/gtceu/loot_tables/blocks/laser_reflector_pipe.json b/src/generated/resources/data/gtceu/loot_tables/blocks/laser_reflector_pipe.json new file mode 100644 index 0000000000..d25afea720 --- /dev/null +++ b/src/generated/resources/data/gtceu/loot_tables/blocks/laser_reflector_pipe.json @@ -0,0 +1,21 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "bonus_rolls": 0.0, + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "gtceu:laser_reflector_pipe" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "gtceu:blocks/laser_reflector_pipe" +} \ No newline at end of file diff --git a/src/generated/resources/data/gtceu/loot_tables/blocks/normal_optical_pipe.json b/src/generated/resources/data/gtceu/loot_tables/blocks/optical_fiber_cable.json similarity index 74% rename from src/generated/resources/data/gtceu/loot_tables/blocks/normal_optical_pipe.json rename to src/generated/resources/data/gtceu/loot_tables/blocks/optical_fiber_cable.json index 40b990e301..7f919761db 100644 --- a/src/generated/resources/data/gtceu/loot_tables/blocks/normal_optical_pipe.json +++ b/src/generated/resources/data/gtceu/loot_tables/blocks/optical_fiber_cable.json @@ -11,11 +11,11 @@ "entries": [ { "type": "minecraft:item", - "name": "gtceu:normal_optical_pipe" + "name": "gtceu:optical_fiber_cable" } ], "rolls": 1.0 } ], - "random_sequence": "gtceu:blocks/normal_optical_pipe" + "random_sequence": "gtceu:blocks/optical_fiber_cable" } \ No newline at end of file diff --git a/src/generated/resources/data/gtceu/tags/items/ppe_armor.json b/src/generated/resources/data/gtceu/tags/items/ppe_armor.json index ec5f73fa99..bde19d498c 100644 --- a/src/generated/resources/data/gtceu/tags/items/ppe_armor.json +++ b/src/generated/resources/data/gtceu/tags/items/ppe_armor.json @@ -10,7 +10,7 @@ "gtceu:quarktech_leggings", "gtceu:quarktech_boots", "gtceu:quarktech_helmet", - "gtceu:avanced_nanomuscle_chestplate", + "gtceu:advanced_nanomuscle_chestplate", "gtceu:advanced_quarktech_chestplate" ] } \ No newline at end of file diff --git a/src/generated/resources/data/gtceu/worldgen/configured_feature/raw_oil_sprout.json b/src/generated/resources/data/gtceu/worldgen/configured_feature/raw_oil_sprout.json index 9f7c63b09e..a274658f90 100644 --- a/src/generated/resources/data/gtceu/worldgen/configured_feature/raw_oil_sprout.json +++ b/src/generated/resources/data/gtceu/worldgen/configured_feature/raw_oil_sprout.json @@ -1,7 +1,7 @@ { "type": "gtceu:fluid_sprout", "config": { - "fluid": "gtceu:oil_medium", + "fluid": "gtceu:raw_oil", "size": { "type": "minecraft:uniform", "value": { diff --git a/src/generated/resources/data/minecraft/tags/blocks/replaceable.json b/src/generated/resources/data/minecraft/tags/blocks/replaceable.json index 51b1c7c9f3..be225f678b 100644 --- a/src/generated/resources/data/minecraft/tags/blocks/replaceable.json +++ b/src/generated/resources/data/minecraft/tags/blocks/replaceable.json @@ -1,9 +1,9 @@ { "values": [ "gtceu:oil", - "gtceu:oil_light", - "gtceu:oil_heavy", - "gtceu:oil_medium", + "gtceu:light_oil", + "gtceu:heavy_oil", + "gtceu:raw_oil", "gtceu:natural_gas" ] } \ No newline at end of file diff --git a/src/main/java/com/gregtechceu/gtceu/GTCEu.java b/src/main/java/com/gregtechceu/gtceu/GTCEu.java index 09fd3c85c3..694a87a3bb 100644 --- a/src/main/java/com/gregtechceu/gtceu/GTCEu.java +++ b/src/main/java/com/gregtechceu/gtceu/GTCEu.java @@ -145,11 +145,11 @@ public static boolean isJEILoaded() { } public static boolean isREILoaded() { - return isModLoaded(GTValues.MODID_REI) && !(isClientSide() || REIRuntime.getInstance().isOverlayVisible()); + return isModLoaded(GTValues.MODID_REI) && (!isClientSide() || REIRuntime.getInstance().isOverlayVisible()); } public static boolean isEMILoaded() { - return isModLoaded(GTValues.MODID_EMI) && !(isClientSide() || EmiConfig.enabled); + return isModLoaded(GTValues.MODID_EMI) && (!isClientSide() || EmiConfig.enabled); } public static boolean isKubeJSLoaded() { diff --git a/src/main/java/com/gregtechceu/gtceu/api/GTCEuAPI.java b/src/main/java/com/gregtechceu/gtceu/api/GTCEuAPI.java index e9bdd6edac..45738ace2a 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/GTCEuAPI.java +++ b/src/main/java/com/gregtechceu/gtceu/api/GTCEuAPI.java @@ -26,6 +26,8 @@ public class GTCEuAPI { + public static final int GT_DATA_VERSION = 2; + /** Will always be available */ public static GTCEu instance; /** Will be available at the Construction stage */ diff --git a/src/main/java/com/gregtechceu/gtceu/api/addon/IGTAddon.java b/src/main/java/com/gregtechceu/gtceu/api/addon/IGTAddon.java index 7aa4eb4571..e810f01d1e 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/addon/IGTAddon.java +++ b/src/main/java/com/gregtechceu/gtceu/api/addon/IGTAddon.java @@ -6,9 +6,15 @@ import com.gregtechceu.gtceu.api.registry.registrate.GTRegistrate; import com.gregtechceu.gtceu.common.data.GTOres; +import net.minecraft.core.Registry; +import net.minecraft.data.loot.packs.VanillaBlockLoot; import net.minecraft.data.recipes.FinishedRecipe; import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagLoader; +import net.minecraft.world.level.storage.loot.LootTable; +import java.util.List; +import java.util.Map; import java.util.function.Consumer; @SuppressWarnings("unused") @@ -85,6 +91,22 @@ default void addRecipes(Consumer provider) {} default void removeRecipes(Consumer consumer) {} + /** + * Use this to load client data like models and block states dynamically. + */ + default void loadDynamicResources() {} + + /** + * Use this to load loot tables dynamically. + */ + default void loadDynamicLoot(Map lootTables, VanillaBlockLoot vanillaBlockLoot) {} + + /** + * Use this to load tags dynamically. + */ + default void loadDynamicTags(Map> tagMap, + Registry registry) {} + /** * Use {@link GTOres#create(ResourceLocation, Consumer)} to register the veins. */ diff --git a/src/main/java/com/gregtechceu/gtceu/api/block/MaterialBlock.java b/src/main/java/com/gregtechceu/gtceu/api/block/MaterialBlock.java index be17a8e898..06d5c18c06 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/block/MaterialBlock.java +++ b/src/main/java/com/gregtechceu/gtceu/api/block/MaterialBlock.java @@ -1,10 +1,11 @@ package com.gregtechceu.gtceu.api.block; import com.gregtechceu.gtceu.GTCEu; -import com.gregtechceu.gtceu.api.blockentity.PipeBlockEntity; import com.gregtechceu.gtceu.api.data.chemical.material.Material; import com.gregtechceu.gtceu.api.data.tag.TagPrefix; -import com.gregtechceu.gtceu.api.item.PipeBlockItem; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.block.PipeBlock; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.block.PipeBlockItem; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; import com.gregtechceu.gtceu.api.item.tool.GTToolType; import com.gregtechceu.gtceu.api.item.tool.ToolHelper; import com.gregtechceu.gtceu.client.renderer.block.MaterialBlockRenderer; @@ -192,7 +193,7 @@ public InteractionResult use(BlockState state, Level level, BlockPos pos, Player continue; } BlockEntity te = level.getBlockEntity(blockPos); - if (te instanceof PipeBlockEntity pbe && pbe.getFrameMaterial() != null) { + if (te instanceof PipeBlockEntity pbe && pbe.getFrameMaterial() != null) { blockPos.move(Direction.UP); continue; } @@ -201,7 +202,7 @@ public InteractionResult use(BlockState state, Level level, BlockPos pos, Player if (!player.isCreative()) stack.shrink(1); return InteractionResult.SUCCESS; - } else if (te instanceof PipeBlockEntity pbe && pbe.getFrameMaterial() == null) { + } else if (te instanceof PipeBlockEntity pbe && pbe.getFrameMaterial() == null) { pbe.setFrameMaterial(frameBlock.material); if (!player.isCreative()) @@ -228,7 +229,7 @@ public static MaterialBlock getFrameboxFromItem(ItemStack stack) { public boolean removeFrame(Level level, BlockPos pos, Player player, ItemStack stack) { BlockEntity te = level.getBlockEntity(pos); - if (te instanceof PipeBlockEntity pipeTile) { + if (te instanceof PipeBlockEntity pipeTile) { Material mat = pipeTile.getFrameMaterial(); if (mat != null) { pipeTile.setFrameMaterial(null); @@ -251,17 +252,17 @@ public boolean canBeReplaced(BlockState state, BlockPlaceContext useContext) { public boolean replaceWithFramedPipe(Level level, BlockPos pos, BlockState state, Player player, ItemStack stackInHand, BlockHitResult hit) { - PipeBlock pipeBlock = (PipeBlock) ((PipeBlockItem) stackInHand.getItem()).getBlock(); - if (pipeBlock.pipeType.getThickness() < 1) { + PipeBlock pipeBlock = ((PipeBlockItem) stackInHand.getItem()).getBlock(); + if (pipeBlock.getStructure().getRenderThickness() < 1) { PipeBlockItem itemBlock = (PipeBlockItem) stackInHand.getItem(); BlockState pipeState = pipeBlock.defaultBlockState(); BlockPlaceContext context = new BlockPlaceContext(level, player, InteractionHand.MAIN_HAND, stackInHand, hit); BlockState original = level.getBlockState(context.getClickedPos()); itemBlock.placeBlock(context, pipeState); - var pipeTile = pipeBlock.getPipeTile(level, pos); - if (pipeTile instanceof PipeBlockEntity pipeBlockEntity) { - pipeBlockEntity.setFrameMaterial(material); + var pipeTile = pipeBlock.getBlockEntity(level, pos); + if (pipeTile != null) { + pipeTile.setFrameMaterial(material); } else { // reset the state if we didn't place correctly level.setBlockAndUpdate(context.getClickedPos(), original); diff --git a/src/main/java/com/gregtechceu/gtceu/api/block/MaterialPipeBlock.java b/src/main/java/com/gregtechceu/gtceu/api/block/MaterialPipeBlock.java deleted file mode 100644 index 034cf09bbe..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/api/block/MaterialPipeBlock.java +++ /dev/null @@ -1,120 +0,0 @@ -package com.gregtechceu.gtceu.api.block; - -import com.gregtechceu.gtceu.api.blockentity.PipeBlockEntity; -import com.gregtechceu.gtceu.api.data.chemical.material.Material; -import com.gregtechceu.gtceu.api.pipenet.*; -import com.gregtechceu.gtceu.client.model.PipeModel; -import com.gregtechceu.gtceu.client.renderer.block.PipeBlockRenderer; - -import net.minecraft.MethodsReturnNonnullByDefault; -import net.minecraft.client.color.block.BlockColor; -import net.minecraft.core.BlockPos; -import net.minecraft.network.chat.MutableComponent; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.BlockAndTintGetter; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; - -import org.jetbrains.annotations.Nullable; - -import javax.annotation.ParametersAreNonnullByDefault; - -/** - * @author KilaBash - * @date 2023/2/28 - * @implNote MaterialPipeBlock - */ -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault -public abstract class MaterialPipeBlock< - PipeType extends Enum & IPipeType & IMaterialPipeType, NodeDataType, - WorldPipeNetType extends LevelPipeNet>> - extends PipeBlock { - - public final Material material; - public final PipeBlockRenderer renderer; - public final PipeModel model; - - public MaterialPipeBlock(Properties properties, PipeType pipeType, Material material) { - super(properties, pipeType); - this.material = material; - this.model = createPipeModel(); - this.renderer = new PipeBlockRenderer(this.model); - } - - @OnlyIn(Dist.CLIENT) - public static BlockColor tintedColor() { - return (blockState, level, blockPos, index) -> { - if (blockState.getBlock() instanceof MaterialPipeBlock block) { - if (blockPos != null && level != null && - level.getBlockEntity(blockPos) instanceof PipeBlockEntity pipe) { - if (pipe.getFrameMaterial() != null) { - if (index == 3) { - return pipe.getFrameMaterial().getMaterialRGB(); - } else if (index == 4) { - return pipe.getFrameMaterial().getMaterialSecondaryRGB(); - } - } - if (pipe.isPainted()) { - return pipe.getRealColor(); - } - } - return block.tinted(blockState, level, blockPos, index); - } - return -1; - }; - } - - public int tinted(BlockState blockState, @Nullable BlockAndTintGetter blockAndTintGetter, - @Nullable BlockPos blockPos, int index) { - return index == 0 || index == 1 ? material.getMaterialRGB() : -1; - } - - @Override - protected PipeModel getPipeModel() { - return model; - } - - @Override - public final NodeDataType createRawData(BlockState pState, @Nullable ItemStack pStack) { - return createMaterialData(); - } - - @Override - public NodeDataType createProperties(IPipeNode pipeTile) { - PipeType pipeType = pipeTile.getPipeType(); - Material material = ((MaterialPipeBlock) pipeTile - .getPipeBlock()).material; - if (pipeType == null || material == null) { - return getFallbackType(); - } - return createProperties(pipeType, material); - } - - protected abstract NodeDataType createProperties(PipeType pipeType, Material material); - - @Override - public @Nullable PipeBlockRenderer getRenderer(BlockState state) { - return renderer; - } - - @Override - public final NodeDataType getFallbackType() { - return createMaterialData(); - } - - protected abstract NodeDataType createMaterialData(); - - protected abstract PipeModel createPipeModel(); - - @Override - public String getDescriptionId() { - return pipeType.getTagPrefix().getUnlocalizedName(material); - } - - @Override - public MutableComponent getName() { - return pipeType.getTagPrefix().getLocalizedName(material); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/api/block/MetaMachineBlock.java b/src/main/java/com/gregtechceu/gtceu/api/block/MetaMachineBlock.java index 3c209bc0ed..e42e23b82a 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/block/MetaMachineBlock.java +++ b/src/main/java/com/gregtechceu/gtceu/api/block/MetaMachineBlock.java @@ -12,7 +12,6 @@ import com.gregtechceu.gtceu.api.machine.feature.*; import com.gregtechceu.gtceu.common.data.GTItems; import com.gregtechceu.gtceu.common.machine.owner.IMachineOwner; -import com.gregtechceu.gtceu.utils.GTUtil; import com.lowdragmc.lowdraglib.client.renderer.IRenderer; import com.lowdragmc.lowdraglib.utils.LocalizationUtils; @@ -262,9 +261,7 @@ public void onRemove(BlockState pState, Level pLevel, BlockPos pPos, BlockState machineLife.onMachineRemoved(); } if (machine != null) { - for (Direction direction : GTUtil.DIRECTIONS) { - machine.getCoverContainer().removeCover(direction, null); - } + machine.getCoverContainer().dropAllCovers(); } pLevel.updateNeighbourForOutputSignal(pPos, this); @@ -355,6 +352,7 @@ public void neighborChanged(BlockState state, Level level, BlockPos pos, Block b var machine = getMachine(level, pos); if (machine != null) { machine.onNeighborChanged(block, fromPos, isMoving); + machine.getCoverContainer().updateInputRedstoneSignals(); } super.neighborChanged(state, level, pos, block, fromPos, isMoving); } diff --git a/src/main/java/com/gregtechceu/gtceu/api/block/PipeBlock.java b/src/main/java/com/gregtechceu/gtceu/api/block/PipeBlock.java deleted file mode 100644 index fac438c52e..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/api/block/PipeBlock.java +++ /dev/null @@ -1,480 +0,0 @@ -package com.gregtechceu.gtceu.api.block; - -import com.gregtechceu.gtceu.GTCEu; -import com.gregtechceu.gtceu.api.blockentity.PipeBlockEntity; -import com.gregtechceu.gtceu.api.capability.ICoverable; -import com.gregtechceu.gtceu.api.cover.CoverBehavior; -import com.gregtechceu.gtceu.api.data.tag.TagPrefix; -import com.gregtechceu.gtceu.api.item.PipeBlockItem; -import com.gregtechceu.gtceu.api.item.component.IInteractionItem; -import com.gregtechceu.gtceu.api.item.tool.GTToolType; -import com.gregtechceu.gtceu.api.item.tool.ToolHelper; -import com.gregtechceu.gtceu.api.pipenet.IPipeNode; -import com.gregtechceu.gtceu.api.pipenet.IPipeType; -import com.gregtechceu.gtceu.api.pipenet.LevelPipeNet; -import com.gregtechceu.gtceu.api.pipenet.PipeNet; -import com.gregtechceu.gtceu.client.model.PipeModel; -import com.gregtechceu.gtceu.client.renderer.block.PipeBlockRenderer; -import com.gregtechceu.gtceu.common.data.GTItems; -import com.gregtechceu.gtceu.common.data.GTMaterialBlocks; -import com.gregtechceu.gtceu.common.item.CoverPlaceBehavior; -import com.gregtechceu.gtceu.config.ConfigHolder; -import com.gregtechceu.gtceu.data.recipe.VanillaRecipeHelper; -import com.gregtechceu.gtceu.utils.GTUtil; - -import com.lowdragmc.lowdraglib.client.renderer.IBlockRendererProvider; - -import net.minecraft.MethodsReturnNonnullByDefault; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.sounds.SoundSource; -import net.minecraft.util.RandomSource; -import net.minecraft.world.InteractionHand; -import net.minecraft.world.InteractionResult; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.BlockItem; -import net.minecraft.world.item.DyeColor; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.context.UseOnContext; -import net.minecraft.world.level.*; -import net.minecraft.world.level.block.*; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.entity.BlockEntityTicker; -import net.minecraft.world.level.block.entity.BlockEntityType; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.StateDefinition; -import net.minecraft.world.level.block.state.properties.BlockStateProperties; -import net.minecraft.world.level.material.FluidState; -import net.minecraft.world.level.material.Fluids; -import net.minecraft.world.level.storage.loot.LootParams; -import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets; -import net.minecraft.world.level.storage.loot.parameters.LootContextParams; -import net.minecraft.world.phys.BlockHitResult; -import net.minecraft.world.phys.Vec3; -import net.minecraft.world.phys.shapes.CollisionContext; -import net.minecraft.world.phys.shapes.EntityCollisionContext; -import net.minecraft.world.phys.shapes.Shapes; -import net.minecraft.world.phys.shapes.VoxelShape; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - -import javax.annotation.ParametersAreNonnullByDefault; - -/** - * @author KilaBash - * @date 2023/2/28 - * @implNote PipeBlock - */ -@SuppressWarnings("deprecation") -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault -public abstract class PipeBlock & IPipeType, NodeDataType, - WorldPipeNetType extends LevelPipeNet>> extends AppearanceBlock - implements EntityBlock, IBlockRendererProvider, SimpleWaterloggedBlock { - - public final PipeType pipeType; - - public PipeBlock(Properties properties, PipeType pipeType) { - super(properties); - this.pipeType = pipeType; - registerDefaultState(defaultBlockState().setValue(BlockProperties.SERVER_TICK, false) - .setValue(BlockStateProperties.WATERLOGGED, false)); - } - - @Override - protected void createBlockStateDefinition(StateDefinition.Builder builder) { - super.createBlockStateDefinition(builder.add(BlockProperties.SERVER_TICK, BlockStateProperties.WATERLOGGED)); - } - - @Override - public FluidState getFluidState(BlockState state) { - return state.getValue(BlockStateProperties.WATERLOGGED) ? Fluids.WATER.getSource(false) : - super.getFluidState(state); - } - - @Override - public BlockState updateShape(BlockState state, Direction direction, BlockState neighborState, LevelAccessor level, - BlockPos pos, BlockPos neighborPos) { - if (state.getValue(BlockStateProperties.WATERLOGGED)) { - level.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(level)); - } - return super.updateShape(state, direction, neighborState, level, pos, neighborPos); - } - - @Override - public final PipeBlockEntity newBlockEntity(BlockPos pos, BlockState state) { - return getBlockEntityType().create(pos, state); - } - - public abstract WorldPipeNetType getWorldPipeNet(ServerLevel level); - - public abstract BlockEntityType> getBlockEntityType(); - - /** - * Add data via placement. - */ - public abstract NodeDataType createRawData(BlockState pState, @Nullable ItemStack pStack); - - public NodeDataType createProperties(BlockState state, @Nullable ItemStack stack) { - return pipeType.modifyProperties(createRawData(state, stack)); - } - - public abstract NodeDataType createProperties(IPipeNode pipeTile); - - /** - * Sometimes some people - */ - public abstract NodeDataType getFallbackType(); - - @Nullable - @Override - public abstract PipeBlockRenderer getRenderer(BlockState state); - - protected abstract PipeModel getPipeModel(); - - public void updateActiveNodeStatus(@NotNull Level worldIn, BlockPos pos, - IPipeNode pipeTile) { - if (worldIn.isClientSide) return; - - PipeNet pipeNet = getWorldPipeNet((ServerLevel) worldIn).getNetFromPos(pos); - if (pipeNet != null && pipeTile != null) { - int activeConnections = pipeTile.getConnections(); // remove blocked connections - boolean isActiveNodeNow = activeConnections != 0; - boolean modeChanged = pipeNet.markNodeAsActive(pos, isActiveNodeNow); - if (modeChanged) { - onActiveModeChange(worldIn, pos, isActiveNodeNow, false); - } - } - } - - @Override - public void onNeighborChange(BlockState state, LevelReader level, BlockPos pos, BlockPos neighbor) { - if (level.isClientSide()) return; - IPipeNode pipeTile = getPipeTile(level, pos); - - if (pipeTile != null) { - Direction facing = GTUtil.getFacingToNeighbor(pos, neighbor); - if (facing == null) return; - CoverBehavior cover = pipeTile.getCoverContainer().getCoverAtSide(facing); - if (!ConfigHolder.INSTANCE.machines.gt6StylePipesCables) { - boolean open = pipeTile.isConnected(facing); - boolean canConnect = cover != null || - canConnect(pipeTile, facing); - if (!open && canConnect) - pipeTile.setConnection(facing, true, false); - if (open && !canConnect) - pipeTile.setConnection(facing, false, false); - updateActiveNodeStatus(pipeTile.getPipeLevel(), pos, pipeTile); - } - PipeNet net = pipeTile.getPipeNet(); - if (net != null) { - pipeTile.getPipeNet().onNeighbourUpdate(neighbor); - } - if (cover != null) cover.onNeighborChanged(state.getBlock(), pos, false); - } - } - - /** - * Get pipe nodes with the same pipe type. - */ - @Nullable - @SuppressWarnings("unchecked") - public IPipeNode getPipeTile(BlockGetter level, BlockPos pos) { - if (level.getBlockEntity(pos) instanceof IPipeNode pipeTile && - pipeTile.getPipeType().type().equals(pipeType.type())) { - return (IPipeNode) pipeTile; - } - return null; - } - - /** - * Can be used to update tile entity to tickable when node becomes active - * usable for fluid pipes, as example - */ - protected void onActiveModeChange(Level world, BlockPos pos, boolean isActiveNow, boolean isInitialChange) {} - - public boolean canConnect(IPipeNode selfTile, Direction facing) { - if (selfTile.getPipeLevel().getBlockState(selfTile.getPipePos().relative(facing)).getBlock() == Blocks.AIR) - return false; - CoverBehavior cover = selfTile.getCoverContainer().getCoverAtSide(facing); - if (cover != null && !cover.canPipePassThrough()) { - return false; - } - BlockEntity other = selfTile.getNeighbor(facing); - if (other instanceof IPipeNode node) { - cover = node.getCoverContainer().getCoverAtSide(facing.getOpposite()); - if (cover != null && !cover.canPipePassThrough()) - return false; - return canPipesConnect(selfTile, facing, (IPipeNode) other); - } - return canPipeConnectToBlock(selfTile, facing, other); - } - - public abstract boolean canPipesConnect(IPipeNode selfTile, Direction side, - IPipeNode sideTile); - - public abstract boolean canPipeConnectToBlock(IPipeNode selfTile, Direction side, - @Nullable BlockEntity tile); - - @Override - public void setPlacedBy(Level level, BlockPos pos, BlockState state, @Nullable LivingEntity placer, - ItemStack stack) { - super.setPlacedBy(level, pos, state, placer, stack); - IPipeNode pipeTile = getPipeTile(level, pos); - if (pipeTile != null) { - // Color pipes/cables on place if holding spray can in off-hand - if (placer instanceof Player player) { - ItemStack offhand = placer.getOffhandItem(); - for (int i = 0; i < DyeColor.values().length; i++) { - if (offhand.is(GTItems.SPRAY_CAN_DYES[i].get())) { - ((IInteractionItem) GTItems.SPRAY_CAN_DYES[i].get().getComponents().get(0)) - .useOn(new UseOnContext(player, InteractionHand.OFF_HAND, - new BlockHitResult(Vec3.ZERO, player.getDirection(), pos, false))); - break; - } - } - } - } - } - - @Override - public void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean isMoving) { - level.scheduleTick(pos, this, 1); - } - - @Override - public void neighborChanged(BlockState state, Level level, BlockPos pos, Block block, BlockPos fromPos, - boolean isMoving) { - if (level.isClientSide) return; - IPipeNode pipeTile = getPipeTile(level, pos); - if (pipeTile != null) { - Direction facing = GTUtil.getFacingToNeighbor(pos, fromPos); - if (facing == null) return; - if (!ConfigHolder.INSTANCE.machines.gt6StylePipesCables) { - boolean open = pipeTile.isConnected(facing); - boolean canConnect = pipeTile.getCoverContainer().getCoverAtSide(facing) != null || - this.canConnect(pipeTile, facing); - if (!open && canConnect && state.getBlock() != block) - pipeTile.setConnection(facing, true, false); - if (open && !canConnect) - pipeTile.setConnection(facing, false, false); - updateActiveNodeStatus(level, pos, pipeTile); - } - } - } - - @Override - public void onRemove(BlockState pState, Level pLevel, BlockPos pPos, BlockState pNewState, boolean pIsMoving) { - if (pState.hasBlockEntity() && !pState.is(pNewState.getBlock())) { - pLevel.removeBlockEntity(pPos); - if (pLevel instanceof ServerLevel serverLevel) { - getWorldPipeNet(serverLevel).removeNode(pPos); - } - } - } - - @Override - public void destroy(LevelAccessor level, BlockPos pos, BlockState state) { - IPipeNode pipeTile = getPipeTile(level, pos); - if (pipeTile != null) { - pipeTile.getCoverContainer().dropAllCovers(); - } - super.destroy(level, pos, state); - if (level instanceof ServerLevel serverLevel) { - getWorldPipeNet(serverLevel).removeNode(pos); - } - } - - @Override - public void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { - IPipeNode pipeTile = getPipeTile(level, pos); - if (pipeTile != null) { - int activeConnections = pipeTile.getConnections(); - boolean isActiveNode = activeConnections != 0; - getWorldPipeNet(level).addNode(pos, createRawData(state, null), 0, activeConnections, isActiveNode); - onActiveModeChange(level, pos, isActiveNode, true); - } - } - - @Override - public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, - BlockHitResult hit) { - ItemStack itemStack = player.getItemInHand(hand); - BlockEntity entity = level.getBlockEntity(pos); - - PipeBlockEntity pipeBlockEntity = null; - if (entity instanceof PipeBlockEntity pbe) { - pipeBlockEntity = pbe; - } - if (pipeBlockEntity == null) { - return InteractionResult.FAIL; - } - - if (pipeBlockEntity.getFrameMaterial() == null && pipeType.getThickness() < 1) { - var frameBlock = MaterialBlock.getFrameboxFromItem(itemStack); - if (frameBlock != null) { - pipeBlockEntity.setFrameMaterial(frameBlock.material); - if (!player.isCreative()) itemStack.shrink(1); - SoundType type = VanillaRecipeHelper.isMaterialWood(frameBlock.material) ? SoundType.WOOD : - SoundType.METAL; - level.playSound(player, pos, - type.getPlaceSound(), SoundSource.BLOCKS, - (type.getVolume() + 1.0F) / 2.0F, type.getPitch() * 0.8F); - player.swing(hand); - return InteractionResult.SUCCESS; - } - } - - if (itemStack.getItem() instanceof PipeBlockItem itemPipe) { - BlockPos offsetPos = pos.offset(hit.getDirection().getNormal()); - BlockState stateAtSide = level.getBlockState(offsetPos); - if (stateAtSide.getBlock() instanceof MaterialBlock matBlock && matBlock.tagPrefix == TagPrefix.frameGt) { - if (itemPipe.getBlock().pipeType == pipeType) { - boolean wasPlaced = matBlock.replaceWithFramedPipe(level, offsetPos, stateAtSide, player, itemStack, - hit); - if (wasPlaced) { - pipeBlockEntity.setConnection(hit.getDirection(), true, false); - } - return wasPlaced ? InteractionResult.CONSUME : InteractionResult.FAIL; - } - } - } - - Set types = ToolHelper.getToolTypes(itemStack); - if ((!types.isEmpty() && ToolHelper.canUse(itemStack)) || (types.isEmpty() && player.isShiftKeyDown())) { - var result = pipeBlockEntity.onToolClick(types, itemStack, new UseOnContext(player, hand, hit)); - if (result.getSecond() == InteractionResult.CONSUME && player instanceof ServerPlayer serverPlayer) { - ToolHelper.playToolSound(result.getFirst(), serverPlayer); - - if (!serverPlayer.isCreative()) { - ToolHelper.damageItem(itemStack, serverPlayer, 1); - } - } - return result.getSecond(); - } - return InteractionResult.PASS; - } - - @Override - public void entityInside(BlockState state, Level level, BlockPos pos, Entity entity) { - var pipeNode = getPipeTile(level, pos); - if (pipeNode == null) { - GTCEu.LOGGER.error("Pipe was null"); - return; - } - if (pipeNode.getFrameMaterial() != null) { - BlockState frameState = GTMaterialBlocks.MATERIAL_BLOCKS.get(TagPrefix.frameGt, pipeNode.getFrameMaterial()) - .getDefaultState(); - frameState.getBlock().entityInside(frameState, level, pos, entity); - } - super.entityInside(state, level, pos, entity); - } - - @Override - public boolean isCollisionShapeFullBlock(BlockState state, BlockGetter level, BlockPos pos) { - var pipeNode = getPipeTile(level, pos); - if (pipeNode != null && pipeNode.getFrameMaterial() != null) { - return false; - } - return false; - } - - @Override - public VoxelShape getCollisionShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) { - var pipeNode = getPipeTile(level, pos); - if (pipeNode != null && pipeNode.getFrameMaterial() != null) { - return MaterialBlock.FRAME_COLLISION_BOX; - } - return super.getCollisionShape(state, level, pos, context); - } - - @Override - public VoxelShape getShape(BlockState pState, BlockGetter pLevel, BlockPos pPos, CollisionContext context) { - var pipeNode = getPipeTile(pLevel, pPos); - var connections = 0; - if (pipeNode != null) { - if (pipeNode.getFrameMaterial() != null) { - return Shapes.block(); - } - connections = pipeNode.getVisualConnections(); - VoxelShape shape = getPipeModel().getShapes(connections); - shape = Shapes.or(shape, pipeNode.getCoverContainer().addCoverCollisionBoundingBox()); - - if (context instanceof EntityCollisionContext entityCtx && entityCtx.getEntity() instanceof Player player) { - var coverable = pipeNode.getCoverContainer(); - var held = player.getMainHandItem(); - Set types = Set.of(GTToolType.WIRE_CUTTER, GTToolType.WRENCH); - BlockEntity tile = pLevel.getBlockEntity(pPos); - if (tile instanceof PipeBlockEntity pipeTile) { - types = Set.of(pipeTile.getPipeTuneTool()); - } - - if ((player.isShiftKeyDown() && held.isEmpty() && coverable.hasAnyCover()) || - types.stream().anyMatch(type -> type.itemTags.stream().anyMatch(held::is)) || - CoverPlaceBehavior.isCoverBehaviorItem(held, coverable::hasAnyCover, - coverDef -> ICoverable.canPlaceCover(coverDef, coverable)) || - (held.getItem() instanceof BlockItem blockItem && - blockItem.getBlock() instanceof PipeBlock pipeBlock && - pipeBlock.pipeType.type().equals(pipeType.type()))) { - return Shapes.block(); - } - } - return shape; - } - return getPipeModel().getShapes(connections); - } - - @Nullable - @Override - public BlockEntityTicker getTicker(Level level, BlockState state, - BlockEntityType blockEntityType) { - if (blockEntityType == getBlockEntityType()) { - if (!level.isClientSide && state.getValue(BlockProperties.SERVER_TICK)) { - return (pLevel, pPos, pState, pTile) -> { - if (pTile instanceof IPipeNode pipeNode) { - pipeNode.serverTick(); - } - }; - } - } - return null; - } - - @Override - public BlockState getBlockAppearance(BlockState state, BlockAndTintGetter level, BlockPos pos, Direction side, - BlockState sourceState, BlockPos sourcePos) { - var pipe = getPipeTile(level, pos); - if (pipe != null) { - var appearance = pipe.getCoverContainer().getBlockAppearance(state, level, pos, side, sourceState, - sourcePos); - if (appearance != null) return appearance; - } - return super.getBlockAppearance(state, level, pos, side, sourceState, sourcePos); - } - - @Override - public List getDrops(BlockState state, LootParams.Builder builder) { - var context = builder.withParameter(LootContextParams.BLOCK_STATE, state).create(LootContextParamSets.BLOCK); - BlockEntity tileEntity = context.getParamOrNull(LootContextParams.BLOCK_ENTITY); - List drops = new ArrayList<>(super.getDrops(state, builder)); - if (tileEntity instanceof IPipeNode pipeTile) { - if (pipeTile.getFrameMaterial() != null) { - drops.addAll(GTMaterialBlocks.MATERIAL_BLOCKS.get(TagPrefix.frameGt, pipeTile.getFrameMaterial()) - .getDefaultState().getDrops(builder)); - } - for (Direction direction : GTUtil.DIRECTIONS) { - pipeTile.getCoverContainer().removeCover(direction, null); - } - } - return drops; - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/api/blockentity/BaseSyncedBlockEntity.java b/src/main/java/com/gregtechceu/gtceu/api/blockentity/BaseSyncedBlockEntity.java new file mode 100644 index 0000000000..84b2750580 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/blockentity/BaseSyncedBlockEntity.java @@ -0,0 +1,119 @@ +package com.gregtechceu.gtceu.api.blockentity; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.network.PacketDataList; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.network.Connection; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.ClientGamePacketListener; +import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.function.Consumer; + +public abstract class BaseSyncedBlockEntity extends BlockEntity implements ISyncedBlockEntity, INeighborCache { + + private final PacketDataList updates = new PacketDataList(); + + public BaseSyncedBlockEntity(BlockEntityType type, BlockPos pos, BlockState blockState) { + super(type, pos, blockState); + } + + public @Nullable BlockEntity getNeighbor(@NotNull Direction side) { + if (level == null || worldPosition == null) return null; + return level.getBlockEntity(worldPosition.relative(side)); + } + + @Override + public final void writeCustomData(int discriminator, @NotNull Consumer<@NotNull FriendlyByteBuf> dataWriter) { + ByteBuf backedBuffer = Unpooled.buffer(); + dataWriter.accept(new FriendlyByteBuf(backedBuffer)); + byte[] updateData = Arrays.copyOfRange(backedBuffer.array(), 0, backedBuffer.writerIndex()); + this.updates.add(discriminator, updateData); + notifyWorld(); + } + + /** + * Adds all data packets from another synced tile entity. Useful when the old tile is replaced with a new one. + * + * @param syncedBlockEntity other synced tile entity + */ + public void addPacketsFrom(BaseSyncedBlockEntity syncedBlockEntity) { + if (this == syncedBlockEntity || syncedBlockEntity.updates.isEmpty()) return; + boolean wasEmpty = this.updates.isEmpty(); + this.updates.addAll(syncedBlockEntity.updates); + syncedBlockEntity.updates.clear(); + if (wasEmpty) notifyWorld(); // if the data is not empty we already notified the world + } + + private void notifyWorld() { + BlockState blockState = getBlockState(); + level.sendBlockUpdated(getBlockPos(), blockState, blockState, 0); + } + + @Override + public final Packet getUpdatePacket() { + if (this.updates.isEmpty()) { + return null; + } + CompoundTag updateTag = new CompoundTag(); + updateTag.put("d", this.updates.dumpToNbt()); + return ClientboundBlockEntityDataPacket.create(this, be -> updateTag); + } + + @Override + public void onDataPacket(Connection net, ClientboundBlockEntityDataPacket pkt) { + CompoundTag updateTag = pkt.getTag(); + ListTag listTag = updateTag.getList("d", Tag.TAG_COMPOUND); + for (Tag entryBase : listTag) { + CompoundTag entryTag = (CompoundTag) entryBase; + for (String discriminatorKey : entryTag.getAllKeys()) { + ByteBuf backedBuffer = Unpooled.copiedBuffer(entryTag.getByteArray(discriminatorKey)); + receiveCustomData(Integer.parseInt(discriminatorKey), new FriendlyByteBuf(backedBuffer)); + if (backedBuffer.readableBytes() != 0) { + GTCEu.LOGGER.error( + "Block entity {} failed to finish reading receiveCustomData with discriminator {} and {} bytes remaining", + BuiltInRegistries.BLOCK_ENTITY_TYPE.getKey(this.getType()), discriminatorKey, + backedBuffer.readableBytes()); + } + } + } + } + + @Override + public final @NotNull CompoundTag getUpdateTag() { + CompoundTag updateTag = super.saveWithoutMetadata(); + ByteBuf backedBuffer = Unpooled.buffer(); + writeInitialSyncData(new FriendlyByteBuf(backedBuffer)); + byte[] updateData = Arrays.copyOfRange(backedBuffer.array(), 0, backedBuffer.writerIndex()); + updateTag.putByteArray("d", updateData); + return updateTag; + } + + @Override + public final void handleUpdateTag(@NotNull CompoundTag tag) { + super.handleUpdateTag(tag); // deserializes Forge data and capabilities + byte[] updateData = tag.getByteArray("d"); + ByteBuf backedBuffer = Unpooled.copiedBuffer(updateData); + receiveInitialSyncData(new FriendlyByteBuf(backedBuffer)); + if (backedBuffer.readableBytes() != 0) { + GTCEu.LOGGER.error("Block entity {} failed to finish reading initialSyncData with {} bytes remaining", + BuiltInRegistries.BLOCK_ENTITY_TYPE.getKey(this.getType()), backedBuffer.readableBytes()); + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/blockentity/IDirtyNotifiable.java b/src/main/java/com/gregtechceu/gtceu/api/blockentity/IDirtyNotifiable.java new file mode 100644 index 0000000000..56848697c1 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/blockentity/IDirtyNotifiable.java @@ -0,0 +1,6 @@ +package com.gregtechceu.gtceu.api.blockentity; + +public interface IDirtyNotifiable { + + void markAsDirty(); +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/blockentity/IHasWorldObjectAndCoords.java b/src/main/java/com/gregtechceu/gtceu/api/blockentity/IHasWorldObjectAndCoords.java new file mode 100644 index 0000000000..33b93e48ea --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/blockentity/IHasWorldObjectAndCoords.java @@ -0,0 +1,37 @@ +package com.gregtechceu.gtceu.api.blockentity; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.Level; + +public interface IHasWorldObjectAndCoords extends IDirtyNotifiable { + + Level getLevel(); + + BlockPos getBlockPos(); + + default boolean isServerSide() { + return getLevel() != null && !getLevel().isClientSide; + } + + default boolean isClientSide() { + return getLevel() != null && getLevel().isClientSide; + } + + default void notifyBlockUpdate() { + if (getLevel() != null) { + getLevel().updateNeighborsAt(getBlockPos(), getLevel().getBlockState(getBlockPos()).getBlock()); + } + } + + default void scheduleRenderUpdate() { + var pos = getBlockPos(); + if (getLevel() != null) { + var state = getLevel().getBlockState(pos); + if (getLevel().isClientSide) { + getLevel().sendBlockUpdated(pos, state, state, 1 << 3); + } else { + getLevel().blockEvent(pos, state.getBlock(), 1, 0); + } + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/blockentity/INeighborCache.java b/src/main/java/com/gregtechceu/gtceu/api/blockentity/INeighborCache.java new file mode 100644 index 0000000000..6361cf2a33 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/blockentity/INeighborCache.java @@ -0,0 +1,37 @@ +package com.gregtechceu.gtceu.api.blockentity; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * An interface defining access to cached neighboring tile entities to a block or tile entity + */ +public interface INeighborCache extends IHasWorldObjectAndCoords { + + /** + * @param facing the side at which the neighbor is located + * @return the neighboring tile entity at the side + */ + default @Nullable BlockEntity getNeighbor(@NotNull Direction facing) { + return getLevel().getBlockEntity(getBlockPos().relative(facing)); + } + + default @Nullable BlockEntity getNeighborNoChunkloading(@NotNull Direction facing) { + BlockPos pos = getBlockPos().relative(facing); + return getLevel().isLoaded(pos) ? getLevel().getBlockEntity(pos) : null; + } + + /** + * Called when an adjacent neighboring block has changed at a side in some way + * + * @param fromBlock + * @param fromPos the side at which the neighbor has changed + * @param isMoving + */ + void onNeighborChanged(Block fromBlock, BlockPos fromPos, boolean isMoving); +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/blockentity/ISyncedBlockEntity.java b/src/main/java/com/gregtechceu/gtceu/api/blockentity/ISyncedBlockEntity.java new file mode 100644 index 0000000000..1dd2544a15 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/blockentity/ISyncedBlockEntity.java @@ -0,0 +1,94 @@ +package com.gregtechceu.gtceu.api.blockentity; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.Connection; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; +import net.minecraft.world.level.block.entity.BlockEntity; + +import org.jetbrains.annotations.NotNull; + +import java.util.function.Consumer; + +/** + * Functions which sync data between the Server and Client sides in a TileEntity. + */ +public interface ISyncedBlockEntity { + + /** + * Used to sync data from Server -> Client. + * Called during initial loading of the chunk or when many blocks change at once. + *

+ * Data is received in {@link #receiveInitialSyncData(FriendlyByteBuf)}. + *

+ * Typically used to send server side data to the client on initial chunk loading. + *

+ * Should be called automatically. + *

+ * This method is called Server-Side. + *

+ * Equivalent to {@link BlockEntity#getUpdateTag()}. + * + * @param buf the buffer to write data to + */ + default void writeInitialSyncData(@NotNull FriendlyByteBuf buf) {} + + /** + * Used to receive Server -> Client sync data. + * Called during initial loading of the chunk or when many blocks change at once. + *

+ * Data sent is from {@link #writeInitialSyncData(FriendlyByteBuf)}. + *

+ * Typically used to receive server side data on initial chunk loading. + *

+ * Should be called automatically. + *

+ * This method is called Client-Side. + *

+ * Equivalent to {@link net.minecraft.world.level.block.entity.BlockEntity#handleUpdateTag(CompoundTag)}. + * + * @param buf the buffer to read data from + */ + default void receiveInitialSyncData(@NotNull FriendlyByteBuf buf) {} + + /** + * Used to send an anonymous Server -> Client packet. + * Call to build up the packet to send to the client when it is re-synced. + *

+ * Data is received in {@link #receiveCustomData(int, FriendlyByteBuf)}; + *

+ * Typically used to signal to the client that a rendering update is needed + * when sending a server-side state update. + *

+ * Should be called manually. + *

+ * This method is called Server-Side. + *

+ * Equivalent to {@link BlockEntity#getUpdatePacket()} + * + * @param discriminator the discriminator determining the packet sent. + * @param dataWriter a consumer which writes packet data to a buffer. + * @see gregtech.api.capability.GregtechDataCodes + */ + default void writeCustomData(int discriminator, @NotNull Consumer<@NotNull FriendlyByteBuf> dataWriter) {} + + /** + * Used to receive an anonymous Server -> Client packet. + * Called when receiving a packet for the location this TileEntity is currently in. + *

+ * Data is sent with {@link #writeCustomData(int, Consumer)}. + *

+ * Typically used to perform a rendering update when receiving server-side state changes. + *

+ * Should be called automatically. + *

+ * This method is called Client-Side. + *

+ * Equivalent to {@link BlockEntity#onDataPacket(Connection, ClientboundBlockEntityDataPacket)} + * + * @param discriminator the discriminator determining the packet sent. + * @param buf the buffer containing the packet data. + * @see gregtech.api.capability.GregtechDataCodes + */ + default void receiveCustomData(int discriminator, @NotNull FriendlyByteBuf buf) {} +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/blockentity/MetaMachineBlockEntity.java b/src/main/java/com/gregtechceu/gtceu/api/blockentity/MetaMachineBlockEntity.java index 4344734f2a..a711b21b50 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/blockentity/MetaMachineBlockEntity.java +++ b/src/main/java/com/gregtechceu/gtceu/api/blockentity/MetaMachineBlockEntity.java @@ -3,6 +3,9 @@ import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.GTValues; import com.gregtechceu.gtceu.api.capability.*; +import com.gregtechceu.gtceu.api.capability.data.IComputationProvider; +import com.gregtechceu.gtceu.api.capability.data.IComputationUser; +import com.gregtechceu.gtceu.api.capability.data.IDataAccess; import com.gregtechceu.gtceu.api.capability.forge.GTCapability; import com.gregtechceu.gtceu.api.item.tool.GTToolType; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; @@ -15,10 +18,10 @@ import com.gregtechceu.gtceu.api.misc.LaserContainerList; import com.gregtechceu.gtceu.api.pipenet.longdistance.ILDEndpoint; import com.gregtechceu.gtceu.client.renderer.GTRendererProvider; -import com.gregtechceu.gtceu.common.datafixers.TagFixer; +import com.gregtechceu.gtceu.common.datafixer.TagFixer; import com.gregtechceu.gtceu.common.machine.owner.IMachineOwner; -import com.gregtechceu.gtceu.common.pipelike.fluidpipe.longdistance.LDFluidEndpointMachine; -import com.gregtechceu.gtceu.common.pipelike.item.longdistance.LDItemEndpointMachine; +import com.gregtechceu.gtceu.common.pipelike.longdistance.fluid.LDFluidEndpointMachine; +import com.gregtechceu.gtceu.common.pipelike.longdistance.item.LDItemEndpointMachine; import com.gregtechceu.gtceu.utils.GTTransferUtils; import com.lowdragmc.lowdraglib.client.renderer.IRenderer; @@ -59,7 +62,7 @@ * @date 2023/2/17 * @implNote MetaMachineBlockEntity */ -public class MetaMachineBlockEntity extends BlockEntity implements IMachineBlockEntity { +public class MetaMachineBlockEntity extends NeighborCacheBlockEntity implements IMachineBlockEntity { public final MultiManagedStorage managedStorage = new MultiManagedStorage(); @Getter @@ -71,7 +74,7 @@ public class MetaMachineBlockEntity extends BlockEntity implements IMachineBlock private final long offset = GTValues.RNG.nextInt(20); protected MetaMachineBlockEntity(BlockEntityType type, BlockPos pos, BlockState blockState) { - super(type, pos, blockState); + super(type, pos, blockState, true); this.metaMachine = getDefinition().createMetaMachine(this); } @@ -255,19 +258,28 @@ public static LazyOptional getCapability(MetaMachine machine, @NotNull Ca LazyOptional.of(() -> list.size() == 1 ? list.get(0) : new LaserContainerList(list))); } } else if (cap == GTCapability.CAPABILITY_COMPUTATION_PROVIDER) { - if (machine instanceof IOpticalComputationProvider computationProvider) { + if (machine instanceof IComputationProvider computationProvider) { return GTCapability.CAPABILITY_COMPUTATION_PROVIDER.orEmpty(cap, LazyOptional.of(() -> computationProvider)); } - var list = getCapabilitiesFromTraits(machine.getTraits(), side, IOpticalComputationProvider.class); + var list = getCapabilitiesFromTraits(machine.getTraits(), side, IComputationProvider.class); if (!list.isEmpty()) { return GTCapability.CAPABILITY_COMPUTATION_PROVIDER.orEmpty(cap, LazyOptional.of(() -> list.get(0))); } + } else if (cap == GTCapability.CAPABILITY_COMPUTATION_USER) { + if (machine instanceof IComputationUser computationProvider) { + return GTCapability.CAPABILITY_COMPUTATION_USER.orEmpty(cap, + LazyOptional.of(() -> computationProvider)); + } + var list = getCapabilitiesFromTraits(machine.getTraits(), side, IComputationUser.class); + if (!list.isEmpty()) { + return GTCapability.CAPABILITY_COMPUTATION_USER.orEmpty(cap, LazyOptional.of(() -> list.get(0))); + } } else if (cap == GTCapability.CAPABILITY_DATA_ACCESS) { - if (machine instanceof IDataAccessHatch computationProvider) { + if (machine instanceof IDataAccess computationProvider) { return GTCapability.CAPABILITY_DATA_ACCESS.orEmpty(cap, LazyOptional.of(() -> computationProvider)); } - var list = getCapabilitiesFromTraits(machine.getTraits(), side, IDataAccessHatch.class); + var list = getCapabilitiesFromTraits(machine.getTraits(), side, IDataAccess.class); if (!list.isEmpty()) { return GTCapability.CAPABILITY_DATA_ACCESS.orEmpty(cap, LazyOptional.of(() -> list.get(0))); } @@ -321,6 +333,11 @@ public AABB getRenderBoundingBox() { return new AABB(worldPosition.offset(-1, 0, -1), worldPosition.offset(2, 2, 2)); } + @Override + public void markAsDirty() { + this.setChanged(); + } + @Override protected void saveAdditional(CompoundTag tag) { super.saveAdditional(tag); diff --git a/src/main/java/com/gregtechceu/gtceu/api/blockentity/NeighborCacheBlockEntity.java b/src/main/java/com/gregtechceu/gtceu/api/blockentity/NeighborCacheBlockEntity.java new file mode 100644 index 0000000000..37470ed282 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/blockentity/NeighborCacheBlockEntity.java @@ -0,0 +1,98 @@ +package com.gregtechceu.gtceu.api.blockentity; + +import com.gregtechceu.gtceu.utils.GTUtil; + +import com.lowdragmc.lowdraglib.syncdata.blockentity.IAsyncAutoSyncBlockEntity; +import com.lowdragmc.lowdraglib.syncdata.blockentity.IAutoPersistBlockEntity; +import com.lowdragmc.lowdraglib.syncdata.blockentity.IRPCBlockEntity; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; + +import org.jetbrains.annotations.MustBeInvokedByOverriders; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; + +public abstract class NeighborCacheBlockEntity extends BaseSyncedBlockEntity implements INeighborCache, + IAsyncAutoSyncBlockEntity, IRPCBlockEntity, IAutoPersistBlockEntity { + + private final BlockEntity[] neighbors = new BlockEntity[6]; + private boolean neighborsInvalidated = false; + + /** + * @param doInvalidationHere set to false if you override {@link NeighborCacheBlockEntity#invalidateNeighbors()} + * with a method that references something you do not yet have set. + */ + public NeighborCacheBlockEntity(BlockEntityType type, BlockPos pos, BlockState blockState, + boolean doInvalidationHere) { + super(type, pos, blockState); + if (doInvalidationHere) invalidateNeighbors(); + } + + protected void invalidateNeighbors() { + if (!this.neighborsInvalidated) { + Arrays.fill(this.neighbors, this); + this.neighborsInvalidated = true; + } + } + + @MustBeInvokedByOverriders + @Override + public void setLevel(@NotNull Level LevelIn) { + super.setLevel(LevelIn); + invalidateNeighbors(); + } + + @Override + public void setRemoved() { + super.setRemoved(); + invalidateNeighbors(); + } + + @Override + public void onChunkUnloaded() { + super.onChunkUnloaded(); + invalidateNeighbors(); + } + + @Override + public @Nullable BlockEntity getNeighbor(@NotNull Direction facing) { + if (level == null || this.getBlockPos() == null) return null; + int i = facing.get3DDataValue(); + BlockEntity neighbor = this.neighbors[i]; + if (neighbor == this || (neighbor != null && neighbor.isRemoved())) { + neighbor = level.getBlockEntity(worldPosition.relative(facing)); + this.neighbors[i] = neighbor; + this.neighborsInvalidated = false; + } + return neighbor; + } + + @Override + public @Nullable BlockEntity getNeighborNoChunkloading(@NotNull Direction facing) { + if (level == null || this.getBlockPos() == null) return null; + int i = facing.get3DDataValue(); + BlockEntity neighbor = this.neighbors[i]; + if (neighbor == this || (neighbor != null && neighbor.isRemoved())) { + BlockPos pos = getBlockPos().relative(facing); + if (level.isLoaded(pos)) { + neighbor = level.getBlockEntity(pos); + this.neighbors[i] = neighbor; + this.neighborsInvalidated = false; + } else return null; + } + return neighbor; + } + + public void onNeighborChanged(Block fromBlock, BlockPos fromPos, boolean isMoving) { + Direction facing = GTUtil.getFacingToNeighbor(this.getBlockPos(), fromPos); + if (facing != null) this.neighbors[facing.get3DDataValue()] = this; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/blockentity/PipeBlockEntity.java b/src/main/java/com/gregtechceu/gtceu/api/blockentity/PipeBlockEntity.java deleted file mode 100644 index 4dfca277d8..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/api/blockentity/PipeBlockEntity.java +++ /dev/null @@ -1,466 +0,0 @@ -package com.gregtechceu.gtceu.api.blockentity; - -import com.gregtechceu.gtceu.api.GTValues; -import com.gregtechceu.gtceu.api.block.BlockProperties; -import com.gregtechceu.gtceu.api.block.MaterialPipeBlock; -import com.gregtechceu.gtceu.api.capability.ICoverable; -import com.gregtechceu.gtceu.api.capability.IToolable; -import com.gregtechceu.gtceu.api.cover.CoverBehavior; -import com.gregtechceu.gtceu.api.data.chemical.material.Material; -import com.gregtechceu.gtceu.api.data.tag.TagPrefix; -import com.gregtechceu.gtceu.api.gui.GuiTextures; -import com.gregtechceu.gtceu.api.item.tool.GTToolType; -import com.gregtechceu.gtceu.api.item.tool.IToolGridHighlight; -import com.gregtechceu.gtceu.api.machine.TickableSubscription; -import com.gregtechceu.gtceu.api.pipenet.*; -import com.gregtechceu.gtceu.common.data.GTMaterialBlocks; -import com.gregtechceu.gtceu.common.datafixers.TagFixer; -import com.gregtechceu.gtceu.utils.GTUtil; - -import com.lowdragmc.lowdraglib.gui.texture.ResourceTexture; -import com.lowdragmc.lowdraglib.syncdata.IEnhancedManaged; -import com.lowdragmc.lowdraglib.syncdata.IManagedStorage; -import com.lowdragmc.lowdraglib.syncdata.annotation.DescSynced; -import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; -import com.lowdragmc.lowdraglib.syncdata.annotation.RequireRerender; -import com.lowdragmc.lowdraglib.syncdata.blockentity.IAsyncAutoSyncBlockEntity; -import com.lowdragmc.lowdraglib.syncdata.blockentity.IAutoPersistBlockEntity; -import com.lowdragmc.lowdraglib.syncdata.field.FieldManagedStorage; -import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; - -import net.minecraft.MethodsReturnNonnullByDefault; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.core.particles.ParticleTypes; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.server.TickTask; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.InteractionResult; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.context.UseOnContext; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.entity.BlockEntityType; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.phys.BlockHitResult; - -import com.mojang.datafixers.util.Pair; -import lombok.Getter; -import lombok.Setter; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - -import javax.annotation.ParametersAreNonnullByDefault; - -/** - * @author KilaBash - * @date 2023/2/28 - * @implNote PipeBlockEntity - */ -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault -public abstract class PipeBlockEntity & IPipeType, NodeDataType> - extends BlockEntity implements IPipeNode, IEnhancedManaged, - IAsyncAutoSyncBlockEntity, IAutoPersistBlockEntity, IToolGridHighlight, IToolable { - - public static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder(PipeBlockEntity.class); - @Getter - private final FieldManagedStorage syncStorage = new FieldManagedStorage(this); - private final long offset = GTValues.RNG.nextInt(20); - - @Getter - @DescSynced - @Persisted(key = "cover") - protected final PipeCoverContainer coverContainer; - - @Getter - @Setter - @DescSynced - @Persisted - @RequireRerender - protected int connections = Node.ALL_CLOSED; - @Setter - @DescSynced - @Persisted - @RequireRerender - private int blockedConnections = Node.ALL_CLOSED; - private NodeDataType cachedNodeData; - - @Persisted - @DescSynced - @RequireRerender - @Getter - @Setter - private int paintingColor = -1; - - @RequireRerender - @DescSynced - @Persisted - @Getter - @Setter - @Nullable - private Material frameMaterial = null; - private final List serverTicks; - private final List waitingToAdd; - - public PipeBlockEntity(BlockEntityType type, BlockPos pos, BlockState blockState) { - super(type, pos, blockState); - this.coverContainer = new PipeCoverContainer(this); - this.serverTicks = new ArrayList<>(); - this.waitingToAdd = new ArrayList<>(); - } - - ////////////////////////////////////// - // ***** Initialization ******// - ////////////////////////////////////// - public void scheduleRenderUpdate() { - IPipeNode.super.scheduleRenderUpdate(); - } - - @Override - public IManagedStorage getRootStorage() { - return syncStorage; - } - - @Override - public ManagedFieldHolder getFieldHolder() { - return MANAGED_FIELD_HOLDER; - } - - @Override - public void onChanged() { - var level = getLevel(); - if (level != null && !level.isClientSide && level.getServer() != null) { - level.getServer().execute(this::setChanged); - } - } - - @Override - public long getOffsetTimer() { - return level == null ? offset : (level.getServer().getTickCount() + offset); - } - - @Override - public void setRemoved() { - super.setRemoved(); - coverContainer.onUnload(); - } - - @Override - public void clearRemoved() { - super.clearRemoved(); - coverContainer.onLoad(); - } - - @Override - public int getNumConnections() { - int count = 0; - int connections = getConnections(); - while (connections > 0) { - count++; - connections = connections & (connections - 1); - } - return count; - } - - @Override - public int getBlockedConnections() { - return canHaveBlockedFaces() ? blockedConnections : 0; - } - - @Override - public NodeDataType getNodeData() { - if (cachedNodeData == null) { - this.cachedNodeData = getPipeBlock().createProperties(this); - } - return cachedNodeData; - } - - @Nullable - public TickableSubscription subscribeServerTick(Runnable runnable) { - if (!isRemote()) { - var subscription = new TickableSubscription(runnable); - waitingToAdd.add(subscription); - var blockState = getBlockState(); - if (!blockState.getValue(BlockProperties.SERVER_TICK)) { - if (getLevel() instanceof ServerLevel serverLevel) { - blockState = blockState.setValue(BlockProperties.SERVER_TICK, true); - setBlockState(blockState); - serverLevel.getServer().tell(new TickTask(0, () -> serverLevel.setBlockAndUpdate(getBlockPos(), - getBlockState().setValue(BlockProperties.SERVER_TICK, true)))); - } - } - return subscription; - } - return null; - } - - public void unsubscribe(@Nullable TickableSubscription current) { - if (current != null) { - current.unsubscribe(); - } - } - - public final void serverTick() { - if (!waitingToAdd.isEmpty()) { - serverTicks.addAll(waitingToAdd); - waitingToAdd.clear(); - } - var iter = serverTicks.iterator(); - while (iter.hasNext()) { - var tickable = iter.next(); - if (tickable.isStillSubscribed()) { - tickable.run(); - } - if (!tickable.isStillSubscribed()) { - iter.remove(); - } - } - if (serverTicks.isEmpty() && waitingToAdd.isEmpty() && !this.isRemoved()) { - getLevel().setBlockAndUpdate(getBlockPos(), getBlockState().setValue(BlockProperties.SERVER_TICK, false)); - } - } - - ////////////////////////////////////// - // ******* Pipe Status *******// - ////////////////////////////////////// - - @Override - public void setBlocked(Direction side, boolean isBlocked) { - if (level instanceof ServerLevel serverLevel && canHaveBlockedFaces()) { - blockedConnections = withSideConnection(blockedConnections, side, isBlocked); - setChanged(); - LevelPipeNet worldPipeNet = getPipeBlock().getWorldPipeNet(serverLevel); - PipeNet net = worldPipeNet.getNetFromPos(getBlockPos()); - if (net != null) { - net.onPipeConnectionsUpdate(); - } - } - } - - @Override - public int getVisualConnections() { - var visualConnections = connections; - for (var side : GTUtil.DIRECTIONS) { - var cover = getCoverContainer().getCoverAtSide(side); - if (cover != null && cover.canPipePassThrough()) { - visualConnections = visualConnections | (1 << side.ordinal()); - } - } - return visualConnections; - } - - @Override - public void setConnection(Direction side, boolean connected, boolean fromNeighbor) { - // fix desync between two connections. Can happen if a pipe side is blocked, and a new pipe is placed next to - // it. - if (!getLevel().isClientSide) { - if (isConnected(side) == connected) { - return; - } - BlockEntity tile = getNeighbor(side); - // block connections if Pipe Types do not match - if (connected && - tile instanceof IPipeNode pipeTile && - pipeTile.getPipeType().getClass() != this.getPipeType().getClass()) { - return; - } - - if (!connected) { - var cover = getCoverContainer().getCoverAtSide(side); - if (cover != null && cover.canPipePassThrough()) return; - } - - connections = withSideConnection(connections, side, connected); - - updateNetworkConnection(side, connected); - setChanged(); - - if (!fromNeighbor && tile instanceof IPipeNode pipeTile) { - syncPipeConnections(side, pipeTile); - } - } - } - - private void syncPipeConnections(Direction side, IPipeNode pipe) { - Direction oppositeSide = side.getOpposite(); - boolean neighbourOpen = pipe.isConnected(oppositeSide); - if (isConnected(side) == neighbourOpen) { - return; - } - if (!neighbourOpen || pipe.getCoverContainer().getCoverAtSide(oppositeSide) == null) { - pipe.setConnection(oppositeSide, !neighbourOpen, true); - } - } - - private void updateNetworkConnection(Direction side, boolean connected) { - LevelPipeNet worldPipeNet = getPipeBlock().getWorldPipeNet((ServerLevel) getLevel()); - worldPipeNet.updateBlockedConnections(getPipePos(), side, !connected); - } - - protected int withSideConnection(int blockedConnections, Direction side, boolean connected) { - int index = 1 << side.ordinal(); - if (connected) { - return blockedConnections | index; - } else { - return blockedConnections & ~index; - } - } - - @Override - public void notifyBlockUpdate() { - getLevel().updateNeighborsAt(getBlockPos(), getPipeBlock()); - getPipeBlock().updateActiveNodeStatus(getLevel(), getBlockPos(), this); - } - - @Override - public boolean triggerEvent(int id, int para) { - if (id == 1) { // chunk re render - if (level != null && level.isClientSide) { - scheduleRenderUpdate(); - } - return true; - } - return false; - } - - @Override - public void setChanged() { - if (getLevel() != null) { - getLevel().blockEntityChanged(getBlockPos()); - } - } - - ////////////////////////////////////// - // ******* Interaction *******// - ////////////////////////////////////// - @Override - public boolean shouldRenderGrid(Player player, BlockPos pos, BlockState state, ItemStack held, - Set toolTypes) { - if (toolTypes.contains(getPipeTuneTool())) return true; - for (CoverBehavior cover : coverContainer.getCovers()) { - if (cover.shouldRenderGrid(player, pos, state, held, toolTypes)) return true; - } - return false; - } - - public ResourceTexture getPipeTexture(boolean isBlock) { - return isBlock ? GuiTextures.TOOL_PIPE_CONNECT : GuiTextures.TOOL_PIPE_BLOCK; - } - - @Override - public ResourceTexture sideTips(Player player, BlockPos pos, BlockState state, Set toolTypes, - Direction side) { - if (toolTypes.contains(getPipeTuneTool())) { - if (player.isShiftKeyDown() && this.canHaveBlockedFaces()) { - return getPipeTexture(isBlocked(side)); - } else { - return getPipeTexture(isConnected(side)); - } - } - var cover = coverContainer.getCoverAtSide(side); - if (cover != null) { - return cover.sideTips(player, pos, state, toolTypes, side); - } - return null; - } - - @Override - public Pair onToolClick(Set toolTypes, ItemStack itemStack, - UseOnContext context) { - // the side hit from the machine grid - var playerIn = context.getPlayer(); - if (playerIn == null) return Pair.of(null, InteractionResult.PASS); - - var hand = context.getHand(); - var hitResult = new BlockHitResult(context.getClickLocation(), context.getClickedFace(), - context.getClickedPos(), false); - Direction gridSide = ICoverable.determineGridSideHit(hitResult); - CoverBehavior coverBehavior = gridSide == null ? null : coverContainer.getCoverAtSide(gridSide); - if (gridSide == null) gridSide = hitResult.getDirection(); - - // Prioritize covers where they apply (Screwdriver, Soft Mallet) - if (toolTypes.isEmpty() && playerIn.isShiftKeyDown()) { - if (coverBehavior != null) { - return Pair.of(null, coverBehavior.onScrewdriverClick(playerIn, hand, hitResult)); - } - } - if (toolTypes.contains(GTToolType.SCREWDRIVER)) { - if (coverBehavior != null) { - return Pair.of(GTToolType.SCREWDRIVER, coverBehavior.onScrewdriverClick(playerIn, hand, hitResult)); - } - } else if (toolTypes.contains(GTToolType.SOFT_MALLET)) { - if (coverBehavior != null) { - return Pair.of(GTToolType.SOFT_MALLET, coverBehavior.onSoftMalletClick(playerIn, hand, hitResult)); - } - } else if (toolTypes.contains(getPipeTuneTool())) { - if (playerIn.isShiftKeyDown() && this.canHaveBlockedFaces()) { - boolean isBlocked = this.isBlocked(gridSide); - this.setBlocked(gridSide, !isBlocked); - } else { - boolean isOpen = this.isConnected(gridSide); - this.setConnection(gridSide, !isOpen, false); - } - playerIn.swing(hand); - return Pair.of(getPipeTuneTool(), InteractionResult.CONSUME); - } else if (toolTypes.contains(GTToolType.CROWBAR)) { - if (coverBehavior != null) { - if (!isRemote()) { - getCoverContainer().removeCover(gridSide, playerIn); - playerIn.swing(hand); - return Pair.of(GTToolType.CROWBAR, InteractionResult.CONSUME); - } - } else { - if (frameMaterial != null) { - Block.popResource(getLevel(), getPipePos(), - GTMaterialBlocks.MATERIAL_BLOCKS.get(TagPrefix.frameGt, frameMaterial).asStack()); - frameMaterial = null; - playerIn.swing(hand); - return Pair.of(GTToolType.CROWBAR, InteractionResult.CONSUME); - } - } - } - - return Pair.of(null, InteractionResult.PASS); - } - - public GTToolType getPipeTuneTool() { - return GTToolType.WRENCH; - } - - @Override - public int getDefaultPaintingColor() { - return this.getPipeBlock() instanceof MaterialPipeBlock materialPipeBlock ? - materialPipeBlock.material.getMaterialRGB() : IPipeNode.super.getDefaultPaintingColor(); - } - - public void doExplosion(float explosionPower) { - getLevel().removeBlock(getPipePos(), false); - if (!getLevel().isClientSide) { - ((ServerLevel) getLevel()).sendParticles(ParticleTypes.LARGE_SMOKE, getPipePos().getX() + 0.5, - getPipePos().getY() + 0.5, getPipePos().getZ() + 0.5, - 10, 0.2, 0.2, 0.2, 0.0); - } - getLevel().explode(null, getPipePos().getX() + 0.5, getPipePos().getY() + 0.5, getPipePos().getZ() + 0.5, - explosionPower, Level.ExplosionInteraction.NONE); - } - - public static boolean isFaceBlocked(int blockedConnections, Direction side) { - return (blockedConnections & (1 << side.ordinal())) > 0; - } - - public static boolean isConnected(int connections, Direction side) { - return (connections & (1 << side.ordinal())) > 0; - } - - @Override - public void load(CompoundTag tag) { - TagFixer.fixFluidTags(tag); - super.load(tag); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/api/capability/GTCapabilityHelper.java b/src/main/java/com/gregtechceu/gtceu/api/capability/GTCapabilityHelper.java index 4cea0f02cb..a12d65177a 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/capability/GTCapabilityHelper.java +++ b/src/main/java/com/gregtechceu/gtceu/api/capability/GTCapabilityHelper.java @@ -1,5 +1,8 @@ package com.gregtechceu.gtceu.api.capability; +import com.gregtechceu.gtceu.api.capability.data.IComputationProvider; +import com.gregtechceu.gtceu.api.capability.data.IComputationUser; +import com.gregtechceu.gtceu.api.capability.data.IDataAccess; import com.gregtechceu.gtceu.api.capability.forge.GTCapability; import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMaintenanceMachine; import com.gregtechceu.gtceu.api.machine.trait.RecipeLogic; @@ -90,18 +93,24 @@ public static IMaintenanceMachine getMaintenanceMachine(Level level, BlockPos po } @Nullable - public static ILaserContainer getLaser(Level level, BlockPos pos, @Nullable Direction side) { + public static ILaserRelay getLaser(Level level, BlockPos pos, @Nullable Direction side) { return getBlockEntityCapability(GTCapability.CAPABILITY_LASER, level, pos, side); } @Nullable - public static IOpticalComputationProvider getOpticalComputationProvider(Level level, BlockPos pos, - @Nullable Direction side) { + public static IComputationProvider getComputationProvider(Level level, BlockPos pos, + @Nullable Direction side) { return getBlockEntityCapability(GTCapability.CAPABILITY_COMPUTATION_PROVIDER, level, pos, side); } @Nullable - public static IDataAccessHatch getDataAccess(Level level, BlockPos pos, @Nullable Direction side) { + public static IComputationUser getComputationUser(Level level, BlockPos pos, + @Nullable Direction side) { + return getBlockEntityCapability(GTCapability.CAPABILITY_COMPUTATION_USER, level, pos, side); + } + + @Nullable + public static IDataAccess getDataAccess(Level level, BlockPos pos, @Nullable Direction side) { return getBlockEntityCapability(GTCapability.CAPABILITY_DATA_ACCESS, level, pos, side); } diff --git a/src/main/java/com/gregtechceu/gtceu/api/capability/ICoverable.java b/src/main/java/com/gregtechceu/gtceu/api/capability/ICoverable.java index eaa8de82d9..03e681c87e 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/capability/ICoverable.java +++ b/src/main/java/com/gregtechceu/gtceu/api/capability/ICoverable.java @@ -5,7 +5,6 @@ import com.gregtechceu.gtceu.api.blockentity.ITickSubscription; import com.gregtechceu.gtceu.api.cover.CoverBehavior; import com.gregtechceu.gtceu.api.cover.CoverDefinition; -import com.gregtechceu.gtceu.api.transfer.fluid.IFluidHandlerModifiable; import com.gregtechceu.gtceu.utils.GTUtil; import net.minecraft.core.BlockPos; @@ -16,14 +15,19 @@ import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; -import net.minecraftforge.items.IItemHandlerModifiable; +import net.minecraftforge.common.capabilities.ICapabilityProvider; +import net.minecraftforge.fluids.capability.IFluidHandler; +import net.minecraftforge.items.IItemHandler; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; @@ -32,7 +36,7 @@ import java.util.Objects; import java.util.stream.Collectors; -public interface ICoverable extends ITickSubscription, IAppearance { +public interface ICoverable extends ITickSubscription, IAppearance, ICapabilityProvider { Level getLevel(); @@ -50,27 +54,35 @@ public interface ICoverable extends ITickSubscription, IAppearance { void scheduleNeighborShapeUpdate(); + /** + * @param side the side to check + * @return if the cover can be added at the side + */ boolean canPlaceCoverOnSide(CoverDefinition definition, Direction side); + /** + * @return if it is possible to attach any cover at all + */ + boolean acceptsCovers(); + double getCoverPlateThickness(); Direction getFrontFacing(); boolean shouldRenderBackSide(); - IItemHandlerModifiable getItemHandlerCap(@Nullable Direction side, boolean useCoverCapability); + IItemHandler getItemHandlerCap(@Nullable Direction side, boolean useCoverCapability); - IFluidHandlerModifiable getFluidHandlerCap(@Nullable Direction side, boolean useCoverCapability); + IFluidHandler getFluidHandlerCap(@Nullable Direction side, boolean useCoverCapability); /** - * Its an internal method, you should never call it yourself. - *
* Use {@link ICoverable#removeCover(boolean, Direction, Player)} and * {@link ICoverable#placeCoverOnSide(Direction, ItemStack, CoverDefinition, ServerPlayer)} instead * * @param coverBehavior * @param side */ + @ApiStatus.Internal void setCoverAtSide(@Nullable CoverBehavior coverBehavior, Direction side); @Nullable @@ -79,7 +91,7 @@ public interface ICoverable extends ITickSubscription, IAppearance { default boolean placeCoverOnSide(Direction side, ItemStack itemStack, CoverDefinition coverDefinition, ServerPlayer player) { CoverBehavior coverBehavior = coverDefinition.createCoverBehavior(this, side); - if (!canPlaceCoverOnSide(coverDefinition, side) || !coverBehavior.canAttach()) { + if (!canPlaceCoverOnSide(coverDefinition, side) || !coverBehavior.canAttach(this, side)) { return false; } if (getCoverAtSide(side) != null) { @@ -150,6 +162,13 @@ default void onUnload() { } } + /** + * @param side the side to get the neighbor at + * @return the neighbor tile entity at the side + */ + @Nullable + BlockEntity getNeighbor(@NotNull Direction side); + default void onNeighborChanged(Block block, BlockPos fromPos, boolean isMoving) { for (CoverBehavior cover : getCovers()) { cover.onNeighborChanged(block, fromPos, isMoving); @@ -163,6 +182,13 @@ default boolean hasAnyCover() { return false; } + /** + * @param side the side to get the redstone from + * @param ignoreCover if the cover is being ignored + * @return the redstone signal being input at the side + */ + int getInputRedstoneSignal(@NotNull Direction side, boolean ignoreCover); + default boolean hasCover(Direction facing) { return getCoverAtSide(facing) != null; } @@ -249,7 +275,7 @@ static boolean canPlaceCover(CoverDefinition coverDef, ICoverable coverable) { for (Direction facing : GTUtil.DIRECTIONS) { if (coverable.canPlaceCoverOnSide(coverDef, facing)) { var cover = coverDef.createCoverBehavior(coverable, facing); - if (cover.canAttach()) { + if (cover.canAttach(coverable, facing)) { return true; } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/capability/IDataAccessHatch.java b/src/main/java/com/gregtechceu/gtceu/api/capability/IDataAccessHatch.java deleted file mode 100644 index 094a9471c5..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/api/capability/IDataAccessHatch.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.gregtechceu.gtceu.api.capability; - -import com.gregtechceu.gtceu.api.recipe.GTRecipe; - -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; -import java.util.Collection; - -public interface IDataAccessHatch { - - /** - * If passed a {@code seen} context, you must use {@link #isRecipeAvailable(GTRecipe, Collection)} to prevent - * infinite recursion - * - * @param recipe the recipe to check - * @return if the recipe is available for use - */ - default boolean isRecipeAvailable(@NotNull GTRecipe recipe) { - Collection list = new ArrayList<>(); - list.add(this); - return isRecipeAvailable(recipe, list); - } - - /** - * @param recipe the recipe to check - * @param seen the hatches already checked - * @return if the recipe is available for use - */ - boolean isRecipeAvailable(@NotNull GTRecipe recipe, @NotNull Collection seen); - - // REMEMBER TO CALL THIS FOR YOUR CUSTOM DATA HATCH TO WORK. - default GTRecipe modifyRecipe(GTRecipe recipe) { - // creative hatches do not need to check, they always have the recipe - if (this.isCreative()) return recipe; - - // hatches need to have the recipe available - if (this.isRecipeAvailable(recipe)) return recipe; - return null; - } - - /** - * @return true if this Data Access Hatch is creative or not - */ - boolean isCreative(); -} diff --git a/src/main/java/com/gregtechceu/gtceu/api/capability/IEnergyContainer.java b/src/main/java/com/gregtechceu/gtceu/api/capability/IEnergyContainer.java index 7440cf1c5f..c615b62d49 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/capability/IEnergyContainer.java +++ b/src/main/java/com/gregtechceu/gtceu/api/capability/IEnergyContainer.java @@ -14,7 +14,7 @@ public interface IEnergyContainer extends IEnergyInfoProvider { * @param amperage packet size (energy to add / input amperage) * @return amount of used amperes. 0 if not accepted anything. */ - long acceptEnergyFromNetwork(Direction side, long voltage, long amperage); + long acceptEnergyFromNetwork(Direction side, long voltage, long amperage, boolean simulate); /** * @return if this container accepts energy from the given side @@ -31,7 +31,7 @@ default boolean outputsEnergy(Direction side) { /** * This changes the amount stored. * This should only be used internally (f.e. draining while working or filling while generating). - * For transfer between blocks use {@link #acceptEnergyFromNetwork(Direction, long, long)}!!! + * For transfer between blocks use {@link #acceptEnergyFromNetwork(Direction, long, long, boolean)}!!! * * @param differenceAmount amount of energy to add (>0) or remove (<0) * @return amount of energy added or removed @@ -135,7 +135,7 @@ default boolean isOneProbeHidden() { IEnergyContainer DEFAULT = new IEnergyContainer() { @Override - public long acceptEnergyFromNetwork(Direction Direction, long l, long l1) { + public long acceptEnergyFromNetwork(Direction Direction, long l, long l1, boolean simulate) { return 0; } diff --git a/src/main/java/com/gregtechceu/gtceu/api/capability/IHazardParticleContainer.java b/src/main/java/com/gregtechceu/gtceu/api/capability/IHazardParticleContainer.java index 0be8c435a0..8723080e48 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/capability/IHazardParticleContainer.java +++ b/src/main/java/com/gregtechceu/gtceu/api/capability/IHazardParticleContainer.java @@ -21,36 +21,32 @@ default boolean outputsHazard(Direction side, MedicalCondition condition) { /** * This changes the amount stored. * - * @param differenceAmount amount of particles to add (>0) or remove (<0) + * @param amount amount of particles to add (>0) or remove (<0) + * @param simulate If true, the insertion is only simulated * @return amount of particles added or removed */ - float changeHazard(MedicalCondition condition, float differenceAmount); + float changeHazard(MedicalCondition condition, float amount, boolean simulate); /** * Adds specified amount of particles to this particles container * * @param particlesToAdd amount of particles to add + * @param simulate If true, the insertion is only simulated * @return amount of particles added */ - default float addHazard(MedicalCondition condition, float particlesToAdd) { - return changeHazard(condition, particlesToAdd); + default float addHazard(MedicalCondition condition, float particlesToAdd, boolean simulate) { + return changeHazard(condition, particlesToAdd, simulate); } /** * Removes specified amount of particles from this particles container * * @param particlesToRemove amount of particles to remove + * @param simulate If true, the insertion is only simulated * @return amount of particles removed */ - default float removeHazard(MedicalCondition condition, float particlesToRemove) { - return -changeHazard(condition, -particlesToRemove); - } - - /** - * @return the maximum amount of particles that can be inserted - */ - default float getHazardCanBeInserted(MedicalCondition condition) { - return getHazardCapacity(condition) - getHazardStored(condition); + default float removeHazard(MedicalCondition condition, float particlesToRemove, boolean simulate) { + return -changeHazard(condition, -particlesToRemove, simulate); } /** @@ -71,7 +67,7 @@ public boolean inputsHazard(Direction side, MedicalCondition condition) { } @Override - public float changeHazard(MedicalCondition condition, float differenceAmount) { + public float changeHazard(MedicalCondition condition, float amount, boolean simulate) { return 0; } diff --git a/src/main/java/com/gregtechceu/gtceu/api/capability/ILaserContainer.java b/src/main/java/com/gregtechceu/gtceu/api/capability/ILaserContainer.java index fbfcb5ed16..2d400c4f67 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/capability/ILaserContainer.java +++ b/src/main/java/com/gregtechceu/gtceu/api/capability/ILaserContainer.java @@ -3,4 +3,4 @@ /** * It is its own separate interface to make piping work easier */ -public interface ILaserContainer extends IEnergyContainer {} +public interface ILaserContainer extends IEnergyContainer, ILaserRelay {} diff --git a/src/main/java/com/gregtechceu/gtceu/api/capability/ILaserRelay.java b/src/main/java/com/gregtechceu/gtceu/api/capability/ILaserRelay.java new file mode 100644 index 0000000000..5208fe874f --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/capability/ILaserRelay.java @@ -0,0 +1,13 @@ +package com.gregtechceu.gtceu.api.capability; + +public interface ILaserRelay { + + /** + * Receive a laser pulse. + * + * @param laserVoltage the voltage of the laser. + * @param laserAmperage the amperage of the laser. + * @return how much amperage was received. + */ + long receiveLaser(long laserVoltage, long laserAmperage); +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/capability/IOpticalComputationHatch.java b/src/main/java/com/gregtechceu/gtceu/api/capability/IOpticalComputationHatch.java deleted file mode 100644 index 1b1235ce8a..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/api/capability/IOpticalComputationHatch.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.gregtechceu.gtceu.api.capability; - -public interface IOpticalComputationHatch extends IOpticalComputationProvider { - - /** If this hatch transmits or receives CWU/t. */ - boolean isTransmitter(); -} diff --git a/src/main/java/com/gregtechceu/gtceu/api/capability/IOpticalComputationProvider.java b/src/main/java/com/gregtechceu/gtceu/api/capability/IOpticalComputationProvider.java deleted file mode 100644 index 999197c823..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/api/capability/IOpticalComputationProvider.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.gregtechceu.gtceu.api.capability; - -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; -import java.util.Collection; - -/** - * MUST be implemented on any multiblock which uses - * Transmitter Computation Hatches in its structure. - */ -public interface IOpticalComputationProvider { - - /** - * Request some amount of CWU/t (Compute Work Units per tick) from this Machine. - * Implementors should expect these requests to occur each tick that computation is required. - * - * @param cwut Maximum amount of CWU/t requested. - * @return The amount of CWU/t that could be supplied. - */ - default int requestCWUt(int cwut, boolean simulate) { - Collection list = new ArrayList<>(); - list.add(this); - return requestCWUt(cwut, simulate, list); - } - - /** - * Request some amount of CWU/t (Compute Work Units per tick) from this Machine. - * Implementors should expect these requests to occur each tick that computation is required. - * - * @param cwut Maximum amount of CWU/t requested. - * @param seen The Optical Computation Providers already checked - * @return The amount of CWU/t that could be supplied. - */ - int requestCWUt(int cwut, boolean simulate, @NotNull Collection seen); - - /** - * The maximum of CWU/t that this computation provider can provide. - */ - default int getMaxCWUt() { - Collection list = new ArrayList<>(); - list.add(this); - return getMaxCWUt(list); - } - - /** - * The maximum of CWU/t that this computation provider can provide. - * - * @param seen The Optical Computation Providers already checked - */ - int getMaxCWUt(@NotNull Collection seen); - - /** - * Whether this Computation Provider can "Bridge" with other Computation Providers. - * Checked by machines like the Network Switch. - */ - default boolean canBridge() { - Collection list = new ArrayList<>(); - list.add(this); - return canBridge(list); - } - - /** - * Whether this Computation Provider can "Bridge" with other Computation Providers. - * Checked by machines like the Network Switch. - * - * @param seen The Optical Computation Providers already checked - */ - boolean canBridge(@NotNull Collection seen); -} diff --git a/src/main/java/com/gregtechceu/gtceu/api/capability/IOpticalComputationReceiver.java b/src/main/java/com/gregtechceu/gtceu/api/capability/IOpticalComputationReceiver.java deleted file mode 100644 index 204a6c311a..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/api/capability/IOpticalComputationReceiver.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.gregtechceu.gtceu.api.capability; - -import com.gregtechceu.gtceu.api.machine.trait.NotifiableComputationContainer; - -/** - * Used in conjunction with {@link NotifiableComputationContainer}. - */ -public interface IOpticalComputationReceiver { - - IOpticalComputationProvider getComputationProvider(); -} diff --git a/src/main/java/com/gregtechceu/gtceu/api/capability/IOpticalDataAccessHatch.java b/src/main/java/com/gregtechceu/gtceu/api/capability/IOpticalDataAccessHatch.java deleted file mode 100644 index 2b40949941..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/api/capability/IOpticalDataAccessHatch.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.gregtechceu.gtceu.api.capability; - -public interface IOpticalDataAccessHatch extends IDataAccessHatch { - - /** - * @return if this hatch transmits data through cables - */ - boolean isTransmitter(); -} diff --git a/src/main/java/com/gregtechceu/gtceu/api/capability/IPropertyFluidFilter.java b/src/main/java/com/gregtechceu/gtceu/api/capability/IPropertyFluidFilter.java index 59642e443a..02564860a2 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/capability/IPropertyFluidFilter.java +++ b/src/main/java/com/gregtechceu/gtceu/api/capability/IPropertyFluidFilter.java @@ -3,14 +3,13 @@ import com.gregtechceu.gtceu.api.fluids.FluidState; import com.gregtechceu.gtceu.api.fluids.attribute.FluidAttribute; import com.gregtechceu.gtceu.api.fluids.attribute.IAttributedFluid; -import com.gregtechceu.gtceu.utils.FormattingUtil; -import com.gregtechceu.gtceu.utils.GTUtil; import net.minecraft.network.chat.Component; import net.minecraft.world.level.material.Fluid; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.FluidType; +import it.unimi.dsi.fastutil.objects.Object2BooleanMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.UnmodifiableView; @@ -18,15 +17,13 @@ import java.util.List; import java.util.function.Predicate; -import static com.gregtechceu.gtceu.api.fluids.FluidConstants.CRYOGENIC_FLUID_THRESHOLD; - public interface IPropertyFluidFilter extends Predicate { @Override default boolean test(@NotNull FluidStack stack) { Fluid fluid = stack.getFluid(); FluidType fluidType = fluid.getFluidType(); - if (fluidType.getTemperature() < CRYOGENIC_FLUID_THRESHOLD && !isCryoProof()) return false; + if (fluidType.getTemperature() < getMinFluidTemperature()) return false; if (fluid instanceof IAttributedFluid attributedFluid) { FluidState state = attributedFluid.getState(); @@ -72,6 +69,13 @@ default boolean test(@NotNull FluidStack stack) { */ void setCanContain(@NotNull FluidAttribute attribute, boolean canContain); + /** + * Set the container as able to contain a list of attributes + * + * @param attributes valid attributes this container can contain + */ + void setCanContain(@NotNull Object2BooleanMap attributes); + @NotNull @UnmodifiableView Collection<@NotNull FluidAttribute> getContainedAttributes(); @@ -79,27 +83,15 @@ default boolean test(@NotNull FluidStack stack) { /** * Append tooltips about containment info * - * @param tooltip the tooltip to append to - * @param showToolsInfo if the "hold shift" line should mention tool info - * @param showTemperatureInfo if the temperature information should be displayed + * @param tooltip the tooltip to append to */ - default void appendTooltips(@NotNull List tooltip, boolean showToolsInfo, boolean showTemperatureInfo) { - if (GTUtil.isShiftDown()) { - if (showTemperatureInfo) - tooltip.add(Component.translatable("gtceu.fluid_pipe.max_temperature", - FormattingUtil.formatNumbers(getMaxFluidTemperature()))); - if (isGasProof()) tooltip.add(Component.translatable("gtceu.fluid_pipe.gas_proof")); - else tooltip.add(Component.translatable("gtceu.fluid_pipe.not_gas_proof")); - if (isPlasmaProof()) tooltip.add(Component.translatable("gtceu.fluid_pipe.plasma_proof")); - if (isCryoProof()) tooltip.add(Component.translatable("gtceu.fluid_pipe.cryo_proof")); - getContainedAttributes().forEach(a -> a.appendContainerTooltips(tooltip::add)); - } else if (isGasProof() || isCryoProof() || isPlasmaProof() || !getContainedAttributes().isEmpty()) { - if (showToolsInfo) { - tooltip.add(Component.translatable("gtceu.tooltip.tool_fluid_hold_shift")); - } else { - tooltip.add(Component.translatable("gtceu.tooltip.fluid_pipe_hold_shift")); - } - } + default void appendTooltips(@NotNull List tooltip) { + tooltip.add(Component.translatable("gtceu.fluid_pipe.max_temperature", getMaxFluidTemperature())); + tooltip.add(Component.translatable("gtceu.fluid_pipe.min_temperature", getMinFluidTemperature())); + if (isGasProof()) tooltip.add(Component.translatable("gtceu.fluid_pipe.gas_proof")); + else tooltip.add(Component.translatable("gtceu.fluid_pipe.not_gas_proof")); + if (isPlasmaProof()) tooltip.add(Component.translatable("gtceu.fluid_pipe.plasma_proof")); + getContainedAttributes().forEach(a -> a.appendContainerTooltips(tooltip::add)); } /** @@ -112,17 +104,23 @@ default void appendTooltips(@NotNull List tooltip, boolean showToolsI /** * This is always checked, regardless of the contained fluid being a {@link IAttributedFluid} or not * - * @return whether this filter allows gases + * @return the minimum allowed temperature for a fluid */ - boolean isGasProof(); + int getMinFluidTemperature(); /** - * @return whether this filter allows cryogenic fluids + * This is always checked, regardless of the contained fluid being a {@link IAttributedFluid} or not + * + * @return whether this filter allows gases */ - boolean isCryoProof(); + default boolean isGasProof() { + return canContain(FluidState.GAS); + } /** * @return whether this filter allows plasmas */ - boolean isPlasmaProof(); + default boolean isPlasmaProof() { + return canContain(FluidState.PLASMA); + } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/capability/IThermalFluidHandlerItemStack.java b/src/main/java/com/gregtechceu/gtceu/api/capability/IThermalFluidHandlerItemStack.java index 79fde0ad45..62728ed083 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/capability/IThermalFluidHandlerItemStack.java +++ b/src/main/java/com/gregtechceu/gtceu/api/capability/IThermalFluidHandlerItemStack.java @@ -28,17 +28,8 @@ default boolean canFillFluidType(FluidStack stack) { var temp = fluidType.getTemperature(); if (temp > getMaxFluidTemperature()) return false; // fluids less than 120K are cryogenic - if (temp < 120 && !isCryoProof()) return false; + if (temp < getMinFluidTemperature()) return false; if (fluidType.isLighterThanAir() && !isGasProof()) return false; - - // TODO custom fluid - // for (RegistryEntry entry : GTRegistries.REGISTRATE.getAll(Registry.FLUID_REGISTRY)) { - // if (entry.get() == fluid) { - // FluidType fluidType = ((MaterialFluid) fluid).getFluidType(); - // if (fluidType == FluidTypes.ACID && !isAcidProof()) return false; - // if (fluidType == FluidTypes.PLASMA && !isPlasmaProof()) return false; - // } - // } return true; } @@ -52,23 +43,16 @@ default boolean canFillFluidType(FluidStack stack) { /** * This is always checked, regardless of the contained fluid being a {@link IAttributedFluid} or not * - * @return true if this fluid container allows gases, otherwise false - */ - boolean isGasProof(); - - /** - * @see FluidAttributes - * - * @return true if this fluid container allows acids, otherwise false + * @return the minimum allowed temperature for a fluid to be stored in this container */ - boolean isAcidProof(); + int getMinFluidTemperature(); /** - * @see FluidAttributes + * This is always checked, regardless of the contained fluid being a {@link IAttributedFluid} or not * - * @return true if this fluid container allows cryogenics, otherwise false + * @return true if this fluid container allows gases, otherwise false */ - boolean isCryoProof(); + boolean isGasProof(); /** * @see FluidAttributes diff --git a/src/main/java/com/gregtechceu/gtceu/api/capability/compat/EUToFEProvider.java b/src/main/java/com/gregtechceu/gtceu/api/capability/compat/EUToFEProvider.java index 7299d80b60..ede36eb1c2 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/capability/compat/EUToFEProvider.java +++ b/src/main/java/com/gregtechceu/gtceu/api/capability/compat/EUToFEProvider.java @@ -51,7 +51,7 @@ public GTEnergyWrapper(IEnergyStorage energyStorage) { } @Override - public long acceptEnergyFromNetwork(Direction facing, long voltage, long amperage) { + public long acceptEnergyFromNetwork(Direction facing, long voltage, long amperage, boolean simulate) { int receive = 0; // Try to use the internal buffer before consuming a new packet diff --git a/src/main/java/com/gregtechceu/gtceu/api/capability/data/IComputationDataAccess.java b/src/main/java/com/gregtechceu/gtceu/api/capability/data/IComputationDataAccess.java new file mode 100644 index 0000000000..22b9233edc --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/capability/data/IComputationDataAccess.java @@ -0,0 +1,13 @@ +package com.gregtechceu.gtceu.api.capability.data; + +import com.gregtechceu.gtceu.api.capability.data.query.DataAccessFormat; + +import org.jetbrains.annotations.NotNull; + +public interface IComputationDataAccess extends IHatchDataAccess { + + @Override + default @NotNull DataAccessFormat getFormat() { + return DataAccessFormat.COMPUTATION; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/capability/data/IComputationProvider.java b/src/main/java/com/gregtechceu/gtceu/api/capability/data/IComputationProvider.java new file mode 100644 index 0000000000..f4cdeb6967 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/capability/data/IComputationProvider.java @@ -0,0 +1,26 @@ +package com.gregtechceu.gtceu.api.capability.data; + +public interface IComputationProvider { + + /** + * Returns whether this provider supports bridging. If false, CWU will not be requested through + * network switches with multiple inputs. + * + * @return whether bridging is supported. + */ + boolean supportsBridging(); + + /** + * Called to supply CWU to a requester. + * + * @param requested the requested CWU + * @param simulate whether to simulate the request + * @return the amount of CWU supplied. + */ + long supplyCWU(long requested, boolean simulate); + + /** + * @return the maximum CWU that can be supplied in a single tick. + */ + long maxCWUt(); +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/capability/data/IComputationUser.java b/src/main/java/com/gregtechceu/gtceu/api/capability/data/IComputationUser.java new file mode 100644 index 0000000000..6602cb89e5 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/capability/data/IComputationUser.java @@ -0,0 +1,16 @@ +package com.gregtechceu.gtceu.api.capability.data; + +/** + * Used for {@link gregtech.api.capability.impl.ComputationRecipeLogic} + */ +public interface IComputationUser { + + /** + * Called to request CWU for recipe logic. + * + * @param requested the requested CWU + * @param simulate whether to simulate the request + * @return the amount of CWU supplied. + */ + long requestCWU(long requested, boolean simulate); +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/capability/data/IDataAccess.java b/src/main/java/com/gregtechceu/gtceu/api/capability/data/IDataAccess.java new file mode 100644 index 0000000000..ee749cb6d1 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/capability/data/IDataAccess.java @@ -0,0 +1,54 @@ +package com.gregtechceu.gtceu.api.capability.data; + +import com.gregtechceu.gtceu.api.capability.data.query.DataAccessFormat; +import com.gregtechceu.gtceu.api.capability.data.query.DataQueryObject; + +import org.jetbrains.annotations.NotNull; + +public interface IDataAccess { + + /** + * Queries this {@link IDataAccess} with the specified query. + * + * @param queryObject the object representing the query. + * @return if the query has been cancelled + */ + boolean accessData(@NotNull DataQueryObject queryObject); + + /** + * @return the {@link DataAccessFormat} this {@link IDataAccess} uses. + */ + @NotNull + DataAccessFormat getFormat(); + + /** + * @param queryObject a query object + * @return whether this {@link IDataAccess} supports the query object. + */ + default boolean supportsQuery(@NotNull DataQueryObject queryObject) { + return getFormat().supportsFormat(queryObject.getFormat()); + } + + /** + * Provides standardized logic for querying a collection of {@link IDataAccess}es. + * + * @param accesses the {@link IDataAccess}es to query. + * @param query the object representing the query. + * @return if the query has been cancelled + */ + static boolean accessData(@NotNull Iterable accesses, + @NotNull DataQueryObject query) { + boolean walk = false; + boolean cancelled = false; + for (IDataAccess access : accesses) { + if (query.traverseTo(access)) { + query.setShouldTriggerWalker(false); + cancelled = access.accessData(query); + if (!walk) walk = query.isShouldTriggerWalker(); + if (cancelled) break; + } + } + query.setShouldTriggerWalker(walk); + return cancelled; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/capability/data/IHatchDataAccess.java b/src/main/java/com/gregtechceu/gtceu/api/capability/data/IHatchDataAccess.java new file mode 100644 index 0000000000..91e5a0b227 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/capability/data/IHatchDataAccess.java @@ -0,0 +1,9 @@ +package com.gregtechceu.gtceu.api.capability.data; + +public interface IHatchDataAccess extends IDataAccess { + + /** + * @return if this hatch transmits data through cables + */ + boolean isTransmitter(); +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/capability/data/IStandardDataAccess.java b/src/main/java/com/gregtechceu/gtceu/api/capability/data/IStandardDataAccess.java new file mode 100644 index 0000000000..a44dd6b15c --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/capability/data/IStandardDataAccess.java @@ -0,0 +1,13 @@ +package com.gregtechceu.gtceu.api.capability.data; + +import com.gregtechceu.gtceu.api.capability.data.query.DataAccessFormat; + +import org.jetbrains.annotations.NotNull; + +public interface IStandardDataAccess extends IHatchDataAccess { + + @Override + default @NotNull DataAccessFormat getFormat() { + return DataAccessFormat.STANDARD; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/capability/data/query/ComputationQuery.java b/src/main/java/com/gregtechceu/gtceu/api/capability/data/query/ComputationQuery.java new file mode 100644 index 0000000000..7d52722734 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/capability/data/query/ComputationQuery.java @@ -0,0 +1,67 @@ +package com.gregtechceu.gtceu.api.capability.data.query; + +import com.gregtechceu.gtceu.api.capability.data.IComputationProvider; + +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import org.jetbrains.annotations.NotNull; + +import java.util.Set; + +public class ComputationQuery extends DataQueryObject implements IComputationQuery { + + private final Set providers; + + private boolean bridged = false; + private boolean foundUnbridgeable = false; + + public ComputationQuery() { + providers = new ObjectOpenHashSet<>(); + } + + @Override + @NotNull + public DataQueryFormat getFormat() { + return DataQueryFormat.COMPUTATION; + } + + @Override + public void setBridged() { + this.bridged = true; + } + + public boolean foundUnbridgeable() { + return foundUnbridgeable; + } + + @Override + public void registerProvider(IComputationProvider provider) { + if (bridged && !provider.supportsBridging()) { + foundUnbridgeable = true; + return; + } + providers.add(provider); + } + + @NotNull + public Set getProviders() { + return providers; + } + + public long requestCWU(long amount, boolean simulate) { + long remaining = amount; + for (IComputationProvider provider : getProviders()) { + remaining -= provider.supplyCWU(remaining, simulate); + assert remaining >= 0; + if (remaining == 0) return amount; + } + return amount - remaining; + } + + public long maxCWUt() { + long amount = 0; + for (IComputationProvider provider : getProviders()) { + amount += provider.maxCWUt(); + } + return amount; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/capability/data/query/DataAccessFormat.java b/src/main/java/com/gregtechceu/gtceu/api/capability/data/query/DataAccessFormat.java new file mode 100644 index 0000000000..069937c6a8 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/capability/data/query/DataAccessFormat.java @@ -0,0 +1,55 @@ +package com.gregtechceu.gtceu.api.capability.data.query; + +import net.minecraft.util.StringRepresentable; + +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Set; + +public final class DataAccessFormat implements StringRepresentable { + + public static final DataAccessFormat STANDARD = create("gtceu.data_format.access.standard", + DataQueryFormat.RECIPE); + public static final DataAccessFormat COMPUTATION = create("gtceu.data_format.access.computation", + DataQueryFormat.COMPUTATION); + + public static final DataAccessFormat UNIVERSAL = new DataAccessFormat("gtceu.data_format.access.universal", + null); + + private final Set supportedFormats; + + public static DataAccessFormat create(@NotNull String name, DataQueryFormat... allowedFormats) { + return new DataAccessFormat(name, new ObjectOpenHashSet<>(allowedFormats)); + } + + private final @NotNull String name; + + private DataAccessFormat(@NotNull String name, Set supportedFormats) { + this.name = name; + this.supportedFormats = supportedFormats; + } + + @Override + public @NotNull String getSerializedName() { + return name; + } + + @Contract("_ -> this") + public DataAccessFormat support(DataQueryFormat format) { + if (supportedFormats != null) this.supportedFormats.add(format); + return this; + } + + @Contract("_ -> this") + public DataAccessFormat notSupport(DataQueryFormat format) { + if (supportedFormats != null) this.supportedFormats.remove(format); + return this; + } + + public boolean supportsFormat(DataQueryFormat format) { + if (supportedFormats == null) return true; + else return supportedFormats.contains(format); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/capability/data/query/DataQueryFormat.java b/src/main/java/com/gregtechceu/gtceu/api/capability/data/query/DataQueryFormat.java new file mode 100644 index 0000000000..23d8728cc5 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/capability/data/query/DataQueryFormat.java @@ -0,0 +1,26 @@ +package com.gregtechceu.gtceu.api.capability.data.query; + +import net.minecraft.util.StringRepresentable; + +import org.jetbrains.annotations.NotNull; + +public final class DataQueryFormat implements StringRepresentable { + + public static final DataQueryFormat RECIPE = create("gtceu.data_format.query.recipe"); + public static final DataQueryFormat COMPUTATION = create("gtceu.data_format.query.computation"); + + public static DataQueryFormat create(@NotNull String name) { + return new DataQueryFormat(name); + } + + private final @NotNull String name; + + private DataQueryFormat(@NotNull String name) { + this.name = name; + } + + @Override + public @NotNull String getSerializedName() { + return name; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/capability/data/query/DataQueryObject.java b/src/main/java/com/gregtechceu/gtceu/api/capability/data/query/DataQueryObject.java new file mode 100644 index 0000000000..b0f881dd28 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/capability/data/query/DataQueryObject.java @@ -0,0 +1,48 @@ +package com.gregtechceu.gtceu.api.capability.data.query; + +import com.gregtechceu.gtceu.api.capability.data.IDataAccess; + +import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public abstract class DataQueryObject { + + private static int ID = 0; + + private final int id; + + @Getter + @Setter + private boolean shouldTriggerWalker = false; + + private final ReferenceOpenHashSet visited = new ReferenceOpenHashSet<>(); + + public DataQueryObject() { + this.id = ID++; + } + + /** + * Used to tell this query when it passes through a location during traversal, + * know if the location supports this query, + * and know if this query has already visited the location. + * + * @param location the location next on traversal + * @return whether the location is not null, supports this query, and has not yet been visited. + */ + @Contract("null -> false") + public final boolean traverseTo(@Nullable IDataAccess location) { + return location != null && location.supportsQuery(this) && this.visited.add(location); + } + + @NotNull + public abstract DataQueryFormat getFormat(); + + @Override + public int hashCode() { + return id; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/capability/data/query/IBridgeable.java b/src/main/java/com/gregtechceu/gtceu/api/capability/data/query/IBridgeable.java new file mode 100644 index 0000000000..6747257a32 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/capability/data/query/IBridgeable.java @@ -0,0 +1,9 @@ +package com.gregtechceu.gtceu.api.capability.data.query; + +public interface IBridgeable { + + /** + * Called when a query has traversed a multiblock with more than one reception point and continued onward. + */ + void setBridged(); +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/capability/data/query/IComputationQuery.java b/src/main/java/com/gregtechceu/gtceu/api/capability/data/query/IComputationQuery.java new file mode 100644 index 0000000000..247d2f3bd4 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/capability/data/query/IComputationQuery.java @@ -0,0 +1,8 @@ +package com.gregtechceu.gtceu.api.capability.data.query; + +import com.gregtechceu.gtceu.api.capability.data.IComputationProvider; + +public interface IComputationQuery extends IBridgeable { + + void registerProvider(IComputationProvider provider); +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/capability/data/query/RecipeDataQuery.java b/src/main/java/com/gregtechceu/gtceu/api/capability/data/query/RecipeDataQuery.java new file mode 100644 index 0000000000..edb450f3f9 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/capability/data/query/RecipeDataQuery.java @@ -0,0 +1,32 @@ +package com.gregtechceu.gtceu.api.capability.data.query; + +import com.gregtechceu.gtceu.api.recipe.GTRecipe; + +import lombok.Getter; +import org.jetbrains.annotations.NotNull; + +public class RecipeDataQuery extends DataQueryObject { + + @Getter + private final GTRecipe recipe; + + private boolean found = false; + + public RecipeDataQuery(GTRecipe recipe) { + this.recipe = recipe; + } + + @Override + public @NotNull DataQueryFormat getFormat() { + return DataQueryFormat.RECIPE; + } + + public void setFound() { + this.found = true; + this.setShouldTriggerWalker(true); + } + + public boolean isFound() { + return found; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/capability/forge/GTCapability.java b/src/main/java/com/gregtechceu/gtceu/api/capability/forge/GTCapability.java index 89535cd5ca..8405be0876 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/capability/forge/GTCapability.java +++ b/src/main/java/com/gregtechceu/gtceu/api/capability/forge/GTCapability.java @@ -1,6 +1,9 @@ package com.gregtechceu.gtceu.api.capability.forge; import com.gregtechceu.gtceu.api.capability.*; +import com.gregtechceu.gtceu.api.capability.data.IComputationProvider; +import com.gregtechceu.gtceu.api.capability.data.IComputationUser; +import com.gregtechceu.gtceu.api.capability.data.IDataAccess; import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMaintenanceMachine; import com.gregtechceu.gtceu.api.machine.trait.RecipeLogic; @@ -33,11 +36,13 @@ public class GTCapability { .get(new CapabilityToken<>() {}); public static final Capability CAPABILITY_MAINTENANCE_MACHINE = CapabilityManager .get(new CapabilityToken<>() {}); - public static final Capability CAPABILITY_LASER = CapabilityManager + public static final Capability CAPABILITY_LASER = CapabilityManager .get(new CapabilityToken<>() {}); - public static final Capability CAPABILITY_COMPUTATION_PROVIDER = CapabilityManager + public static final Capability CAPABILITY_COMPUTATION_PROVIDER = CapabilityManager .get(new CapabilityToken<>() {}); - public static final Capability CAPABILITY_DATA_ACCESS = CapabilityManager + public static final Capability CAPABILITY_COMPUTATION_USER = CapabilityManager + .get(new CapabilityToken<>() {}); + public static final Capability CAPABILITY_DATA_ACCESS = CapabilityManager .get(new CapabilityToken<>() {}); public static final Capability CAPABILITY_HAZARD_CONTAINER = CapabilityManager .get(new CapabilityToken<>() {}); @@ -56,9 +61,10 @@ public static void register(RegisterCapabilitiesEvent event) { event.register(IElectricItem.class); event.register(ICleanroomReceiver.class); event.register(IMaintenanceMachine.class); - event.register(ILaserContainer.class); - event.register(IOpticalComputationProvider.class); - event.register(IDataAccessHatch.class); + event.register(ILaserRelay.class); + event.register(IComputationProvider.class); + event.register(IComputationUser.class); + event.register(IDataAccess.class); event.register(IMedicalConditionTracker.class); event.register(IHazardParticleContainer.class); } diff --git a/src/main/java/com/gregtechceu/gtceu/api/capability/recipe/CWURecipeCapability.java b/src/main/java/com/gregtechceu/gtceu/api/capability/recipe/CWURecipeCapability.java index 669f7f5a4b..4feb1f2c5a 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/capability/recipe/CWURecipeCapability.java +++ b/src/main/java/com/gregtechceu/gtceu/api/capability/recipe/CWURecipeCapability.java @@ -3,7 +3,7 @@ import com.gregtechceu.gtceu.api.recipe.GTRecipe; import com.gregtechceu.gtceu.api.recipe.content.Content; import com.gregtechceu.gtceu.api.recipe.content.ContentModifier; -import com.gregtechceu.gtceu.api.recipe.content.SerializerInteger; +import com.gregtechceu.gtceu.api.recipe.content.SerializerLong; import com.lowdragmc.lowdraglib.gui.widget.LabelWidget; import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup; @@ -18,21 +18,21 @@ * @date 2023/2/20 * @implNote ItemRecipeCapability */ -public class CWURecipeCapability extends RecipeCapability { +public class CWURecipeCapability extends RecipeCapability { public final static CWURecipeCapability CAP = new CWURecipeCapability(); protected CWURecipeCapability() { - super("cwu", 0xFFEEEE00, false, 3, SerializerInteger.INSTANCE); + super("cwu", 0xFFEEEE00, false, 3, SerializerLong.INSTANCE); } @Override - public Integer copyInner(Integer content) { + public Long copyInner(Long content) { return content; } @Override - public Integer copyWithModifier(Integer content, ContentModifier modifier) { + public Long copyWithModifier(Long content, ContentModifier modifier) { return modifier.apply(content); } @@ -40,7 +40,7 @@ public Integer copyWithModifier(Integer content, ContentModifier modifier) { public void addXEIInfo(WidgetGroup group, int xOffset, GTRecipe recipe, List contents, boolean perTick, boolean isInput, MutableInt yOffset) { if (perTick) { - int cwu = contents.stream().map(Content::getContent).mapToInt(CWURecipeCapability.CAP::of).sum(); + long cwu = contents.stream().map(Content::getContent).mapToLong(CWURecipeCapability.CAP::of).sum(); group.addWidget(new LabelWidget(3 - xOffset, yOffset.addAndGet(10), LocalizationUtils.format("gtceu.recipe.computation_per_tick", cwu))); } diff --git a/src/main/java/com/gregtechceu/gtceu/api/cover/CoverBehavior.java b/src/main/java/com/gregtechceu/gtceu/api/cover/CoverBehavior.java index f53466980a..f2356eef3e 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/cover/CoverBehavior.java +++ b/src/main/java/com/gregtechceu/gtceu/api/cover/CoverBehavior.java @@ -7,7 +7,7 @@ import com.gregtechceu.gtceu.api.item.tool.GTToolType; import com.gregtechceu.gtceu.api.item.tool.IToolGridHighlight; import com.gregtechceu.gtceu.api.transfer.fluid.IFluidHandlerModifiable; -import com.gregtechceu.gtceu.client.renderer.cover.ICoverRenderer; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRenderer; import com.lowdragmc.lowdraglib.gui.texture.ResourceTexture; import com.lowdragmc.lowdraglib.syncdata.IEnhancedManaged; @@ -27,9 +27,14 @@ import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.BlockHitResult; -import net.minecraftforge.items.IItemHandlerModifiable; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.util.LazyOptional; +import net.minecraftforge.items.IItemHandler; import lombok.Getter; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; @@ -61,6 +66,9 @@ public abstract class CoverBehavior implements IEnhancedManaged, IToolGridHighli @Persisted protected int redstoneSignalOutput = 0; + @OnlyIn(Dist.CLIENT) + protected @Nullable CoverRenderer renderer; + public CoverBehavior(CoverDefinition definition, ICoverable coverHolder, Direction attachedSide) { this.coverDefinition = definition; this.coverHolder = coverHolder; @@ -94,7 +102,7 @@ public void onChanged() { * * @return true if cover can be attached, false otherwise */ - public boolean canAttach() { + public boolean canAttach(@NotNull ICoverable coverable, @NotNull Direction side) { return true; } @@ -141,10 +149,20 @@ public void setRedstoneSignalOutput(int redstoneSignalOutput) { coverHolder.markDirty(); } + /** + * @return if the Cover can connect to redstone + */ public boolean canConnectRedstone() { return false; } + /** + * Called when the redstone input signal changes. + * + * @param redstone the new signal value + */ + public void onRedstoneInputSignalChange(int redstone) {} + ////////////////////////////////////// // ******* Interaction *******// ////////////////////////////////////// @@ -177,10 +195,21 @@ public boolean shouldRenderPlate() { return true; } - public ICoverRenderer getCoverRenderer() { - return coverDefinition.getCoverRenderer(); + /** + * @return if the pipe this cover is placed on should always render a connection to the cover + */ + public boolean forcePipeRenderConnection() { + return true; } + @OnlyIn(Dist.CLIENT) + public @NotNull CoverRenderer getRenderer() { + if (renderer == null) renderer = buildRenderer(); + return renderer; + } + + protected abstract CoverRenderer buildRenderer(); + public @Nullable IFancyConfigurator getConfigurator() { return null; } @@ -217,7 +246,7 @@ public BlockState getAppearance(BlockState sourceState, BlockPos sourcePos) { ////////////////////////////////////// @Nullable - public IItemHandlerModifiable getItemHandlerCap(IItemHandlerModifiable defaultValue) { + public IItemHandler getItemHandlerCap(IItemHandler defaultValue) { return defaultValue; } @@ -225,4 +254,16 @@ public IItemHandlerModifiable getItemHandlerCap(IItemHandlerModifiable defaultVa public IFluidHandlerModifiable getFluidHandlerCap(IFluidHandlerModifiable defaultValue) { return defaultValue; } + + /** + * Will be called for each capability request to the CoverableView + * Cover can override CoverableView capabilities, modify their values, or deny accessing them + * + * @param capability the requested Capability + * @param defaultValue value of the capability from CoverableView itself + * @return the resulting capability the caller will receive + */ + public LazyOptional getCapability(@NotNull Capability capability, LazyOptional defaultValue) { + return defaultValue; + } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/cover/CoverDefinition.java b/src/main/java/com/gregtechceu/gtceu/api/cover/CoverDefinition.java index 30fb5b9177..c895cfcc71 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/cover/CoverDefinition.java +++ b/src/main/java/com/gregtechceu/gtceu/api/cover/CoverDefinition.java @@ -1,7 +1,6 @@ package com.gregtechceu.gtceu.api.cover; import com.gregtechceu.gtceu.api.capability.ICoverable; -import com.gregtechceu.gtceu.client.renderer.cover.ICoverRenderer; import net.minecraft.core.Direction; import net.minecraft.resources.ResourceLocation; @@ -23,16 +22,13 @@ public interface TieredCoverBehaviourProvider { @Getter private final ResourceLocation id; private final CoverBehaviourProvider behaviorCreator; - @Getter - private final ICoverRenderer coverRenderer; - public CoverDefinition(ResourceLocation id, CoverBehaviourProvider behaviorCreator, ICoverRenderer coverRenderer) { + public CoverDefinition(ResourceLocation id, CoverBehaviourProvider behaviorCreator) { this.behaviorCreator = behaviorCreator; this.id = id; - this.coverRenderer = coverRenderer; } - public CoverBehavior createCoverBehavior(ICoverable metaTileEntity, Direction side) { - return behaviorCreator.create(this, metaTileEntity, side); + public CoverBehavior createCoverBehavior(ICoverable metaBlockEntity, Direction side) { + return behaviorCreator.create(this, metaBlockEntity, side); } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/cover/filter/CoverWithFluidFilter.java b/src/main/java/com/gregtechceu/gtceu/api/cover/filter/CoverWithFluidFilter.java new file mode 100644 index 0000000000..9e0241ae35 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/cover/filter/CoverWithFluidFilter.java @@ -0,0 +1,18 @@ +package com.gregtechceu.gtceu.api.cover.filter; + +import com.gregtechceu.gtceu.common.cover.data.FilterMode; +import com.gregtechceu.gtceu.common.cover.data.ManualIOMode; + +import net.minecraftforge.fluids.FluidStack; + +import org.jetbrains.annotations.NotNull; + +public interface CoverWithFluidFilter { + + @NotNull + FilterHandler getFilterHandler(); + + FilterMode getFilterMode(); + + ManualIOMode getManualIOMode(); +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/cover/filter/CoverWithItemFilter.java b/src/main/java/com/gregtechceu/gtceu/api/cover/filter/CoverWithItemFilter.java new file mode 100644 index 0000000000..75821071c6 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/cover/filter/CoverWithItemFilter.java @@ -0,0 +1,18 @@ +package com.gregtechceu.gtceu.api.cover.filter; + +import com.gregtechceu.gtceu.common.cover.data.FilterMode; +import com.gregtechceu.gtceu.common.cover.data.ManualIOMode; + +import net.minecraft.world.item.ItemStack; + +import org.jetbrains.annotations.NotNull; + +public interface CoverWithItemFilter { + + @NotNull + FilterHandler getFilterHandler(); + + FilterMode getFilterMode(); + + ManualIOMode getManualIOMode(); +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/cover/filter/Filter.java b/src/main/java/com/gregtechceu/gtceu/api/cover/filter/Filter.java index a97ab05f7c..853d7bdf1c 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/cover/filter/Filter.java +++ b/src/main/java/com/gregtechceu/gtceu/api/cover/filter/Filter.java @@ -1,21 +1,30 @@ package com.gregtechceu.gtceu.api.cover.filter; +import com.gregtechceu.gtceu.common.cover.filter.MatchResult; + import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup; import net.minecraft.nbt.CompoundTag; +import net.minecraft.util.StringRepresentable; + +import lombok.RequiredArgsConstructor; import java.util.function.Consumer; +import java.util.function.Function; import java.util.function.Predicate; +import java.util.function.Supplier; /** * @author KilaBash * @date 2023/3/14 * @implNote Filter */ -public interface Filter> extends Predicate { +public interface Filter> extends Predicate, Function { WidgetGroup openConfigurator(int x, int y); + void loadFilter(CompoundTag tag); + CompoundTag saveFilter(); void setOnUpdated(Consumer onUpdated); @@ -23,4 +32,73 @@ public interface Filter> extends Predicate { default boolean isBlackList() { return false; } + + default void setBlackList(boolean blackList) {} + + int getMaxTransferSize(); + + void setMaxTransferSize(int maxTransferSize); + + default int getTransferLimit(int slot, int transferSize) { + return transferSize; + } + + default int getTransferLimit(int slot) { + return getMaxTransferSize(); + } + + default int getTransferLimit(T stack, int transferSize) { + return 0; + } + + default int getTransferLimit(T stack) { + return 0; + } + + default boolean testGeneric(Object test) { + return this.test((T) test); + } + + default MatchResult match(T test) { + return this.apply(test); + } + + default MatchResult matchGeneric(Object test) { + return this.apply((T) test); + } + + @RequiredArgsConstructor + enum FilterType implements StringRepresentable { + + ITEM("item", SimpleItemFilter::new), + ITEM_TAG("item_tag", TagItemFilter::new), + FLUID("fluid", SimpleFluidFilter::new), + FLUID_TAG("fluid_tag", TagFluidFilter::new); + + private final String name; + private final Supplier> constructor; + + @Override + public String getSerializedName() { + return name; + } + + public static FilterType getByName(String name) { + return switch (name) { + case "item" -> FilterType.ITEM; + case "item_tag" -> FilterType.ITEM_TAG; + case "fluid" -> FilterType.FLUID; + case "fluid_tag" -> FilterType.FLUID_TAG; + default -> throw new IllegalStateException("Unexpected value: " + name); + }; + } + + public static Filter makeNew(FilterType filterType) { + return filterType.constructor.get(); + } + + public static Filter makeNew(String filterType) { + return getByName(filterType).constructor.get(); + } + } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/cover/filter/FilterHandler.java b/src/main/java/com/gregtechceu/gtceu/api/cover/filter/FilterHandler.java index ca2f9dd9ad..feddebd598 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/cover/filter/FilterHandler.java +++ b/src/main/java/com/gregtechceu/gtceu/api/cover/filter/FilterHandler.java @@ -51,7 +51,7 @@ public FilterHandler(IEnhancedManaged container) { this.container = container; } - protected abstract F loadFilter(ItemStack filterItem); + public abstract F loadFilter(ItemStack filterItem); protected abstract F getEmptyFilter(); @@ -146,21 +146,25 @@ private void updateFilter() { private void loadFilterFromItem() { if (!this.filterItem.isEmpty()) { - this.filter = loadFilter(this.filterItem); - filter.setOnUpdated(this.onFilterUpdated); - if (filter instanceof SmartItemFilter smart && - container instanceof CoverBehavior cover && - cover.coverHolder instanceof MachineCoverContainer mcc) { - var machine = MetaMachine.getMachine(mcc.getLevel(), mcc.getPos()); - if (machine != null) { - smart.setModeFromMachine(machine.getDefinition().getName()); - } - } - this.onFilterLoaded.accept(this.filter); + setFilter(loadFilter(this.filterItem)); } updateFilterGroupUI(); } + public void setFilter(@Nullable F filter) { + this.filter = filter; + this.filter.setOnUpdated(this.onFilterUpdated); + if (this.filter instanceof SmartItemFilter smart && + container instanceof CoverBehavior cover && + cover.coverHolder instanceof MachineCoverContainer mcc) { + var machine = MetaMachine.getMachine(mcc.getLevel(), mcc.getPos()); + if (machine != null) { + smart.setModeFromMachine(machine.getDefinition().getName()); + } + } + this.onFilterLoaded.accept(this.filter); + } + private void updateFilterGroupUI() { if (this.filterGroup == null) return; diff --git a/src/main/java/com/gregtechceu/gtceu/api/cover/filter/FilterHandlers.java b/src/main/java/com/gregtechceu/gtceu/api/cover/filter/FilterHandlers.java index d2ccc7aa08..28ea2175c3 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/cover/filter/FilterHandlers.java +++ b/src/main/java/com/gregtechceu/gtceu/api/cover/filter/FilterHandlers.java @@ -11,7 +11,7 @@ static FilterHandler item(IEnhancedManaged container) { return new FilterHandler<>(container) { @Override - protected ItemFilter loadFilter(ItemStack filterItem) { + public ItemFilter loadFilter(ItemStack filterItem) { return ItemFilter.loadFilter(filterItem); } @@ -31,7 +31,7 @@ static FilterHandler fluid(IEnhancedManaged container) return new FilterHandler<>(container) { @Override - protected FluidFilter loadFilter(ItemStack filterItem) { + public FluidFilter loadFilter(ItemStack filterItem) { return FluidFilter.loadFilter(filterItem); } diff --git a/src/main/java/com/gregtechceu/gtceu/api/cover/filter/FluidFilter.java b/src/main/java/com/gregtechceu/gtceu/api/cover/filter/FluidFilter.java index bfa7de498e..3046a5049c 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/cover/filter/FluidFilter.java +++ b/src/main/java/com/gregtechceu/gtceu/api/cover/filter/FluidFilter.java @@ -1,5 +1,7 @@ package com.gregtechceu.gtceu.api.cover.filter; +import com.gregtechceu.gtceu.common.cover.filter.MatchResult; + import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup; import net.minecraft.nbt.CompoundTag; @@ -48,6 +50,11 @@ default boolean supportsAmounts() { */ FluidFilter EMPTY = new FluidFilter() { + @Override + public MatchResult apply(FluidStack fluidStack) { + return MatchResult.ANY; + } + @Override public boolean test(FluidStack fluidStack) { return true; @@ -63,6 +70,11 @@ public WidgetGroup openConfigurator(int x, int y) { throw new NotImplementedException("Not available for empty fluid filter"); } + @Override + public void loadFilter(CompoundTag tag) { + throw new NotImplementedException("Not available for empty fluid filter"); + } + @Override public CompoundTag saveFilter() { throw new NotImplementedException("Not available for empty fluid filter"); @@ -72,5 +84,13 @@ public CompoundTag saveFilter() { public void setOnUpdated(Consumer onUpdated) { throw new NotImplementedException("Not available for empty fluid filter"); } + + @Override + public int getMaxTransferSize() { + return 0; + } + + @Override + public void setMaxTransferSize(int maxTransferSize) {} }; } diff --git a/src/main/java/com/gregtechceu/gtceu/api/cover/filter/ItemFilter.java b/src/main/java/com/gregtechceu/gtceu/api/cover/filter/ItemFilter.java index 8c59307724..65a05fc148 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/cover/filter/ItemFilter.java +++ b/src/main/java/com/gregtechceu/gtceu/api/cover/filter/ItemFilter.java @@ -1,5 +1,7 @@ package com.gregtechceu.gtceu.api.cover.filter; +import com.gregtechceu.gtceu.common.cover.filter.MatchResult; + import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup; import net.minecraft.nbt.CompoundTag; @@ -41,12 +43,21 @@ default boolean supportsAmounts() { return !isBlackList(); } + default int getMaxStackSize() { + return 1; + } + /** * An empty item filter that allows all items.
* ONLY TO BE USED FOR ITEM MATCHING! All other functionality will throw an exception. */ ItemFilter EMPTY = new ItemFilter() { + @Override + public MatchResult apply(ItemStack stack) { + return MatchResult.ANY; + } + @Override public int testItemCount(ItemStack itemStack) { return Integer.MAX_VALUE; @@ -62,6 +73,11 @@ public WidgetGroup openConfigurator(int x, int y) { throw new NotImplementedException("Not available for empty item filter"); } + @Override + public void loadFilter(CompoundTag tag) { + throw new NotImplementedException("Not available for empty item filter"); + } + @Override public CompoundTag saveFilter() { throw new NotImplementedException("Not available for empty item filter"); @@ -71,5 +87,13 @@ public CompoundTag saveFilter() { public void setOnUpdated(Consumer onUpdated) { throw new NotImplementedException("Not available for empty item filter"); } + + @Override + public int getMaxTransferSize() { + return 0; + } + + @Override + public void setMaxTransferSize(int maxTransferSize) {} }; } diff --git a/src/main/java/com/gregtechceu/gtceu/api/cover/filter/SimpleFluidFilter.java b/src/main/java/com/gregtechceu/gtceu/api/cover/filter/SimpleFluidFilter.java index 124d1096e2..610e353542 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/cover/filter/SimpleFluidFilter.java +++ b/src/main/java/com/gregtechceu/gtceu/api/cover/filter/SimpleFluidFilter.java @@ -4,6 +4,7 @@ import com.gregtechceu.gtceu.api.gui.widget.ScrollablePhantomFluidWidget; import com.gregtechceu.gtceu.api.gui.widget.ToggleButtonWidget; import com.gregtechceu.gtceu.api.transfer.fluid.CustomFluidTank; +import com.gregtechceu.gtceu.common.cover.filter.MatchResult; import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup; @@ -11,6 +12,7 @@ import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; import net.minecraft.nbt.Tag; +import net.minecraft.util.Mth; import net.minecraft.world.item.ItemStack; import net.minecraftforge.fluids.FluidStack; @@ -73,8 +75,19 @@ public void setOnUpdated(Consumer onUpdated) { }; } + @Override + public void loadFilter(CompoundTag tag) { + this.isBlackList = tag.getBoolean("isBlackList"); + this.ignoreNbt = tag.getBoolean("matchNbt"); + var list = tag.getList("matches", Tag.TAG_COMPOUND); + for (int i = 0; i < list.size(); i++) { + this.matches[i] = FluidStack.loadFluidStackFromNBT((CompoundTag) list.get(i)); + } + } + public CompoundTag saveFilter() { var tag = new CompoundTag(); + tag.putString("type", FilterType.FLUID.getSerializedName()); tag.putBoolean("isBlackList", isBlackList); tag.putBoolean("matchNbt", ignoreNbt); var list = new ListTag(); @@ -90,6 +103,31 @@ public void setBlackList(boolean blackList) { onUpdated.accept(this); } + public void setMaxTransferSize(int transferRate) { + transferRate = Mth.clamp(transferRate, 1, Integer.MAX_VALUE); + if (this.maxStackSize != transferRate) { + this.maxStackSize = transferRate; + onUpdated.accept(this); + } + } + + public int getMaxTransferSize() { + return isBlackList ? 1 : (int) this.maxStackSize; + } + + @Override + public int getTransferLimit(FluidStack fluidStack, int transferSize) { + long limit = 0; + + for (int i = 0; i < this.matches.length; i++) { + var fluid = this.matches[i]; + if (fluid != null && fluid.isFluidEqual(fluidStack)) { + limit = fluid.getAmount(); + } + } + return isBlackList ? transferSize : (int) limit; + } + public void setIgnoreNbt(boolean ingoreNbt) { this.ignoreNbt = ingoreNbt; onUpdated.accept(this); @@ -180,4 +218,19 @@ public void setMaxStackSize(int maxStackSize) { match.setAmount(Math.min(match.getAmount(), maxStackSize)); } } + + @Override + public MatchResult apply(FluidStack fluidStack) { + int index = -1; + FluidStack returnable = FluidStack.EMPTY; + for (int i = 0; i < matches.length; i++) { + var fluid = matches[i]; + if (fluid != null && fluid.isFluidEqual(fluidStack)) { + index = i; + returnable = fluid.copy(); + break; + } + } + return MatchResult.create(index != -1, returnable, index); + } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/cover/filter/SimpleItemFilter.java b/src/main/java/com/gregtechceu/gtceu/api/cover/filter/SimpleItemFilter.java index 16c7009f03..d916b9ff20 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/cover/filter/SimpleItemFilter.java +++ b/src/main/java/com/gregtechceu/gtceu/api/cover/filter/SimpleItemFilter.java @@ -4,6 +4,7 @@ import com.gregtechceu.gtceu.api.gui.widget.PhantomSlotWidget; import com.gregtechceu.gtceu.api.gui.widget.ToggleButtonWidget; import com.gregtechceu.gtceu.api.transfer.item.CustomItemStackHandler; +import com.gregtechceu.gtceu.common.cover.filter.MatchResult; import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup; @@ -11,6 +12,7 @@ import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; import net.minecraft.nbt.Tag; +import net.minecraft.util.Mth; import net.minecraft.world.item.ItemStack; import net.minecraftforge.items.ItemHandlerHelper; @@ -72,8 +74,19 @@ public void setOnUpdated(Consumer onUpdated) { }; } + @Override + public void loadFilter(CompoundTag tag) { + this.isBlackList = tag.getBoolean("isBlackList"); + this.ignoreNbt = tag.getBoolean("matchNbt"); + var list = tag.getList("matches", Tag.TAG_COMPOUND); + for (int i = 0; i < list.size(); i++) { + this.matches[i] = ItemStack.of((CompoundTag) list.get(i)); + } + } + public CompoundTag saveFilter() { var tag = new CompoundTag(); + tag.putString("type", FilterType.ITEM.getSerializedName()); tag.putBoolean("isBlackList", isBlackList); tag.putBoolean("matchNbt", ignoreNbt); var list = new ListTag(); @@ -89,11 +102,31 @@ public void setBlackList(boolean blackList) { onUpdated.accept(this); } + @Override + public int getMaxTransferSize() { + return maxStackSize; + } + + @Override + public void setMaxTransferSize(int maxTransferSize) { + maxTransferSize = Mth.clamp(maxTransferSize, 1, Integer.MAX_VALUE); + if (this.maxStackSize != maxTransferSize) { + this.maxStackSize = maxTransferSize; + onUpdated.accept(this); + } + } + public void setIgnoreNbt(boolean ingoreNbt) { this.ignoreNbt = ingoreNbt; onUpdated.accept(this); } + @Override + public int getTransferLimit(ItemStack stack, int transferSize) { + int matchedSlot = itemFilterMatch(ignoreNbt, stack); + return getTransferLimit(matchedSlot, transferSize); + } + public WidgetGroup openConfigurator(int x, int y) { WidgetGroup group = new WidgetGroup(x, y, 18 * 3 + 25, 18 * 3); // 80 55 for (int i = 0; i < 3; i++) { @@ -148,6 +181,12 @@ public int testItemCount(ItemStack itemStack) { return totalItemCount; } + @Override + public MatchResult apply(ItemStack stack) { + int matchedSlot = itemFilterMatch(this.ignoreNbt, stack); + return MatchResult.create(matchedSlot != -1 == !isBlackList(), matches[matchedSlot], matchedSlot); + } + public int getTotalConfiguredItemCount(ItemStack itemStack) { int totalCount = 0; @@ -169,4 +208,22 @@ public void setMaxStackSize(int maxStackSize) { match.setCount(Math.min(match.getCount(), maxStackSize)); } } + + public int itemFilterMatch(boolean ignoreNBTData, ItemStack itemStack) { + for (int i = 0; i < matches.length; i++) { + ItemStack filterStack = matches[i]; + if (!filterStack.isEmpty() && areItemsEqual(ignoreNBTData, filterStack, itemStack)) { + return i; + } + } + return -1; + } + + private static boolean areItemsEqual(boolean ignoreNBTData, + ItemStack filterStack, ItemStack itemStack) { + if (!ItemStack.isSameItem(filterStack, itemStack)) { + return false; + } + return ignoreNBTData || ItemStack.isSameItemSameTags(filterStack, itemStack); + } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/cover/filter/SmartItemFilter.java b/src/main/java/com/gregtechceu/gtceu/api/cover/filter/SmartItemFilter.java index dcd3d916dc..0ebaa44ed6 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/cover/filter/SmartItemFilter.java +++ b/src/main/java/com/gregtechceu/gtceu/api/cover/filter/SmartItemFilter.java @@ -5,6 +5,7 @@ import com.gregtechceu.gtceu.api.recipe.GTRecipeType; import com.gregtechceu.gtceu.api.recipe.content.Content; import com.gregtechceu.gtceu.api.recipe.lookup.GTRecipeLookup; +import com.gregtechceu.gtceu.common.cover.filter.MatchResult; import com.gregtechceu.gtceu.common.data.GTRecipeTypes; import com.gregtechceu.gtceu.utils.ItemStackHashStrategy; @@ -41,6 +42,11 @@ private static SmartItemFilter loadFilter(CompoundTag tag, Consumer return handler; } + @Override + public void loadFilter(CompoundTag tag) { + this.filterMode = SmartFilteringMode.VALUES[tag.getInt("filterMode")]; + } + @Override public void setOnUpdated(Consumer onUpdated) { this.onUpdated = filter -> { @@ -49,6 +55,14 @@ public void setOnUpdated(Consumer onUpdated) { }; } + @Override + public int getMaxTransferSize() { + return 0; + } + + @Override + public void setMaxTransferSize(int maxTransferSize) {} + @Override public CompoundTag saveFilter() { var tag = new CompoundTag(); @@ -103,6 +117,11 @@ public void setModeFromMachine(String machineName) { } } + @Override + public MatchResult apply(ItemStack stack) { + return null; + } + @MethodsReturnNonnullByDefault private enum SmartFilteringMode implements EnumSelectorWidget.SelectableEnum { diff --git a/src/main/java/com/gregtechceu/gtceu/api/cover/filter/TagFilter.java b/src/main/java/com/gregtechceu/gtceu/api/cover/filter/TagFilter.java index 11dd35cc9f..b10057bfe0 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/cover/filter/TagFilter.java +++ b/src/main/java/com/gregtechceu/gtceu/api/cover/filter/TagFilter.java @@ -40,6 +40,11 @@ public abstract class TagFilter> implements Filter implement private final Object2BooleanMap cache = new Object2BooleanOpenHashMap<>(); + @Getter + protected long maxStackSize = 1L; + protected TagFluidFilter() {} public static TagFluidFilter loadFilter(ItemStack itemStack) { @@ -37,6 +43,27 @@ private static TagFluidFilter loadFilter(CompoundTag tag, Consumer return handler; } + @Override + public CompoundTag saveFilter() { + CompoundTag tag = super.saveFilter(); + tag.putString("type", FilterType.FLUID_TAG.getSerializedName()); + return tag; + } + + @Override + public int getMaxTransferSize() { + return (int) maxStackSize; + } + + @Override + public void setMaxTransferSize(int transferRate) { + transferRate = Mth.clamp(transferRate, 1, Integer.MAX_VALUE); + if (this.maxStackSize != transferRate) { + this.maxStackSize = transferRate; + onUpdated.accept(this); + } + } + public void setOreDict(String oreDict) { cache.clear(); super.setOreDict(oreDict); @@ -63,4 +90,10 @@ public int testFluidAmount(FluidStack fluidStack) { public boolean supportsAmounts() { return false; } + + @Override + public MatchResult apply(FluidStack fluidStack) { + var match = TagExprFilter.tagsMatch(matchExpr, fluidStack); + return MatchResult.create(match != isBlackList(), match ? fluidStack.copy() : FluidStack.EMPTY, -1); + } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/cover/filter/TagItemFilter.java b/src/main/java/com/gregtechceu/gtceu/api/cover/filter/TagItemFilter.java index f50bb95b75..0dce8a995d 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/cover/filter/TagItemFilter.java +++ b/src/main/java/com/gregtechceu/gtceu/api/cover/filter/TagItemFilter.java @@ -1,13 +1,16 @@ package com.gregtechceu.gtceu.api.cover.filter; +import com.gregtechceu.gtceu.common.cover.filter.MatchResult; import com.gregtechceu.gtceu.utils.TagExprFilter; import net.minecraft.nbt.CompoundTag; +import net.minecraft.util.Mth; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import it.unimi.dsi.fastutil.objects.Object2BooleanMap; import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap; +import lombok.Getter; import java.util.function.Consumer; @@ -20,6 +23,9 @@ public class TagItemFilter extends TagFilter implements I private final Object2BooleanMap cache = new Object2BooleanOpenHashMap<>(); + @Getter + protected int maxStackSize = 1; + protected TagItemFilter() {} public static TagItemFilter loadFilter(ItemStack itemStack) { @@ -36,6 +42,27 @@ private static TagItemFilter loadFilter(CompoundTag tag, Consumer it return handler; } + @Override + public CompoundTag saveFilter() { + CompoundTag tag = super.saveFilter(); + tag.putString("type", FilterType.FLUID_TAG.getSerializedName()); + return tag; + } + + @Override + public int getMaxTransferSize() { + return maxStackSize; + } + + @Override + public void setMaxTransferSize(int transferRate) { + transferRate = Mth.clamp(transferRate, 1, Integer.MAX_VALUE); + if (this.maxStackSize != transferRate) { + this.maxStackSize = transferRate; + onUpdated.accept(this); + } + } + public void setOreDict(String oreDict) { cache.clear(); super.setOreDict(oreDict); @@ -62,4 +89,10 @@ public int testItemCount(ItemStack itemStack) { public boolean supportsAmounts() { return false; } + + @Override + public MatchResult apply(ItemStack itemStack) { + var match = TagExprFilter.tagsMatch(matchExpr, itemStack); + return MatchResult.create(match != isBlackList(), match ? itemStack.copy() : ItemStack.EMPTY, -1); + } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/data/chemical/material/Material.java b/src/main/java/com/gregtechceu/gtceu/api/data/chemical/material/Material.java index 7c03e2c974..7bab51d96e 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/data/chemical/material/Material.java +++ b/src/main/java/com/gregtechceu/gtceu/api/data/chemical/material/Material.java @@ -11,12 +11,16 @@ import com.gregtechceu.gtceu.api.data.tag.TagUtil; import com.gregtechceu.gtceu.api.fluids.FluidBuilder; import com.gregtechceu.gtceu.api.fluids.FluidState; +import com.gregtechceu.gtceu.api.fluids.attribute.FluidAttributes; import com.gregtechceu.gtceu.api.fluids.store.FluidStorageKey; import com.gregtechceu.gtceu.api.fluids.store.FluidStorageKeys; import com.gregtechceu.gtceu.api.item.tool.MaterialToolTier; import com.gregtechceu.gtceu.api.registry.registrate.BuilderBase; import com.gregtechceu.gtceu.common.data.GTMaterials; import com.gregtechceu.gtceu.common.data.GTMedicalConditions; +import com.gregtechceu.gtceu.common.pipelike.handlers.properties.MaterialEnergyProperties; +import com.gregtechceu.gtceu.common.pipelike.handlers.properties.MaterialFluidProperties; +import com.gregtechceu.gtceu.common.pipelike.handlers.properties.MaterialItemProperties; import com.gregtechceu.gtceu.integration.kjs.helpers.MaterialStackWrapper; import com.gregtechceu.gtceu.utils.FormattingUtil; import com.gregtechceu.gtceu.utils.GTMath; @@ -1173,38 +1177,59 @@ public Builder addOreByproducts(Material... byproducts) { return this; } - public Builder cableProperties(long voltage, int amperage, int loss) { - cableProperties(voltage, amperage, loss, false); + private PipeNetProperties getOrCreatePipeNetProperties() { + if (properties.hasProperty(PropertyKey.PIPENET_PROPERTIES)) { + return properties.getProperty(PropertyKey.PIPENET_PROPERTIES); + } else { + PipeNetProperties prop = new PipeNetProperties(); + properties.setProperty(PropertyKey.PIPENET_PROPERTIES, prop); + return prop; + } + } + + public Builder cableProperties(long voltage, long amperage, long loss) { + getOrCreatePipeNetProperties().setProperty(MaterialEnergyProperties.create(voltage, amperage, loss)); return this; } - public Builder cableProperties(long voltage, int amperage, int loss, boolean isSuperCon) { - properties.ensureSet(PropertyKey.DUST); - properties.setProperty(PropertyKey.WIRE, new WireProperties(voltage, amperage, loss, isSuperCon)); + public Builder cableProperties(long voltage, long amperage, long loss, boolean superconductor) { + getOrCreatePipeNetProperties() + .setProperty(MaterialEnergyProperties.create(voltage, amperage, loss, superconductor)); return this; } - public Builder cableProperties(long voltage, int amperage, int loss, boolean isSuperCon, - int criticalTemperature) { - properties.ensureSet(PropertyKey.DUST); - properties.setProperty(PropertyKey.WIRE, - new WireProperties(voltage, amperage, loss, isSuperCon, criticalTemperature)); + public Builder fluidPipeProperties(int maxTemp, long throughput, boolean gasProof) { + getOrCreatePipeNetProperties().setProperty( + MaterialFluidProperties.createMax(throughput, maxTemp).setContain(FluidState.GAS, gasProof)); return this; } - public Builder fluidPipeProperties(int maxTemp, int throughput, boolean gasProof) { - return fluidPipeProperties(maxTemp, throughput, gasProof, false, false, false); + public Builder fluidPipeProperties(int maxTemp, long throughput, boolean gasProof, float priority) { + getOrCreatePipeNetProperties().setProperty(MaterialFluidProperties.createMax(throughput, maxTemp, priority) + .setContain(FluidState.GAS, gasProof)); + return this; } public Builder fluidPipeProperties(int maxTemp, int throughput, boolean gasProof, boolean acidProof, - boolean cryoProof, boolean plasmaProof) { - properties.setProperty(PropertyKey.FLUID_PIPE, - new FluidPipeProperties(maxTemp, throughput, gasProof, acidProof, cryoProof, plasmaProof)); + boolean plasmaProof) { + getOrCreatePipeNetProperties().setProperty( + MaterialFluidProperties.createMax(throughput, maxTemp).setContain(FluidState.GAS, gasProof) + .setContain(FluidAttributes.ACID, acidProof).setContain(FluidState.PLASMA, plasmaProof)); + return this; + } + + public Builder fluidPipeProperties(int maxTemp, int minTemp, int throughput, boolean gasProof, + boolean acidProof, + boolean plasmaProof) { + getOrCreatePipeNetProperties().setProperty(new MaterialFluidProperties(throughput, maxTemp, minTemp) + .setContain(FluidState.GAS, gasProof).setContain(FluidAttributes.ACID, acidProof) + .setContain(FluidState.PLASMA, plasmaProof)); return this; } public Builder itemPipeProperties(int priority, float stacksPerSec) { - properties.setProperty(PropertyKey.ITEM_PIPE, new ItemPipeProperties(priority, stacksPerSec)); + getOrCreatePipeNetProperties() + .setProperty(new MaterialItemProperties((long) (stacksPerSec * 16), priority)); return this; } diff --git a/src/main/java/com/gregtechceu/gtceu/api/data/chemical/material/properties/FluidPipeProperties.java b/src/main/java/com/gregtechceu/gtceu/api/data/chemical/material/properties/FluidPipeProperties.java deleted file mode 100644 index 47ddab6c2b..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/api/data/chemical/material/properties/FluidPipeProperties.java +++ /dev/null @@ -1,131 +0,0 @@ -package com.gregtechceu.gtceu.api.data.chemical.material.properties; - -import com.gregtechceu.gtceu.api.capability.IPropertyFluidFilter; -import com.gregtechceu.gtceu.api.fluids.FluidState; -import com.gregtechceu.gtceu.api.fluids.attribute.FluidAttribute; -import com.gregtechceu.gtceu.api.fluids.attribute.FluidAttributes; - -import it.unimi.dsi.fastutil.objects.Object2BooleanMap; -import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap; -import lombok.Getter; -import lombok.Setter; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.UnmodifiableView; - -import java.util.Collection; -import java.util.Objects; - -public class FluidPipeProperties implements IMaterialProperty, IPropertyFluidFilter { - - /** - * The maximum number of channels any fluid pipe can have - */ - public static final int MAX_PIPE_CHANNELS = 9; - - @Getter - @Setter - private int throughput; - @Getter - @Setter - private int channels; - @Getter - @Setter - private int maxFluidTemperature; - @Getter - @Setter - private boolean gasProof; - @Getter - @Setter - private boolean cryoProof; - @Getter - @Setter - private boolean plasmaProof; - - private final Object2BooleanMap containmentPredicate = new Object2BooleanOpenHashMap<>(); - - public FluidPipeProperties(int maxFluidTemperature, int throughput, boolean gasProof, boolean acidProof, - boolean cryoProof, boolean plasmaProof, int channels) { - this.maxFluidTemperature = maxFluidTemperature; - this.throughput = throughput; - this.gasProof = gasProof; - if (acidProof) setCanContain(FluidAttributes.ACID, true); - this.cryoProof = cryoProof; - this.plasmaProof = plasmaProof; - this.channels = channels; - } - - /** - * Default property constructor. - */ - public FluidPipeProperties(int maxFluidTemperature, int throughput, boolean gasProof, boolean acidProof, - boolean cryoProof, boolean plasmaProof) { - this(maxFluidTemperature, throughput, gasProof, acidProof, cryoProof, plasmaProof, 1); - } - - @Override - public void verifyProperty(MaterialProperties properties) { - if (!properties.hasProperty(PropertyKey.WOOD)) { - properties.ensureSet(PropertyKey.INGOT, true); - } - - if (properties.hasProperty(PropertyKey.ITEM_PIPE)) { - throw new IllegalStateException( - "Material " + properties.getMaterial() + - " has both Fluid and Item Pipe Property, which is not allowed!"); - } - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof FluidPipeProperties that)) return false; - return maxFluidTemperature == that.maxFluidTemperature && - throughput == that.throughput && gasProof == that.gasProof && channels == that.channels; - } - - @Override - public int hashCode() { - return Objects.hash(maxFluidTemperature, throughput, gasProof, channels); - } - - @Override - public String toString() { - return "FluidPipeProperties{" + - "maxFluidTemperature=" + maxFluidTemperature + - ", throughput=" + throughput + - ", gasProof=" + gasProof + - ", acidProof=" + isAcidProof() + - ", cryoProof=" + cryoProof + - ", plasmaProof=" + plasmaProof + - ", channels=" + channels + - '}'; - } - - @Override - public boolean canContain(@NotNull FluidState state) { - return switch (state) { - case LIQUID -> true; - case GAS -> gasProof; - case PLASMA -> plasmaProof; - }; - } - - public boolean isAcidProof() { - return canContain(FluidAttributes.ACID); - } - - @Override - public boolean canContain(@NotNull FluidAttribute attribute) { - return containmentPredicate.getBoolean(attribute); - } - - @Override - public void setCanContain(@NotNull FluidAttribute attribute, boolean canContain) { - containmentPredicate.put(attribute, canContain); - } - - @Override - public @NotNull @UnmodifiableView Collection<@NotNull FluidAttribute> getContainedAttributes() { - return containmentPredicate.keySet(); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/api/data/chemical/material/properties/ItemPipeProperties.java b/src/main/java/com/gregtechceu/gtceu/api/data/chemical/material/properties/ItemPipeProperties.java deleted file mode 100644 index acfb7a57d1..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/api/data/chemical/material/properties/ItemPipeProperties.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.gregtechceu.gtceu.api.data.chemical.material.properties; - -import java.util.Objects; - -public class ItemPipeProperties implements IMaterialProperty { - - /** - * Items will try to take the path with the lowest priority - */ - private int priority; - - /** - * rate in stacks per sec - */ - private float transferRate; - - public ItemPipeProperties(int priority, float transferRate) { - this.priority = priority; - this.transferRate = transferRate; - } - - /** - * Default property constructor. - */ - public ItemPipeProperties() { - this(1, 0.25f); - } - - /** - * Retrieves the priority of the item pipe - * - * @return The item pipe priority - */ - public int getPriority() { - return priority; - } - - /** - * Sets the Priority of the item pipe - */ - public void setPriority(int priority) { - this.priority = priority; - } - - /** - * Retrieve the transfer rate of the item pipe - * - * @return The transfer rate of the item pipe - */ - public float getTransferRate() { - return transferRate; - } - - /** - * Sets the transfer rate of the item pipe - * - * @param transferRate The transfer rate - */ - public void setTransferRate(float transferRate) { - this.transferRate = transferRate; - } - - @Override - public void verifyProperty(MaterialProperties properties) { - if (!properties.hasProperty(PropertyKey.WOOD)) { - properties.ensureSet(PropertyKey.INGOT, true); - } - - if (properties.hasProperty(PropertyKey.FLUID_PIPE)) { - throw new IllegalStateException( - "Material " + properties.getMaterial() + - " has both Fluid and Item Pipe Property, which is not allowed!"); - } - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - ItemPipeProperties that = (ItemPipeProperties) o; - return priority == that.priority && Float.compare(that.transferRate, transferRate) == 0; - } - - @Override - public int hashCode() { - return Objects.hash(priority, transferRate); - } - - @Override - public String toString() { - return "ItemPipeProperties{" + - "priority=" + priority + - ", transferRate=" + transferRate + - '}'; - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/api/data/chemical/material/properties/PipeNetProperties.java b/src/main/java/com/gregtechceu/gtceu/api/data/chemical/material/properties/PipeNetProperties.java new file mode 100644 index 0000000000..3d3a609c32 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/data/chemical/material/properties/PipeNetProperties.java @@ -0,0 +1,154 @@ +package com.gregtechceu.gtceu.api.data.chemical.material.properties; + +import com.gregtechceu.gtceu.api.graphnet.logic.NetLogicData; +import com.gregtechceu.gtceu.api.graphnet.pipenet.IPipeNetNodeHandler; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IPipeMaterialStructure; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IPipeStructure; + +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.StringRepresentable; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.BlockGetter; + +import it.unimi.dsi.fastutil.objects.Object2ObjectRBTreeMap; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +public class PipeNetProperties implements IMaterialProperty, IPipeNetNodeHandler { + + protected final Map, IPipeNetMaterialProperty> properties = new Object2ObjectRBTreeMap<>( + Comparator.comparing(IPipeNetMaterialProperty.MaterialPropertyKey::getSerializedName)); + + public void setProperty(IPipeNetMaterialProperty property) { + this.properties.put(property.getKey(), property); + } + + public boolean hasProperty(IPipeNetMaterialProperty.MaterialPropertyKey key) { + return this.properties.containsKey(key); + } + + public Collection getRegisteredProperties() { + return properties.values(); + } + + public T getProperty(IPipeNetMaterialProperty.MaterialPropertyKey key) { + return key.cast(this.properties.get(key)); + } + + public void removeProperty(IPipeNetMaterialProperty.MaterialPropertyKey key) { + this.properties.remove(key); + } + + public boolean generatesStructure(IPipeStructure structure) { + for (IPipeNetMaterialProperty p : properties.values()) { + if (p.generatesStructure(structure)) return true; + } + return false; + } + + @Override + public @NotNull Collection getOrCreateFromNets(ServerLevel level, BlockPos pos, + IPipeStructure structure) { + List list = new ObjectArrayList<>(); + for (IPipeNetMaterialProperty p : properties.values()) { + if (p.supportsStructure(structure)) { + WorldPipeNode node = p.getOrCreateFromNet(level, pos, structure); + if (node != null) list.add(node); + } + } + return list; + } + + @Override + public @NotNull Collection getFromNets(ServerLevel world, BlockPos pos, IPipeStructure structure) { + List list = new ObjectArrayList<>(); + for (IPipeNetMaterialProperty p : properties.values()) { + if (p.supportsStructure(structure)) { + WorldPipeNode node = p.getFromNet(world, pos, structure); + if (node != null) list.add(node); + } + } + return list; + } + + @Override + public void removeFromNets(ServerLevel level, BlockPos pos, IPipeStructure structure) { + for (IPipeNetMaterialProperty p : properties.values()) { + if (p.supportsStructure(structure)) p.removeFromNet(level, pos, structure); + } + } + + @Override + public void addInformation(@NotNull ItemStack stack, BlockGetter level, @NotNull List tooltip, + @NotNull TooltipFlag flagIn, IPipeStructure structure) { + for (IPipeNetMaterialProperty p : properties.values()) { + if (p.supportsStructure(structure)) + p.addInformation(stack, level, tooltip, flagIn, (IPipeMaterialStructure) structure); + } + } + + @Override + public void verifyProperty(MaterialProperties properties) { + for (IPipeNetMaterialProperty p : this.properties.values()) { + p.verifyProperty(properties); + } + } + + protected static final class MaterialPropertyComparator implements Comparator { + + @Override + public int compare(IPipeNetMaterialProperty o1, IPipeNetMaterialProperty o2) { + return 0; + } + } + + public interface IPipeNetMaterialProperty extends IMaterialProperty { + + @Nullable + WorldPipeNode getOrCreateFromNet(ServerLevel world, BlockPos pos, IPipeStructure structure); + + @Nullable + WorldPipeNode getFromNet(ServerLevel world, BlockPos pos, IPipeStructure structure); + + void mutateData(NetLogicData data, IPipeStructure structure); + + void removeFromNet(ServerLevel world, BlockPos pos, IPipeStructure structure); + + boolean generatesStructure(IPipeStructure structure); + + boolean supportsStructure(IPipeStructure structure); + + void addInformation(@NotNull ItemStack stack, BlockGetter worldIn, @NotNull List tooltip, + @NotNull TooltipFlag flagIn, IPipeMaterialStructure structure); + + MaterialPropertyKey getKey(); + + class MaterialPropertyKey implements StringRepresentable { + + private final @NotNull String name; + + public MaterialPropertyKey(@NotNull String name) { + this.name = name; + } + + @Override + public @NotNull String getSerializedName() { + return name; + } + + T cast(IPipeNetMaterialProperty property) { + return (T) property; + } + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/data/chemical/material/properties/PropertyKey.java b/src/main/java/com/gregtechceu/gtceu/api/data/chemical/material/properties/PropertyKey.java index 4778fe9182..96dba4fa1c 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/data/chemical/material/properties/PropertyKey.java +++ b/src/main/java/com/gregtechceu/gtceu/api/data/chemical/material/properties/PropertyKey.java @@ -6,18 +6,15 @@ public class PropertyKey { public static final PropertyKey ALLOY_BLAST = new PropertyKey<>("blast_alloy", AlloyBlastProperty.class); public static final PropertyKey DUST = new PropertyKey<>("dust", DustProperty.class); - public static final PropertyKey FLUID_PIPE = new PropertyKey<>("fluid_pipe", - FluidPipeProperties.class); + public static final PropertyKey PIPENET_PROPERTIES = new PropertyKey<>("net_pipe", + PipeNetProperties.class); public static final PropertyKey FLUID = new PropertyKey<>("fluid", FluidProperty.class); public static final PropertyKey GEM = new PropertyKey<>("gem", GemProperty.class); public static final PropertyKey INGOT = new PropertyKey<>("ingot", IngotProperty.class); public static final PropertyKey POLYMER = new PropertyKey<>("polymer", PolymerProperty.class); - public static final PropertyKey ITEM_PIPE = new PropertyKey<>("item_pipe", - ItemPipeProperties.class); public static final PropertyKey ORE = new PropertyKey<>("ore", OreProperty.class); public static final PropertyKey TOOL = new PropertyKey<>("tool", ToolProperty.class); public static final PropertyKey ROTOR = new PropertyKey<>("rotor", RotorProperty.class); - public static final PropertyKey WIRE = new PropertyKey<>("wire", WireProperties.class); public static final PropertyKey WOOD = new PropertyKey<>("wood", WoodProperty.class); public static final PropertyKey HAZARD = new PropertyKey<>("hazard", HazardProperty.class); diff --git a/src/main/java/com/gregtechceu/gtceu/api/data/chemical/material/properties/WireProperties.java b/src/main/java/com/gregtechceu/gtceu/api/data/chemical/material/properties/WireProperties.java deleted file mode 100644 index 23f62be810..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/api/data/chemical/material/properties/WireProperties.java +++ /dev/null @@ -1,164 +0,0 @@ -package com.gregtechceu.gtceu.api.data.chemical.material.properties; - -import com.gregtechceu.gtceu.api.GTValues; -import com.gregtechceu.gtceu.api.data.chemical.material.Material; - -import java.util.Objects; - -import static com.gregtechceu.gtceu.api.data.chemical.material.info.MaterialFlags.GENERATE_FOIL; - -public class WireProperties implements IMaterialProperty { - - private long voltage; - private int amperage; - private int lossPerBlock; - private int superconductorCriticalTemperature; - private boolean isSuperconductor; - - public WireProperties(long voltage, int baseAmperage, int lossPerBlock) { - this(voltage, baseAmperage, lossPerBlock, false); - } - - public WireProperties(long voltage, int baseAmperage, int lossPerBlock, boolean isSuperCon) { - this(voltage, baseAmperage, lossPerBlock, isSuperCon, 0); - } - - public WireProperties(long voltage, int baseAmperage, int lossPerBlock, boolean isSuperCon, - int criticalTemperature) { - this.voltage = voltage; - this.amperage = baseAmperage; - this.lossPerBlock = isSuperCon ? 0 : lossPerBlock; - this.superconductorCriticalTemperature = isSuperCon ? criticalTemperature : 0; - this.isSuperconductor = isSuperCon; - } - - public WireProperties copy() { - return new WireProperties(voltage, amperage, lossPerBlock, isSuperconductor, superconductorCriticalTemperature); - } - - /** - * Default values constructor - */ - public WireProperties() { - this(8, 1, 1, false); - } - - /** - * Retrieves the current wire voltage - * - * @return The current wire voltage - */ - public long getVoltage() { - return voltage; - } - - /** - * Sets the current wire voltage - * - * @param voltage The new wire voltage - */ - public void setVoltage(long voltage) { - this.voltage = voltage; - } - - /** - * Retrieves the current wire amperage - * - * @return The current wire amperage - */ - public int getAmperage() { - return amperage; - } - - /** - * Sets the current wire amperage - * - * @param amperage The new current wire amperage - */ - public void setAmperage(int amperage) { - this.amperage = amperage; - } - - /** - * Retrieves the current wire loss per block - * - * @return The current wire loss per block - */ - public int getLossPerBlock() { - return lossPerBlock; - } - - /** - * Sets the current wire loss per block - * - * @param lossPerBlock The new wire loss per block - */ - public void setLossPerBlock(int lossPerBlock) { - this.lossPerBlock = lossPerBlock; - } - - /** - * If the current wire is a Superconductor wire - * - * @return {@code true} if the current wire is a Superconductor - */ - public boolean isSuperconductor() { - return isSuperconductor; - } - - /** - * Sets the current wire to a superconductor wire - * - * @param isSuperconductor The new wire superconductor status - */ - public void setSuperconductor(boolean isSuperconductor) { - this.isSuperconductor = isSuperconductor; - } - - /** - * Retrieves the critical temperature of the superconductor (the temperature at which the superconductive phase - * transition happens) - * - * @return Critical temperature of the material - */ - public int getSuperconductorCriticalTemperature() { - return superconductorCriticalTemperature; - } - - /** - * Sets the material's critical temperature - * - * @param criticalTemperature The new critical temperature - */ - public void setSuperconductorCriticalTemperature(int criticalTemperature) { - this.superconductorCriticalTemperature = this.isSuperconductor ? criticalTemperature : 0; - } - - @Override - public void verifyProperty(MaterialProperties properties) { - properties.ensureSet(PropertyKey.DUST, true); - if (properties.hasProperty(PropertyKey.INGOT)) { - // Ensure all Materials with Cables and voltage tier IV or above have a Foil for recipe generation - Material thisMaterial = properties.getMaterial(); - if (!isSuperconductor && voltage >= GTValues.V[GTValues.IV] && !thisMaterial.hasFlag(GENERATE_FOIL)) { - thisMaterial.addFlags(GENERATE_FOIL); - } - } - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof WireProperties that)) return false; - return voltage == that.voltage && - amperage == that.amperage && - lossPerBlock == that.lossPerBlock && - superconductorCriticalTemperature == that.superconductorCriticalTemperature && - isSuperconductor == that.isSuperconductor; - } - - @Override - public int hashCode() { - return Objects.hash(voltage, amperage, lossPerBlock); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/api/data/medicalcondition/HazardStack.java b/src/main/java/com/gregtechceu/gtceu/api/data/medicalcondition/HazardStack.java new file mode 100644 index 0000000000..67da697183 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/data/medicalcondition/HazardStack.java @@ -0,0 +1,21 @@ +package com.gregtechceu.gtceu.api.data.medicalcondition; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +public class HazardStack { + + @Getter + private final MedicalCondition medicalCondition; + @Getter + private float amount; + + public void grow(float amount) { + this.amount += amount; + } + + public HazardStack copy() { + return new HazardStack(medicalCondition, amount); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/data/tag/TagPrefix.java b/src/main/java/com/gregtechceu/gtceu/api/data/tag/TagPrefix.java index 748e205a4a..f82d4ef43f 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/data/tag/TagPrefix.java +++ b/src/main/java/com/gregtechceu/gtceu/api/data/tag/TagPrefix.java @@ -114,7 +114,7 @@ public static TagPrefix get(String name) { public static final TagPrefix oreRedGranite = oreTagPrefix("red_granite", BlockTags.MINEABLE_WITH_PICKAXE) .langValue("Red Granite %s Ore") - .registerOre(() -> GTBlocks.RED_GRANITE.getDefaultState(), () -> GTMaterials.GraniteRed, + .registerOre(() -> GTBlocks.RED_GRANITE.getDefaultState(), () -> GTMaterials.RedGranite, BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_RED).requiresCorrectToolForDrops() .strength(3.0F, 3.0F), GTCEu.id("block/red_granite")); @@ -729,108 +729,161 @@ public static TagPrefix get(String name) { material.hasFlag(MaterialFlags.GENERATE_FRAME)); // Pipes - public static final TagPrefix pipeTinyFluid = new TagPrefix("pipeTinyFluid") - .itemTable(() -> GTMaterialBlocks.FLUID_PIPE_BLOCKS).langValue("Tiny %s Fluid Pipe") - .miningToolTag(GTToolType.WRENCH.harvestTags.get(0)).materialAmount(GTValues.M / 2) - .unificationEnabled(true); - public static final TagPrefix pipeSmallFluid = new TagPrefix("pipeSmallFluid") - .itemTable(() -> GTMaterialBlocks.FLUID_PIPE_BLOCKS).langValue("Small %s Fluid Pipe") - .miningToolTag(GTToolType.WRENCH.harvestTags.get(0)).materialAmount(GTValues.M).unificationEnabled(true); - public static final TagPrefix pipeNormalFluid = new TagPrefix("pipeNormalFluid") - .itemTable(() -> GTMaterialBlocks.FLUID_PIPE_BLOCKS).langValue("Normal %s Fluid Pipe") - .miningToolTag(GTToolType.WRENCH.harvestTags.get(0)).materialAmount(GTValues.M * 3) + public static final TagPrefix pipeTiny = new TagPrefix("pipeTiny") + .langValue("Tiny %s Pipe") + .itemTable(() -> GTMaterialBlocks.MATERIAL_PIPE_BLOCKS) + .miningToolTag(GTToolType.WRENCH.harvestTags.get(0)) + .materialAmount(GTValues.M / 2) .unificationEnabled(true); - public static final TagPrefix pipeLargeFluid = new TagPrefix("pipeLargeFluid") - .itemTable(() -> GTMaterialBlocks.FLUID_PIPE_BLOCKS).langValue("Large %s Fluid Pipe") - .miningToolTag(GTToolType.WRENCH.harvestTags.get(0)).materialAmount(GTValues.M * 6) + public static final TagPrefix pipeSmall = new TagPrefix("pipeSmall") + .langValue("Small %s Pipe") + .itemTable(() -> GTMaterialBlocks.MATERIAL_PIPE_BLOCKS) + .miningToolTag(GTToolType.WRENCH.harvestTags.get(0)) + .materialAmount(GTValues.M) .unificationEnabled(true); - public static final TagPrefix pipeHugeFluid = new TagPrefix("pipeHugeFluid") - .itemTable(() -> GTMaterialBlocks.FLUID_PIPE_BLOCKS).langValue("Huge %s Fluid Pipe") - .miningToolTag(GTToolType.WRENCH.harvestTags.get(0)).materialAmount(GTValues.M * 12) + public static final TagPrefix pipeNormal = new TagPrefix("pipeNormal") + .langValue("Normal %s Pipe") + .itemTable(() -> GTMaterialBlocks.MATERIAL_PIPE_BLOCKS) + .miningToolTag(GTToolType.WRENCH.harvestTags.get(0)) + .materialAmount(GTValues.M * 3) .unificationEnabled(true); - - public static final TagPrefix pipeQuadrupleFluid = new TagPrefix("pipeQuadrupleFluid") - .itemTable(() -> GTMaterialBlocks.FLUID_PIPE_BLOCKS).langValue("Quadruple %s Fluid Pipe") - .miningToolTag(GTToolType.WRENCH.harvestTags.get(0)).materialAmount(GTValues.M * 4) + public static final TagPrefix pipeLarge = new TagPrefix("pipeLarge") + .langValue("Large %s Pipe") + .itemTable(() -> GTMaterialBlocks.MATERIAL_PIPE_BLOCKS) + .miningToolTag(GTToolType.WRENCH.harvestTags.get(0)) + .materialAmount(GTValues.M * 6) .unificationEnabled(true); - public static final TagPrefix pipeNonupleFluid = new TagPrefix("pipeNonupleFluid") - .itemTable(() -> GTMaterialBlocks.FLUID_PIPE_BLOCKS).langValue("Nonuple %s Fluid Pipe") - .miningToolTag(GTToolType.WRENCH.harvestTags.get(0)).materialAmount(GTValues.M * 9) + public static final TagPrefix pipeHuge = new TagPrefix("pipeHuge") + .langValue("Huge %s Pipe") + .itemTable(() -> GTMaterialBlocks.MATERIAL_PIPE_BLOCKS) + .miningToolTag(GTToolType.WRENCH.harvestTags.get(0)) + .materialAmount(GTValues.M * 12) .unificationEnabled(true); - public static final TagPrefix pipeSmallItem = new TagPrefix("pipeSmallItem") - .itemTable(() -> GTMaterialBlocks.ITEM_PIPE_BLOCKS).langValue("Small %s Item Pipe") - .miningToolTag(GTToolType.WRENCH.harvestTags.get(0)).materialAmount(GTValues.M).unificationEnabled(true); - public static final TagPrefix pipeNormalItem = new TagPrefix("pipeNormalItem") - .itemTable(() -> GTMaterialBlocks.ITEM_PIPE_BLOCKS).langValue("Normal %s Item Pipe") - .miningToolTag(GTToolType.WRENCH.harvestTags.get(0)).materialAmount(GTValues.M * 3) - .unificationEnabled(true); - public static final TagPrefix pipeLargeItem = new TagPrefix("pipeLargeItem") - .itemTable(() -> GTMaterialBlocks.ITEM_PIPE_BLOCKS).langValue("Large %s Item Pipe") - .miningToolTag(GTToolType.WRENCH.harvestTags.get(0)).materialAmount(GTValues.M * 6) + public static final TagPrefix pipeQuadruple = new TagPrefix("pipeQuadruple") + .langValue("Quadruple %s Pipe") + .itemTable(() -> GTMaterialBlocks.MATERIAL_PIPE_BLOCKS) + .miningToolTag(GTToolType.WRENCH.harvestTags.get(0)) + .materialAmount(GTValues.M * 4) .unificationEnabled(true); - public static final TagPrefix pipeHugeItem = new TagPrefix("pipeHugeItem") - .itemTable(() -> GTMaterialBlocks.ITEM_PIPE_BLOCKS).langValue("Huge %s Item Pipe") - .miningToolTag(GTToolType.WRENCH.harvestTags.get(0)).materialAmount(GTValues.M * 12) + public static final TagPrefix pipeNonuple = new TagPrefix("pipeNonuple") + .langValue("Nonuple %s Pipe") + .itemTable(() -> GTMaterialBlocks.MATERIAL_PIPE_BLOCKS) + .miningToolTag(GTToolType.WRENCH.harvestTags.get(0)) + .materialAmount(GTValues.M * 9) .unificationEnabled(true); + public static final TagPrefix pipeTinyRestrictive = new TagPrefix("pipeTinyRestrictive") + .langValue("Tiny Restrictive %s Pipe") + .itemTable(() -> GTMaterialBlocks.MATERIAL_PIPE_BLOCKS) + .miningToolTag(GTToolType.WRENCH.harvestTags.get(0)) + .materialAmount(GTValues.M) + .unificationEnabled(true); public static final TagPrefix pipeSmallRestrictive = new TagPrefix("pipeSmallRestrictive") - .itemTable(() -> GTMaterialBlocks.ITEM_PIPE_BLOCKS).langValue("Small Restrictive %s Item Pipe") - .miningToolTag(GTToolType.WRENCH.harvestTags.get(0)).materialAmount(GTValues.M).unificationEnabled(true); + .langValue("Small Restrictive %s Pipe") + .itemTable(() -> GTMaterialBlocks.MATERIAL_PIPE_BLOCKS) + .miningToolTag(GTToolType.WRENCH.harvestTags.get(0)) + .materialAmount(GTValues.M) + .unificationEnabled(true); public static final TagPrefix pipeNormalRestrictive = new TagPrefix("pipeNormalRestrictive") - .itemTable(() -> GTMaterialBlocks.ITEM_PIPE_BLOCKS).langValue("Normal Restrictive %s Item Pipe") - .miningToolTag(GTToolType.WRENCH.harvestTags.get(0)).materialAmount(GTValues.M * 3) + .langValue("Normal Restrictive %s Pipe") + .itemTable(() -> GTMaterialBlocks.MATERIAL_PIPE_BLOCKS) + .miningToolTag(GTToolType.WRENCH.harvestTags.get(0)) + .materialAmount(GTValues.M * 3) .unificationEnabled(true); public static final TagPrefix pipeLargeRestrictive = new TagPrefix("pipeLargeRestrictive") - .itemTable(() -> GTMaterialBlocks.ITEM_PIPE_BLOCKS).langValue("Large Restrictive %s Item Pipe") - .miningToolTag(GTToolType.WRENCH.harvestTags.get(0)).materialAmount(GTValues.M * 6) + .langValue("Large Restrictive %s Pipe") + .itemTable(() -> GTMaterialBlocks.MATERIAL_PIPE_BLOCKS) + .miningToolTag(GTToolType.WRENCH.harvestTags.get(0)) + .materialAmount(GTValues.M * 6) .unificationEnabled(true); public static final TagPrefix pipeHugeRestrictive = new TagPrefix("pipeHugeRestrictive") - .itemTable(() -> GTMaterialBlocks.ITEM_PIPE_BLOCKS).langValue("Huge Restrictive %s Item Pipe") - .miningToolTag(GTToolType.WRENCH.harvestTags.get(0)).materialAmount(GTValues.M * 12) + .langValue("Huge Restrictive %s Pipe") + .itemTable(() -> GTMaterialBlocks.MATERIAL_PIPE_BLOCKS) + .miningToolTag(GTToolType.WRENCH.harvestTags.get(0)) + .materialAmount(GTValues.M * 12) + .unificationEnabled(true); + + public static final TagPrefix pipeQuadrupleRestrictive = new TagPrefix("pipeQuadrupleRestrictive") + .langValue("Quadruple Restrictive %s Pipe") + .itemTable(() -> GTMaterialBlocks.MATERIAL_PIPE_BLOCKS) + .miningToolTag(GTToolType.WRENCH.harvestTags.get(0)) + .materialAmount(GTValues.M * 4) + .unificationEnabled(true); + public static final TagPrefix pipeNonupleRestrictive = new TagPrefix("pipeNonupleRestrictive") + .langValue("Nonuple Restrictive %s Pipe") + .itemTable(() -> GTMaterialBlocks.MATERIAL_PIPE_BLOCKS) + .miningToolTag(GTToolType.WRENCH.harvestTags.get(0)) + .materialAmount(GTValues.M * 9) .unificationEnabled(true); // Wires and cables - public static final TagPrefix wireGtHex = new TagPrefix("wireGtHex").itemTable(() -> GTMaterialBlocks.CABLE_BLOCKS) - .langValue("16x %s Wire").miningToolTag(GTToolType.WIRE_CUTTER.harvestTags.get(0)) - .materialAmount(GTValues.M * 8).materialIconType(MaterialIconType.wire).unificationEnabled(true); + public static final TagPrefix wireGtHex = new TagPrefix("wireGtHex") + .langValue("16x %s Wire") + .itemTable(() -> GTMaterialBlocks.MATERIAL_PIPE_BLOCKS) + .miningToolTag(GTToolType.WIRE_CUTTER.harvestTags.get(0)) + .materialAmount(GTValues.M * 8) + .materialIconType(MaterialIconType.wire) + .unificationEnabled(true); public static final TagPrefix wireGtOctal = new TagPrefix("wireGtOctal") - .itemTable(() -> GTMaterialBlocks.CABLE_BLOCKS) - .langValue("8x %s Wire").miningToolTag(GTToolType.WIRE_CUTTER.harvestTags.get(0)) - .materialAmount(GTValues.M * 4).materialIconType(MaterialIconType.wire).unificationEnabled(true); + .langValue("8x %s Wire") + .itemTable(() -> GTMaterialBlocks.MATERIAL_PIPE_BLOCKS) + .miningToolTag(GTToolType.WIRE_CUTTER.harvestTags.get(0)) + .materialAmount(GTValues.M * 4) + .materialIconType(MaterialIconType.wire) + .unificationEnabled(true); public static final TagPrefix wireGtQuadruple = new TagPrefix("wireGtQuadruple") - .itemTable(() -> GTMaterialBlocks.CABLE_BLOCKS).langValue("4x %s Wire") - .miningToolTag(GTToolType.WIRE_CUTTER.harvestTags.get(0)).materialAmount(GTValues.M * 2) - .materialIconType(MaterialIconType.wire).unificationEnabled(true); + .langValue("4x %s Wire") + .itemTable(() -> GTMaterialBlocks.MATERIAL_PIPE_BLOCKS) + .miningToolTag(GTToolType.WIRE_CUTTER.harvestTags.get(0)) + .materialAmount(GTValues.M * 2) + .materialIconType(MaterialIconType.wire) + .unificationEnabled(true); public static final TagPrefix wireGtDouble = new TagPrefix("wireGtDouble") - .itemTable(() -> GTMaterialBlocks.CABLE_BLOCKS) - .langValue("2x %s Wire").miningToolTag(GTToolType.WIRE_CUTTER.harvestTags.get(0)).materialAmount(GTValues.M) - .materialIconType(MaterialIconType.wire).unificationEnabled(true); + .langValue("2x %s Wire") + .itemTable(() -> GTMaterialBlocks.MATERIAL_PIPE_BLOCKS) + .miningToolTag(GTToolType.WIRE_CUTTER.harvestTags.get(0)) + .materialAmount(GTValues.M) + .materialIconType(MaterialIconType.wire) + .unificationEnabled(true); public static final TagPrefix wireGtSingle = new TagPrefix("wireGtSingle") - .itemTable(() -> GTMaterialBlocks.CABLE_BLOCKS) - .langValue("1x %s Wire").miningToolTag(GTToolType.WIRE_CUTTER.harvestTags.get(0)) - .materialAmount(GTValues.M / 2).materialIconType(MaterialIconType.wire).unificationEnabled(true); + .langValue("1x %s Wire") + .itemTable(() -> GTMaterialBlocks.MATERIAL_PIPE_BLOCKS) + .miningToolTag(GTToolType.WIRE_CUTTER.harvestTags.get(0)) + .materialAmount(GTValues.M / 2) + .materialIconType(MaterialIconType.wire) + .unificationEnabled(true); public static final TagPrefix cableGtHex = new TagPrefix("cableGtHex") - .itemTable(() -> GTMaterialBlocks.CABLE_BLOCKS) - .langValue("16x %s Cable").miningToolTag(GTToolType.WIRE_CUTTER.harvestTags.get(0)) - .materialAmount(GTValues.M * 8).unificationEnabled(true); + .langValue("16x %s Cable") + .itemTable(() -> GTMaterialBlocks.MATERIAL_PIPE_BLOCKS) + .miningToolTag(GTToolType.WIRE_CUTTER.harvestTags.get(0)) + .materialAmount(GTValues.M * 8) + .unificationEnabled(true); public static final TagPrefix cableGtOctal = new TagPrefix("cableGtOctal") - .itemTable(() -> GTMaterialBlocks.CABLE_BLOCKS) - .langValue("8x %s Cable").miningToolTag(GTToolType.WIRE_CUTTER.harvestTags.get(0)) - .materialAmount(GTValues.M * 4).unificationEnabled(true); + .langValue("8x %s Cable") + .itemTable(() -> GTMaterialBlocks.MATERIAL_PIPE_BLOCKS) + .miningToolTag(GTToolType.WIRE_CUTTER.harvestTags.get(0)) + .materialAmount(GTValues.M * 4) + .unificationEnabled(true); public static final TagPrefix cableGtQuadruple = new TagPrefix("cableGtQuadruple") - .itemTable(() -> GTMaterialBlocks.CABLE_BLOCKS).langValue("4x %s Cable") - .miningToolTag(GTToolType.WIRE_CUTTER.harvestTags.get(0)).materialAmount(GTValues.M * 2) + .langValue("4x %s Cable") + .itemTable(() -> GTMaterialBlocks.MATERIAL_PIPE_BLOCKS) + .miningToolTag(GTToolType.WIRE_CUTTER.harvestTags.get(0)) + .materialAmount(GTValues.M * 2) .unificationEnabled(true); public static final TagPrefix cableGtDouble = new TagPrefix("cableGtDouble") - .itemTable(() -> GTMaterialBlocks.CABLE_BLOCKS) - .langValue("2x %s Cable").miningToolTag(GTToolType.WIRE_CUTTER.harvestTags.get(0)) - .materialAmount(GTValues.M).unificationEnabled(true); + .langValue("2x %s Cable") + .itemTable(() -> GTMaterialBlocks.MATERIAL_PIPE_BLOCKS) + .miningToolTag(GTToolType.WIRE_CUTTER.harvestTags.get(0)) + .materialAmount(GTValues.M) + .unificationEnabled(true); public static final TagPrefix cableGtSingle = new TagPrefix("cableGtSingle") - .itemTable(() -> GTMaterialBlocks.CABLE_BLOCKS) - .langValue("1x %s Cable").miningToolTag(GTToolType.WIRE_CUTTER.harvestTags.get(0)) - .materialAmount(GTValues.M / 2).unificationEnabled(true); + .langValue("1x %s Cable") + .itemTable(() -> GTMaterialBlocks.MATERIAL_PIPE_BLOCKS) + .miningToolTag(GTToolType.WIRE_CUTTER.harvestTags.get(0)) + .materialAmount(GTValues.M / 2) + .unificationEnabled(true); public static class Conditions { @@ -1090,18 +1143,33 @@ public boolean doGenerateBlock(Material material) { } @FunctionalInterface - public interface MaterialRecipeHandler { + public interface PropertyMaterialRecipeHandler { void accept(TagPrefix prefix, Material material, T property, Consumer provider); } + @FunctionalInterface + public interface MaterialRecipeHandler { + + void accept(TagPrefix prefix, Material material, Consumer provider); + } + public void executeHandler(Consumer provider, PropertyKey propertyKey, - MaterialRecipeHandler handler) { - for (Material material : GTCEuAPI.materialManager.getRegisteredMaterials()) { + PropertyMaterialRecipeHandler handler) { + executeHandler(provider, (prefix, material, provider1) -> { if (material.hasProperty(propertyKey) && !material.hasFlag(MaterialFlags.NO_UNIFICATION) && !ChemicalHelper.get(this, material).isEmpty()) { - handler.accept(this, material, material.getProperty(propertyKey), provider); + handler.accept(this, material, material.getProperty(propertyKey), provider1); + } + }); + } + + public void executeHandler(Consumer provider, MaterialRecipeHandler handler) { + for (Material material : GTCEuAPI.materialManager.getRegisteredMaterials()) { + if (!material.hasFlag(MaterialFlags.NO_UNIFICATION) && + !ChemicalHelper.get(this, material).isEmpty()) { + handler.accept(this, material, provider); } } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/datafixer/DataFixesInternals.java b/src/main/java/com/gregtechceu/gtceu/api/datafixer/DataFixesInternals.java new file mode 100644 index 0000000000..2394224938 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/datafixer/DataFixesInternals.java @@ -0,0 +1,99 @@ +/* + * Copyright 2022 QuiltMC + * Modified by the Steam 'n' Rails (Railways) team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.gregtechceu.gtceu.api.datafixer; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.config.ConfigHolder; + +import net.minecraft.SharedConstants; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.util.datafix.DataFixers; + +import com.mojang.datafixers.DSL; +import com.mojang.datafixers.DataFixUtils; +import com.mojang.datafixers.DataFixer; +import com.mojang.datafixers.schemas.Schema; +import com.mojang.serialization.Dynamic; +import org.jetbrains.annotations.*; + +import java.util.function.BiFunction; + +import static com.google.common.base.Preconditions.checkArgument; + +@ApiStatus.Internal +public abstract class DataFixesInternals { + + public static final BiFunction BASE_SCHEMA = (version, parent) -> { + checkArgument(version == 0, "version must be 0"); + checkArgument(parent == null, "parent must be null"); + return get().createBaseSchema(); + }; + + public record DataFixerEntry(DataFixer dataFixer, int currentVersion) {} + + @Contract(pure = true) + @Range(from = 0, to = Integer.MAX_VALUE) + public static int getModDataVersion(@NotNull Dynamic compound) { + return compound.get("GTCEu_DataVersion").asInt(0); + } + + private static DataFixesInternals instance; + + public static @NotNull DataFixesInternals get() { + if (instance == null) { + // Init config in case it's not loaded yet + ConfigHolder.init(); + if (!ConfigHolder.INSTANCE.compat.doDatafixers) { + instance = new NoOpDataFixesInternals(); + return instance; + } + + Schema latestVanillaSchema; + try { + latestVanillaSchema = DataFixers.getDataFixer() + .getSchema(DataFixUtils + .makeKey(SharedConstants.getCurrentVersion().getDataVersion().getVersion())); + } catch (Exception e) { + latestVanillaSchema = null; + } + + if (latestVanillaSchema == null) { + GTCEu.LOGGER.warn("[GTCEuM DFU] Failed to initialize! Either someone stopped DFU from initializing,"); + GTCEu.LOGGER.warn("[GTCEuM DFU] or this Minecraft build is hosed."); + GTCEu.LOGGER.warn("[GTCEuM DFU] Using no-op implementation."); + instance = new NoOpDataFixesInternals(); + } else { + instance = new DataFixesInternalsImpl(latestVanillaSchema); + } + } + + return instance; + } + + public abstract void registerFixer(@Range(from = 0, to = Integer.MAX_VALUE) int currentVersion, + @NotNull DataFixer dataFixer); + + public abstract @Nullable DataFixerEntry getFixerEntry(); + + @Contract(value = "-> new", pure = true) + public abstract @NotNull Schema createBaseSchema(); + + public abstract @NotNull Dynamic updateWithAllFixers(DSL.TypeReference dataFixTypes, + @NotNull Dynamic dynamic); + + public abstract @NotNull CompoundTag addModDataVersions(@NotNull CompoundTag compound); +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/datafixer/DataFixesInternalsImpl.java b/src/main/java/com/gregtechceu/gtceu/api/datafixer/DataFixesInternalsImpl.java new file mode 100644 index 0000000000..9337f273f7 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/datafixer/DataFixesInternalsImpl.java @@ -0,0 +1,82 @@ +/* + * Copyright 2022 QuiltMC + * Modified by the Steam 'n' Rails (Railways) team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.gregtechceu.gtceu.api.datafixer; + +import com.gregtechceu.gtceu.common.datafixer.schemas.V0; + +import net.minecraft.nbt.CompoundTag; + +import com.mojang.datafixers.DSL; +import com.mojang.datafixers.DataFixer; +import com.mojang.datafixers.schemas.Schema; +import com.mojang.serialization.Dynamic; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Range; + +@ApiStatus.Internal +public final class DataFixesInternalsImpl extends DataFixesInternals { + + private final @NotNull Schema latestVanillaSchema; + + private DataFixerEntry dataFixer; + + public DataFixesInternalsImpl(@NotNull Schema latestVanillaSchema) { + this.latestVanillaSchema = latestVanillaSchema; + + this.dataFixer = null; + } + + @Override + public void registerFixer(@Range(from = 0, to = Integer.MAX_VALUE) int currentVersion, + @NotNull DataFixer dataFixer) { + if (this.dataFixer != null) { + throw new IllegalArgumentException("GTCEu already has a registered data fixer"); + } + + this.dataFixer = new DataFixerEntry(dataFixer, currentVersion); + } + + @Override + public @Nullable DataFixerEntry getFixerEntry() { + return dataFixer; + } + + @Override + public @NotNull Schema createBaseSchema() { + return new V0(0, this.latestVanillaSchema); + } + + @Override + public @NotNull Dynamic updateWithAllFixers(DSL.TypeReference type, @NotNull Dynamic dynamic) { + if (dataFixer != null) { + int modDataVersion = DataFixesInternals.getModDataVersion(dynamic); + dynamic = dataFixer.dataFixer().update(type, dynamic, modDataVersion, dataFixer.currentVersion()); + } + + return dynamic; + } + + @Override + public @NotNull CompoundTag addModDataVersions(@NotNull CompoundTag compound) { + if (dataFixer != null) + compound.putInt("GTCEu_DataVersion", dataFixer.currentVersion()); + + return compound; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/datafixer/EmptySchema.java b/src/main/java/com/gregtechceu/gtceu/api/datafixer/EmptySchema.java new file mode 100644 index 0000000000..d52a41cd07 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/datafixer/EmptySchema.java @@ -0,0 +1,52 @@ +/* + * Copyright 2022 QuiltMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.gregtechceu.gtceu.api.datafixer; + +import com.mojang.datafixers.DSL; +import com.mojang.datafixers.schemas.Schema; +import com.mojang.datafixers.types.Type; +import com.mojang.datafixers.types.templates.TypeTemplate; +import it.unimi.dsi.fastutil.objects.Object2ObjectMaps; +import org.jetbrains.annotations.Range; + +import java.util.Map; +import java.util.function.Supplier; + +/** + * Represents an empty {@link Schema}, having no parent and containing no type definitions. + */ +public final class EmptySchema extends FirstSchema { + + /** + * Constructs an empty schema. + * + * @param versionKey the data version key + */ + public EmptySchema(@Range(from = 0, to = Integer.MAX_VALUE) int versionKey) { + super(versionKey); + } + + // Ensure the schema stays empty. + @Override + public void registerType(boolean recursive, DSL.TypeReference type, Supplier template) { + throw new UnsupportedOperationException(); + } + + @Override + protected Map> buildTypes() { + return Object2ObjectMaps.emptyMap(); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/datafixer/FirstSchema.java b/src/main/java/com/gregtechceu/gtceu/api/datafixer/FirstSchema.java new file mode 100644 index 0000000000..a04730bf6d --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/datafixer/FirstSchema.java @@ -0,0 +1,50 @@ +/* + * Copyright 2022 QuiltMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.gregtechceu.gtceu.api.datafixer; + +import com.mojang.datafixers.schemas.Schema; +import com.mojang.datafixers.types.templates.TypeTemplate; +import org.jetbrains.annotations.Range; + +import java.util.Map; +import java.util.function.Supplier; + +public class FirstSchema extends Schema { + + /** + * Creates a schema. + * + * @param versionKey the data version key + */ + public FirstSchema(@Range(from = 0, to = Integer.MAX_VALUE) int versionKey) { + super(versionKey, null); + } + + // all of these methods refer to this.parent without checking if its null + @Override + public void registerTypes(Schema schema, Map> entityTypes, + Map> blockEntityTypes) {} + + @Override + public Map> registerEntities(Schema schema) { + return Map.of(); + } + + @Override + public Map> registerBlockEntities(Schema schema) { + return Map.of(); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/datafixer/LICENSE b/src/main/java/com/gregtechceu/gtceu/api/datafixer/LICENSE new file mode 100644 index 0000000000..ad5b29c1e3 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/datafixer/LICENSE @@ -0,0 +1,205 @@ +Certain portions of the datafixer are taken from the +Quilt Standard Library (https://github.com/QuiltMC/quilt-standard-libraries), +and as such are licensed under the following license: + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/src/main/java/com/gregtechceu/gtceu/api/datafixer/NoOpDataFixesInternals.java b/src/main/java/com/gregtechceu/gtceu/api/datafixer/NoOpDataFixesInternals.java new file mode 100644 index 0000000000..473f347558 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/datafixer/NoOpDataFixesInternals.java @@ -0,0 +1,60 @@ +/* + * Copyright 2022 QuiltMC + * Modified by the Steam 'n' Rails (Railways) team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.gregtechceu.gtceu.api.datafixer; + +import net.minecraft.nbt.CompoundTag; + +import com.mojang.datafixers.DSL; +import com.mojang.datafixers.DataFixer; +import com.mojang.datafixers.schemas.Schema; +import com.mojang.serialization.Dynamic; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Range; + +public class NoOpDataFixesInternals extends DataFixesInternals { + + private final Schema schema; + + public NoOpDataFixesInternals() { + schema = new EmptySchema(0); + } + + @Override + public void registerFixer(@Range(from = 0, to = Integer.MAX_VALUE) int currentVersion, + @NotNull DataFixer dataFixer) {} + + @Override + public @Nullable DataFixerEntry getFixerEntry() { + return null; + } + + @Override + public @NotNull Schema createBaseSchema() { + return schema; + } + + @Override + public @NotNull Dynamic updateWithAllFixers(DSL.TypeReference dataFixTypes, @NotNull Dynamic dynamic) { + return dynamic; + } + + @Override + public @NotNull CompoundTag addModDataVersions(@NotNull CompoundTag compound) { + return compound; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/fluids/ContainmentFailureHandler.java b/src/main/java/com/gregtechceu/gtceu/api/fluids/ContainmentFailureHandler.java new file mode 100644 index 0000000000..e4a0d268f2 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/fluids/ContainmentFailureHandler.java @@ -0,0 +1,13 @@ +package com.gregtechceu.gtceu.api.fluids; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import net.minecraftforge.fluids.FluidStack; + +public interface ContainmentFailureHandler { + + void handleFailure(Level world, BlockPos failingBlock, FluidStack failingStack); + + void handleFailure(Player failingPlayer, FluidStack failingStack); +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/fluids/FluidBuilder.java b/src/main/java/com/gregtechceu/gtceu/api/fluids/FluidBuilder.java index 510610f813..499ea440e9 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/fluids/FluidBuilder.java +++ b/src/main/java/com/gregtechceu/gtceu/api/fluids/FluidBuilder.java @@ -313,7 +313,20 @@ private void determineTextures(@Nullable Material material, @Nullable FluidStora } private void determineTemperature(@Nullable Material material) { - if (temperature != INFER_TEMPERATURE) return; + this.temperature = getDeterminedTemperature(material, null); + } + + public int getDeterminedTemperature(@Nullable Material material, @Nullable FluidStorageKey key) { + FluidState state = this.state; + if (state == null) { + if (key != null && key.getDefaultFluidState() != null) { + state = key.getDefaultFluidState(); + } else { + state = FluidState.LIQUID; // default fallback + } + } + int temperature = this.temperature; + if (temperature != INFER_TEMPERATURE) return temperature; if (material == null) { temperature = ROOM_TEMPERATURE; } else { @@ -343,6 +356,7 @@ private void determineTemperature(@Nullable Material material) { }; } } + return temperature; } private void determineColor(@Nullable Material material) { diff --git a/src/main/java/com/gregtechceu/gtceu/api/fluids/FluidState.java b/src/main/java/com/gregtechceu/gtceu/api/fluids/FluidState.java index cde8d46441..12cbd8a3e1 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/fluids/FluidState.java +++ b/src/main/java/com/gregtechceu/gtceu/api/fluids/FluidState.java @@ -1,9 +1,27 @@ package com.gregtechceu.gtceu.api.fluids; +import com.gregtechceu.gtceu.api.GTValues; +import com.gregtechceu.gtceu.utils.EntityDamageUtil; +import com.gregtechceu.gtceu.utils.GTUtil; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; +import net.minecraftforge.fluids.FluidStack; + import lombok.Getter; import org.jetbrains.annotations.NotNull; -public enum FluidState { +import java.util.List; + +public enum FluidState implements ContainmentFailureHandler { LIQUID("gtceu.fluid.state_liquid"), GAS("gtceu.fluid.state_gas"), @@ -16,4 +34,127 @@ public enum FluidState { FluidState(@NotNull String translationKey) { this.translationKey = translationKey; } + + public static FluidState inferState(FluidStack stack) { + if (stack.getFluid() instanceof GTFluid fluid) return fluid.getState(); + else return stack.getFluid().getFluidType().isLighterThanAir() ? GAS : LIQUID; + } + + @Override + public void handleFailure(Level world, BlockPos failingBlock, net.minecraftforge.fluids.FluidStack failingStack) { + world.playSound(null, failingBlock, SoundEvents.LAVA_EXTINGUISH, SoundSource.BLOCKS, 1.0F, 1.0F); + switch (this) { + default -> { + for (Direction facing : Direction.Plane.HORIZONTAL) { + int particles = GTValues.RNG.nextInt(5); + if (particles != 0) { + GTUtil.spawnParticles(world, facing, ParticleTypes.DRIPPING_WATER, failingBlock, particles); + } + } + float scalar = (float) Math.log(failingStack.getAmount()); + List entities = world.getEntitiesOfClass(LivingEntity.class, + new AABB(failingBlock).inflate(scalar * 0.5)); + for (LivingEntity entity : entities) { + EntityDamageUtil.applyTemperatureDamage(entity, + failingStack.getFluid().getFluidType().getTemperature(failingStack), + scalar, 20); + } + world.removeBlock(failingBlock, false); + } + case GAS -> { + GTUtil.spawnParticles(world, Direction.UP, ParticleTypes.LARGE_SMOKE, failingBlock, + 9 + GTValues.RNG.nextInt(3)); + float scalar = (float) Math.log(failingStack.getAmount()); + List entities = world.getEntitiesOfClass(LivingEntity.class, + new AABB(failingBlock).inflate(scalar)); + for (LivingEntity entity : entities) { + EntityDamageUtil.applyTemperatureDamage(entity, + failingStack.getFluid().getFluidType().getTemperature(failingStack), + scalar * 0.75f, 15); + } + world.removeBlock(failingBlock, false); + } + case PLASMA -> { + GTUtil.spawnParticles(world, Direction.UP, ParticleTypes.LARGE_SMOKE, failingBlock, + 3 + GTValues.RNG.nextInt(3)); + float scalar = (float) Math.log(failingStack.getAmount()); + List entities = world.getEntitiesOfClass(LivingEntity.class, + new AABB(failingBlock).inflate(scalar * 1.5)); + for (LivingEntity entity : entities) { + EntityDamageUtil.applyTemperatureDamage(entity, + failingStack.getFluid().getFluidType().getTemperature(failingStack), + scalar, 30); + } + world.removeBlock(failingBlock, false); + world.explode(null, failingBlock.getX() + 0.5, failingBlock.getY() + 0.5, + failingBlock.getZ() + 0.5, + 1.0f + GTValues.RNG.nextFloat(), Level.ExplosionInteraction.BLOCK); + } + } + } + + @Override + public void handleFailure(Player failingPlayer, net.minecraftforge.fluids.FluidStack failingStack) { + Level world = failingPlayer.level(); + Vec3 pos = failingPlayer.getPosition(1.0f); + world.playSound(null, pos.x, pos.y, pos.z, SoundEvents.LAVA_EXTINGUISH, SoundSource.BLOCKS, 1.0F, + 1.0F); + switch (this) { + default -> { + for (Direction facing : Direction.Plane.HORIZONTAL) { + int particles = GTValues.RNG.nextInt(5); + if (particles != 0) { + GTUtil.spawnParticles(world, facing, ParticleTypes.DRIPPING_WATER, failingPlayer, particles); + } + } + float scalar = (float) Math.log(failingStack.getAmount()); + List entities = world.getEntitiesOfClass(LivingEntity.class, + new AABB(BlockPos.containing(failingPlayer.getPosition(1.0f))).inflate(scalar * 0.5)); + for (LivingEntity entity : entities) { + if (entity == failingPlayer) continue; + EntityDamageUtil.applyTemperatureDamage(entity, + failingStack.getFluid().getFluidType().getTemperature(failingStack), + scalar, 20); + } + EntityDamageUtil.applyTemperatureDamage(failingPlayer, + failingStack.getFluid().getFluidType().getTemperature(failingStack), + scalar * 3, 60); + } + case GAS -> { + GTUtil.spawnParticles(world, Direction.UP, ParticleTypes.LARGE_SMOKE, failingPlayer, + 9 + GTValues.RNG.nextInt(3)); + float scalar = (float) Math.log(failingStack.getAmount()); + List entities = world.getEntitiesOfClass(LivingEntity.class, + new AABB(BlockPos.containing(failingPlayer.getPosition(1.0f))).inflate(scalar)); + for (LivingEntity entity : entities) { + if (entity == failingPlayer) continue; + EntityDamageUtil.applyTemperatureDamage(entity, + failingStack.getFluid().getFluidType().getTemperature(failingStack), + scalar * 0.75f, 15); + } + EntityDamageUtil.applyTemperatureDamage(failingPlayer, + failingStack.getFluid().getFluidType().getTemperature(failingStack), + scalar * 2.25f, 45); + } + case PLASMA -> { + GTUtil.spawnParticles(world, Direction.UP, ParticleTypes.LARGE_SMOKE, failingPlayer, + 3 + GTValues.RNG.nextInt(3)); + float scalar = (float) Math.log(failingStack.getAmount()); + List entities = world.getEntitiesOfClass(LivingEntity.class, + new AABB(BlockPos.containing(failingPlayer.getPosition(1.0f))).inflate(scalar * 1.5)); + for (LivingEntity entity : entities) { + if (entity == failingPlayer) continue; + EntityDamageUtil.applyTemperatureDamage(entity, + failingStack.getFluid().getFluidType().getTemperature(failingStack), + scalar, 30); + } + EntityDamageUtil.applyTemperatureDamage(failingPlayer, + failingStack.getFluid().getFluidType().getTemperature(failingStack), + scalar * 3, 90); + Vec3 vec = failingPlayer.getEyePosition(1); + world.explode(null, vec.x, vec.y, vec.z, + 1.0f + GTValues.RNG.nextFloat(), Level.ExplosionInteraction.BLOCK); + } + } + } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/fluids/PropertyFluidFilter.java b/src/main/java/com/gregtechceu/gtceu/api/fluids/PropertyFluidFilter.java index 5373cba8c1..30fc38d324 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/fluids/PropertyFluidFilter.java +++ b/src/main/java/com/gregtechceu/gtceu/api/fluids/PropertyFluidFilter.java @@ -19,21 +19,21 @@ public class PropertyFluidFilter implements IPropertyFluidFilter { @Getter private final int maxFluidTemperature; @Getter - private final boolean gasProof; + private final int minFluidTemperature; @Getter - private final boolean cryoProof; + private final boolean gasProof; @Getter private final boolean plasmaProof; public PropertyFluidFilter(int maxFluidTemperature, + int minFluidTemperature, boolean gasProof, boolean acidProof, - boolean cryoProof, boolean plasmaProof) { this.maxFluidTemperature = maxFluidTemperature; + this.minFluidTemperature = minFluidTemperature; this.gasProof = gasProof; if (acidProof) setCanContain(FluidAttributes.ACID, true); - this.cryoProof = cryoProof; this.plasmaProof = plasmaProof; } @@ -56,6 +56,11 @@ public void setCanContain(@NotNull FluidAttribute attribute, boolean canContain) containmentPredicate.put(attribute, canContain); } + @Override + public void setCanContain(@NotNull Object2BooleanMap attributes) { + containmentPredicate.putAll(attributes); + } + @Override public @NotNull @UnmodifiableView Collection<@NotNull FluidAttribute> getContainedAttributes() { return containmentPredicate.keySet(); @@ -65,8 +70,8 @@ public void setCanContain(@NotNull FluidAttribute attribute, boolean canContain) public String toString() { return "SimplePropertyFluidFilter{" + "maxFluidTemperature=" + maxFluidTemperature + + "minFluidTemperature=" + minFluidTemperature + ", gasProof=" + gasProof + - ", cryoProof=" + cryoProof + ", plasmaProof=" + plasmaProof + ", containmentPredicate=" + containmentPredicate + '}'; diff --git a/src/main/java/com/gregtechceu/gtceu/api/fluids/attribute/FluidAttribute.java b/src/main/java/com/gregtechceu/gtceu/api/fluids/attribute/FluidAttribute.java index 80eae877ec..68c4b388d0 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/fluids/attribute/FluidAttribute.java +++ b/src/main/java/com/gregtechceu/gtceu/api/fluids/attribute/FluidAttribute.java @@ -1,30 +1,51 @@ package com.gregtechceu.gtceu.api.fluids.attribute; +import com.gregtechceu.gtceu.api.fluids.ContainmentFailureHandler; +import com.gregtechceu.gtceu.api.fluids.GTFluid; +import com.gregtechceu.gtceu.utils.TriConsumer; + +import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import net.minecraftforge.fluids.FluidStack; +import lombok.Getter; import org.jetbrains.annotations.NotNull; +import java.util.Collection; +import java.util.Collections; +import java.util.function.BiConsumer; import java.util.function.Consumer; -public final class FluidAttribute { +public final class FluidAttribute implements ContainmentFailureHandler { + @Getter + @NotNull private final ResourceLocation resourceLocation; private final Consumer> fluidTooltip; private final Consumer> containerTooltip; + private final TriConsumer blockContainmentFailure; + private final BiConsumer playerContainmentFailure; private final int hashCode; public FluidAttribute(@NotNull ResourceLocation resourceLocation, @NotNull Consumer> fluidTooltip, - @NotNull Consumer> containerTooltip) { + @NotNull Consumer> containerTooltip, + @NotNull TriConsumer blockContainmentFailure, + @NotNull BiConsumer playerContainmentFailure) { this.resourceLocation = resourceLocation; this.fluidTooltip = fluidTooltip; this.containerTooltip = containerTooltip; this.hashCode = resourceLocation.hashCode(); + this.blockContainmentFailure = blockContainmentFailure; + this.playerContainmentFailure = playerContainmentFailure; } - public @NotNull ResourceLocation getResourceLocation() { - return resourceLocation; + public static Collection inferAttributes(FluidStack stack) { + if (stack.getFluid() instanceof GTFluid fluid) return fluid.getAttributes(); + else return Collections.emptyList(); } public void appendFluidTooltips(@NotNull Consumer<@NotNull Component> tooltip) { @@ -35,6 +56,16 @@ public void appendContainerTooltips(@NotNull Consumer<@NotNull Component> toolti containerTooltip.accept(tooltip); } + @Override + public void handleFailure(Level world, BlockPos failingBlock, FluidStack failingStack) { + blockContainmentFailure.accept(world, failingBlock, failingStack); + } + + @Override + public void handleFailure(Player failingPlayer, FluidStack failingStack) { + playerContainmentFailure.accept(failingPlayer, failingStack); + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/src/main/java/com/gregtechceu/gtceu/api/fluids/attribute/FluidAttributes.java b/src/main/java/com/gregtechceu/gtceu/api/fluids/attribute/FluidAttributes.java index 2b19123297..1e1263cdb9 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/fluids/attribute/FluidAttributes.java +++ b/src/main/java/com/gregtechceu/gtceu/api/fluids/attribute/FluidAttributes.java @@ -1,8 +1,21 @@ package com.gregtechceu.gtceu.api.fluids.attribute; import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.GTValues; +import com.gregtechceu.gtceu.utils.EntityDamageUtil; +import com.gregtechceu.gtceu.utils.GTUtil; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.particles.ParticleTypes; import net.minecraft.network.chat.Component; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; + +import java.util.List; public final class FluidAttributes { @@ -11,7 +24,44 @@ public final class FluidAttributes { */ public static final FluidAttribute ACID = new FluidAttribute(GTCEu.id("acid"), list -> list.accept(Component.translatable("gtceu.fluid.type_acid.tooltip")), - list -> list.accept(Component.translatable("gtceu.fluid_pipe.acid_proof"))); + list -> list.accept(Component.translatable("gtceu.fluid_pipe.acid_proof")), + (w, b, f) -> { + w.playSound(null, b, SoundEvents.LAVA_EXTINGUISH, SoundSource.BLOCKS, 1.0F, 1.0F); + boolean gaseous = f.getFluid().getFluidType().getDensity(f) <= 0; + for (Direction facing : Direction.Plane.HORIZONTAL) { + GTUtil.spawnParticles(w, facing, ParticleTypes.CRIT, b, 3 + GTValues.RNG.nextInt(2)); + } + GTUtil.spawnParticles(w, gaseous ? Direction.UP : Direction.DOWN, ParticleTypes.CRIT, + b, 6 + GTValues.RNG.nextInt(4)); + float scalar = (float) Math.log(f.getAmount()); + List entities = w.getEntitiesOfClass(LivingEntity.class, + new AABB(b).inflate(scalar * (gaseous ? 0.4 : 0.2))); + for (LivingEntity entity : entities) { + EntityDamageUtil.applyChemicalDamage(entity, scalar * (gaseous ? 0.6f : 0.8f)); + } + w.removeBlock(b, false); + }, + (p, f) -> { + Vec3 pos = p.getPosition(1.0f); + p.level().playSound(null, pos.x, pos.y, pos.z, SoundEvents.LAVA_EXTINGUISH, SoundSource.BLOCKS, 1.0F, + 1.0F); + boolean gaseous = f.getFluid().getFluidType().getDensity(f) <= 0; + for (Direction facing : Direction.Plane.HORIZONTAL) { + GTUtil.spawnParticles(p.level(), facing, ParticleTypes.CRIT, p, + 3 + GTValues.RNG.nextInt(2)); + } + GTUtil.spawnParticles(p.level(), gaseous ? Direction.UP : Direction.DOWN, + ParticleTypes.CRIT, + p, 6 + GTValues.RNG.nextInt(4)); + float scalar = (float) Math.log(f.getAmount()); + List entities = p.level().getEntitiesOfClass(LivingEntity.class, + new AABB(BlockPos.containing(p.getPosition(1.0f))).inflate(scalar * (gaseous ? 0.4 : 0.2))); + for (LivingEntity entity : entities) { + if (entity == p) continue; + EntityDamageUtil.applyChemicalDamage(entity, scalar * (gaseous ? 0.6f : 0.8f)); + } + EntityDamageUtil.applyChemicalDamage(p, scalar * (gaseous ? 3f : 4f)); + }); private FluidAttributes() {} } diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/GraphClassRegistrationEvent.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/GraphClassRegistrationEvent.java new file mode 100644 index 0000000000..8e047da6a4 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/GraphClassRegistrationEvent.java @@ -0,0 +1,25 @@ +package com.gregtechceu.gtceu.api.graphnet; + +import net.minecraftforge.eventbus.api.Event; +import net.minecraftforge.fml.event.IModBusEvent; + +import it.unimi.dsi.fastutil.objects.ObjectRBTreeSet; + +import java.util.Comparator; + +public final class GraphClassRegistrationEvent extends Event implements IModBusEvent { + + private final ObjectRBTreeSet> gather = new ObjectRBTreeSet<>( + Comparator.comparing(GraphClassType::getSerializedName)); + + public void accept(GraphClassType type) { + if (!gather.add(type)) + throw new IllegalStateException( + "Detected a name collision during Graph Class registration! Collision on name: " + + type.getSerializedName()); + } + + ObjectRBTreeSet> getGather() { + return gather; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/GraphClassRegistry.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/GraphClassRegistry.java new file mode 100644 index 0000000000..93b2ab537e --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/GraphClassRegistry.java @@ -0,0 +1,92 @@ +package com.gregtechceu.gtceu.api.graphnet; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.graphnet.logic.NetLogicEntry; +import com.gregtechceu.gtceu.api.graphnet.logic.NetLogicType; + +import net.minecraft.client.Minecraft; +import net.minecraft.network.chat.Component; +import net.minecraftforge.common.MinecraftForge; + +import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Set; + +public final class GraphClassRegistry { + + private static final Int2ObjectArrayMap> REGISTRY; + + private static final Object2IntOpenHashMap NAMES_TO_NETWORK_IDS; + + static { + GraphClassRegistrationEvent event = new GraphClassRegistrationEvent(); + MinecraftForge.EVENT_BUS.post(event); + Set> gather = event.getGather(); + NAMES_TO_NETWORK_IDS = new Object2IntOpenHashMap<>(gather.size()); + REGISTRY = new Int2ObjectArrayMap<>(gather.size()); + int id = 1; + for (GraphClassType type : gather) { + NAMES_TO_NETWORK_IDS.put(type.getSerializedName(), id); + REGISTRY.put(id, type); + id++; + } + } + + public static String getName(int networkID) { + return REGISTRY.get(networkID).getSerializedName(); + } + + public static int getNetworkID(@NotNull String name) { + return NAMES_TO_NETWORK_IDS.getInt(name); + } + + public static int getNetworkID(@NotNull NetLogicType type) { + return getNetworkID(type.getSerializedName()); + } + + public static int getNetworkID(@NotNull NetLogicEntry entry) { + return getNetworkID(entry.getType()); + } + + public static @Nullable GraphClassType getTypeNullable(int networkID) { + return REGISTRY.get(networkID); + } + + public static @Nullable GraphClassType getTypeNullable(@NotNull String name) { + return getTypeNullable(getNetworkID(name)); + } + + public static @NotNull GraphClassType getType(int networkID) { + GraphClassType type = REGISTRY.get(networkID); + if (type == null) throwNonexistenceError(); + assert type != null; + return type; + } + + public static @NotNull GraphClassType getType(@NotNull String name) { + return getType(getNetworkID(name)); + } + + public static void throwNonexistenceError() { + if (GTCEu.isClientThread()) disconnect(); + throw new RuntimeException("Could not find the type of an encoded Graph Class. " + + "This suggests that the server and client have different GT versions or modifications."); + } + + public static void throwDecodingError() { + if (GTCEu.isClientThread()) disconnect(); + throw new RuntimeException("Failed to decode an encoded Graph Class. " + + "This suggests that the server and client have different GT versions or modifications."); + } + + private static void disconnect() { + if (Minecraft.getInstance().getConnection() != null) + Minecraft.getInstance().getConnection() + .onDisconnect(Component.translatable("gtceu.universal.net_logic_disconnect")); + } + + private GraphClassRegistry() {} +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/GraphClassType.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/GraphClassType.java new file mode 100644 index 0000000000..b725fbc94a --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/GraphClassType.java @@ -0,0 +1,35 @@ +package com.gregtechceu.gtceu.api.graphnet; + +import com.gregtechceu.gtceu.api.graphnet.net.IGraphNet; + +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.StringRepresentable; + +import org.jetbrains.annotations.NotNull; + +import java.util.function.Function; + +public class GraphClassType implements StringRepresentable { + + private final @NotNull String name; + private final @NotNull Function supplier; + + public GraphClassType(@NotNull ResourceLocation name, @NotNull Function supplier) { + this.name = name.toString(); + this.supplier = supplier; + } + + public GraphClassType(@NotNull String namespace, @NotNull String name, @NotNull Function supplier) { + this.name = namespace + ":" + name; + this.supplier = supplier; + } + + public final T getNew(@NotNull IGraphNet net) { + return supplier.apply(net); + } + + @Override + public final @NotNull String getSerializedName() { + return name; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/GraphNetBacker.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/GraphNetBacker.java new file mode 100644 index 0000000000..d62267a616 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/GraphNetBacker.java @@ -0,0 +1,294 @@ +package com.gregtechceu.gtceu.api.graphnet; + +import com.gregtechceu.gtceu.api.graphnet.graph.GraphEdge; +import com.gregtechceu.gtceu.api.graphnet.graph.GraphVertex; +import com.gregtechceu.gtceu.api.graphnet.graph.INetGraph; +import com.gregtechceu.gtceu.api.graphnet.group.GroupGraphView; +import com.gregtechceu.gtceu.api.graphnet.group.MergeDirection; +import com.gregtechceu.gtceu.api.graphnet.group.NetGroup; +import com.gregtechceu.gtceu.api.graphnet.net.IGraphNet; +import com.gregtechceu.gtceu.api.graphnet.net.NetEdge; +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; +import com.gregtechceu.gtceu.api.graphnet.traverse.EdgeDirection; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; + +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; + +/** + * The bridge between JGraphT graphs and graphnet abstractions. + * Doesn't do any automatic linking, weighting, predicating, etc. Simply handles storing the JGraphT graph to disk, + * interacting with the JGraphT graph, and managing NetGroups. + */ +public final class GraphNetBacker { + + private final IGraphNet backedNet; + private final INetGraph pipeGraph; + private final Object2ObjectOpenHashMap vertexMap; + + public GraphNetBacker(IGraphNet backedNet, INetGraph graph) { + this.backedNet = backedNet; + this.pipeGraph = graph; + this.vertexMap = new Object2ObjectOpenHashMap<>(); + } + + public IGraphNet getBackedNet() { + return backedNet; + } + + public void addNode(NetNode node) { + GraphVertex vertex = new GraphVertex(node); + GraphVertex existing = this.vertexMap.put(node.getEquivalencyData(), vertex); + if (existing != null) getGraph().removeVertex(existing); + getGraph().addVertex(vertex); + backedNet.setDirty(); + } + + @Nullable + public NetNode getNode(Object equivalencyData) { + GraphVertex vertex = this.vertexMap.get(equivalencyData); + return vertex == null ? null : vertex.wrapped; + } + + public boolean removeNode(@Nullable NetNode node) { + if (node != null) { + if (!this.getGraph().containsVertex(node.wrapper)) { + // edge case -- the node's group most likely still has this node registered, + // but the node doesn't actually exist in the graph. + // no idea what causes this, but it happens. + NetGroup group = node.getGroupUnsafe(); + if (group != null) group.removeNode(node); + } + NetGroup group = node.getGroupUnsafe(); + if (group != null) { + group.splitNode(node); + } else this.removeVertex(node.wrapper); + backedNet.setDirty(); + return true; + } else return false; + } + + @ApiStatus.Internal + public void removeVertex(GraphVertex vertex) { + if (this.getGraph().removeVertex(vertex)) { + if (vertex.wrapped == null) return; + this.vertexMap.remove(vertex.wrapped.getEquivalencyData()); + vertex.wrapped.onRemove(); + backedNet.setDirty(); + } + } + + @Nullable + public NetEdge addEdge(@NotNull NetNode source, @NotNull NetNode target, double weight) { + MergeDirection direction = NetGroup.isEdgeAllowed(source, target); + if (!direction.allowsEdgeCreation()) return null; + GraphEdge graphEdge = getGraph().addEdge(source.wrapper, target.wrapper); + if (graphEdge != null) { + getGraph().setEdgeWeight(graphEdge, weight); + assert graphEdge.wrapped != null; + NetGroup.mergeEdge(graphEdge.wrapped, direction); + backedNet.setDirty(); + } + return graphEdge == null ? null : graphEdge.wrapped; + } + + @Nullable + public NetEdge getEdge(@NotNull NetNode source, @NotNull NetNode target) { + GraphEdge graphEdge = getGraph().getEdge(source.wrapper, target.wrapper); + return graphEdge == null ? null : graphEdge.wrapped; + } + + @NotNull + public Iterable getTouchingEdges(@NotNull NetNode node, @NotNull EdgeDirection direction) { + return direction.selectEdges(getGraph(), node.wrapper).stream() + .map(GraphEdge::getWrapped).filter(Objects::nonNull)::iterator; + } + + public boolean removeEdge(@NotNull NetNode source, NetNode target) { + NetGroup group = source.getGroupUnsafe(); + if (group == null) { + // weird since there should always be a group for two joined nodes, but whatever + return removeEdge(source.wrapper, target.wrapper) != null; + } else return group.splitEdge(source, target); + } + + @ApiStatus.Internal + public GraphEdge removeEdge(GraphVertex source, GraphVertex target) { + GraphEdge edge = this.getGraph().removeEdge(source, target); + if (edge != null) { + backedNet.setDirty(); + } + return edge; + } + + @ApiStatus.Internal + public boolean removeEdge(GraphEdge edge) { + if (this.getGraph().removeEdge(edge)) { + backedNet.setDirty(); + return true; + } + return false; + } + + public INetGraph getGraph() { + return pipeGraph; + } + + // PROPOSAL FOR ALTERNATIVE STORAGE MECHANISM TO REDUCE MEMORY COSTS + // > Always loaded & nbt stored data: + // map & weak map of group ids to groups. No references to group objects exist outside of this, only references to + // grou ids. + // (for pipenet) pipes store a reference to their group id + // > Disk-stored data: + // contents of groups, specifically their nodes and edges. + // > Impl (for pipenet) + // When a pipe is loaded, it goes fetch its group and tells it the pipe's chunk. This chunk is added to a *set* of + // chunks that are 'loading' this group. + // When a chunk is unloaded, it is removed from the set of 'loading' chunks for all groups. + // When the set of 'loading' chunks for a group is empty, the group writes its data to disk and removes itself from + // the map and the graph but not the weak map. + // (proposal - create a graph impl that allows for weak references to vertices and edges, in order to remove need + // for explicit removal of group from graph?) + // When a pipe fetches its group, if the group is not found in the map, it then checks the weak map. + // If found in the weak map, the pipe's chunk is added to the 'loading' chunks and a reference to the group is added + // to the map and the contents are added to the graph. + // If not found in the weak map, the group is instead read from disk and initialized. + // > Benefits of this Impl + // By only loading the (potentially) large number of edges and nodes into the graph that a group contains when that + // group is needed, + // the number of unnecessary references in the graphnet on, say, a large server is drastically reduced. + // however, since there are necessarily more read/write actions to disk, the cpu load would increase in turn. + + public void readFromNBT(@NotNull CompoundTag nbt) { + // construct map of node ids -> nodes, while building nodes to groups + // construct edges using map + Int2ObjectOpenHashMap groupMap = new Int2ObjectOpenHashMap<>(); + ListTag vertices = nbt.getList("Vertices", Tag.TAG_COMPOUND); + int vertexCount = vertices.size(); + Int2ObjectOpenHashMap vertexMap = new Int2ObjectOpenHashMap<>(vertexCount); + for (int i = 0; i < vertexCount; i++) { + CompoundTag tag = vertices.getCompound(i); + GraphClassType type = GraphClassRegistry.getTypeNullable(tag.getString("ClassType")); + Object o = type == null ? null : type.getNew(backedNet); + NetNode node = o instanceof NetNode n ? n : backedNet.getDefaultNodeType().getNew(backedNet); + node.deserializeNBT(tag); + if (tag.contains("GroupID")) { + int id = tag.getInt("GroupID"); + NetGroup group = groupMap.get(id); + if (group == null) { + group = new NetGroup(this.backedNet); + groupMap.put(id, group); + } + group.addNode(node); + } + GraphVertex vertex = new GraphVertex(node); + this.getGraph().addVertex(vertex); + vertexMap.put(i, vertex); + this.vertexMap.put(node.getEquivalencyData(), vertex); + } + + ListTag edges = nbt.getList("Edges", Tag.TAG_COMPOUND); + int edgeCount = edges.size(); + for (int i = 0; i < edgeCount; i++) { + CompoundTag tag = edges.getCompound(i); + GraphClassType type = GraphClassRegistry.getTypeNullable(tag.getString("ClassType")); + Object o = type == null ? null : type.getNew(backedNet); + GraphEdge graphEdge = new GraphEdge( + o instanceof NetEdge e ? e : backedNet.getDefaultEdgeType().getNew(backedNet)); + this.getGraph().addEdge(vertexMap.get(tag.getInt("SourceID")), + vertexMap.get(tag.getInt("TargetID")), graphEdge); + this.getGraph().setEdgeWeight(graphEdge, tag.getDouble("Weight")); + assert graphEdge.wrapped != null; + graphEdge.wrapped.deserializeNBT(tag); + } + } + + @Contract("_ -> param1") + public @NotNull CompoundTag writeToNBT(CompoundTag compound) { + // map of net groups -> autogenerated ids; + // tag of autogenerated vertex ids -> node nbt & group id + // tag of autogenerated edge ids -> edge nbt & source/target ids + Object2IntOpenHashMap groupMap = new Object2IntOpenHashMap<>(); + Object2IntOpenHashMap vertexMap = new Object2IntOpenHashMap<>(); + int i = 0; + int g = 0; + ListTag vertices = new ListTag(); + for (GraphVertex graphVertex : this.getGraph().vertexSet()) { + if (graphVertex.wrapped == null) continue; + vertexMap.put(graphVertex, i); + NetGroup group = graphVertex.wrapped.getGroupUnsafe(); + CompoundTag tag = graphVertex.wrapped.serializeNBT(); + tag.putString("ClassType", graphVertex.wrapped.getType().getSerializedName()); + if (group != null) { + int groupID; + if (!groupMap.containsKey(group)) { + groupMap.put(group, g); + groupID = g; + g++; + } else groupID = groupMap.getInt(group); + tag.putInt("GroupID", groupID); + } + vertices.add(tag); + i++; + } + compound.put("Vertices", vertices); + + ListTag edges = new ListTag(); + for (GraphEdge graphEdge : this.getGraph().edgeSet()) { + if (graphEdge.wrapped == null) continue; + CompoundTag tag = graphEdge.wrapped.serializeNBT(); + tag.putInt("SourceID", vertexMap.getInt(graphEdge.getSource())); + tag.putInt("TargetID", vertexMap.getInt(graphEdge.getTarget())); + tag.putDouble("Weight", graphEdge.getWeight()); + tag.putString("ClassType", graphEdge.wrapped.getType().getSerializedName()); + edges.add(tag); + } + compound.put("Edges", edges); + + return compound; + } + + public static CompoundTag writeGroupToNBT(NetGroup group) { + CompoundTag compound = new CompoundTag(); + GroupGraphView graph = group.getGraphView(); + // map of net groups -> autogenerated ids; + // tag of autogenerated vertex ids -> node nbt & group id + // tag of autogenerated edge ids -> edge nbt & source/target ids + Object2IntOpenHashMap vertexMap = new Object2IntOpenHashMap<>(); + int i = 0; + ListTag vertices = new ListTag(); + for (GraphVertex graphVertex : graph.vertexSet()) { + if (graphVertex.wrapped == null) continue; + vertexMap.put(graphVertex, i); + CompoundTag tag = graphVertex.wrapped.serializeNBT(); + tag.putString("ClassType", graphVertex.wrapped.getType().getSerializedName()); + vertices.add(tag); + i++; + } + compound.put("Vertices", vertices); + + ListTag edges = new ListTag(); + for (GraphEdge graphEdge : graph.edgeSet()) { + if (graphEdge.wrapped == null) continue; + CompoundTag tag = graphEdge.wrapped.serializeNBT(); + tag.putInt("SourceID", vertexMap.getInt(graphEdge.getSource())); + tag.putInt("TargetID", vertexMap.getInt(graphEdge.getTarget())); + tag.putDouble("Weight", graphEdge.getWeight()); + tag.putString("ClassType", graphEdge.wrapped.getType().getSerializedName()); + edges.add(tag); + } + compound.put("Edges", edges); + + return compound; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/GraphNetUtility.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/GraphNetUtility.java new file mode 100644 index 0000000000..25cb5806fe --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/GraphNetUtility.java @@ -0,0 +1,97 @@ +package com.gregtechceu.gtceu.api.graphnet; + +import com.gregtechceu.gtceu.api.graphnet.graph.GraphEdge; +import com.gregtechceu.gtceu.api.graphnet.net.NetEdge; +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; +import com.gregtechceu.gtceu.api.graphnet.predicate.test.IPredicateTestObject; +import com.gregtechceu.gtceu.api.graphnet.traverse.ResilientNetClosestIterator; +import com.gregtechceu.gtceu.utils.MapUtil; + +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; + +import java.util.ArrayDeque; +import java.util.function.ObjIntConsumer; +import java.util.function.Predicate; +import java.util.function.ToIntFunction; + +public final class GraphNetUtility { + + private GraphNetUtility() {} + + public static int p2pWalk(boolean simulate, int available, ToIntFunction limit, + ObjIntConsumer report, + ResilientNetClosestIterator forwardFrontier, + ResilientNetClosestIterator backwardFrontier) { + Object2IntOpenHashMap flowLimitCache = new Object2IntOpenHashMap<>(); + int actual = 0; + main: + while (forwardFrontier.hasNext() || backwardFrontier.hasNext()) { + if (available <= 0) break; + NetNode next = null; + if (forwardFrontier.hasNext()) { + next = forwardFrontier.next(); + if (MapUtil.computeIfAbsent(flowLimitCache, next, limit) <= 0) { + forwardFrontier.markInvalid(next); + next = null; + } + } + if (next == null || !backwardFrontier.hasSeen(next)) { + if (backwardFrontier.hasNext()) { + next = backwardFrontier.next(); + if (MapUtil.computeIfAbsent(flowLimitCache, next, limit) <= 0) { + backwardFrontier.markInvalid(next); + continue; + } + if (!forwardFrontier.hasSeen(next)) continue; + } else continue; + } + // next is not null and both frontiers have paths leading to next + int allowed = available; + NetEdge span; + NetNode trace = next; + ArrayDeque seen = new ArrayDeque<>(); + seen.add(next); + while ((span = forwardFrontier.getSpanningTreeEdge(trace)) != null) { + trace = span.getOppositeNode(trace); + if (trace == null) continue main; + int l = MapUtil.computeIfAbsent(flowLimitCache, trace, limit); + if (l == 0) { + // shouldn't happen + forwardFrontier.markInvalid(trace); + continue main; + } + allowed = Math.min(allowed, l); + seen.addFirst(trace); + } + trace = next; + while ((span = backwardFrontier.getSpanningTreeEdge(trace)) != null) { + trace = span.getOppositeNode(trace); + if (trace == null) continue main; + int l = MapUtil.computeIfAbsent(flowLimitCache, trace, limit); + if (l == 0) { + // shouldn't happen + backwardFrontier.markInvalid(trace); + continue main; + } + allowed = Math.min(allowed, l); + seen.addLast(trace); + } + available -= allowed; + actual += allowed; + for (NetNode n : seen) { + if (!simulate) report.accept(n, allowed); + int remaining = flowLimitCache.getInt(n) - allowed; + flowLimitCache.put(n, remaining); + if (remaining <= 0) { + forwardFrontier.markInvalid(n); + backwardFrontier.markInvalid(n); + } + } + } + return actual; + } + + public static Predicate standardEdgeBlacklist(IPredicateTestObject testObject) { + return o -> o instanceof GraphEdge e && e.wrapped != null && !e.wrapped.test(testObject); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/MultiNodeHelper.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/MultiNodeHelper.java new file mode 100644 index 0000000000..bf4d884c50 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/MultiNodeHelper.java @@ -0,0 +1,135 @@ +package com.gregtechceu.gtceu.api.graphnet; + +import com.gregtechceu.gtceu.api.graphnet.logic.INetLogicEntryListener; +import com.gregtechceu.gtceu.api.graphnet.logic.NetLogicData; +import com.gregtechceu.gtceu.api.graphnet.logic.NetLogicEntry; +import com.gregtechceu.gtceu.api.graphnet.net.IGraphNet; +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNode; + +import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import org.jetbrains.annotations.NotNull; + +import java.lang.ref.WeakReference; +import java.util.List; + +/** + * MultiNodeHelpers are utility objects used to preserve sync between multiple nodes owned by different graphs. They do + * this by
+ * A) keeping a record of traversals to allow for blocking traversal when another net has been traversed + * recently and
+ * B) making sure that logic entries requiring it are the same object across all synced nodes.
+ *
+ * MultiNodeHelpers have no standard implementation and must be handled by a net and its nodes; see + * {@link com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNet} and {@link WorldPipeNode} + * for an example of this in action. + */ +public class MultiNodeHelper implements INetLogicEntryListener { + + protected final Object2ObjectOpenHashMap handledDatas = new Object2ObjectOpenHashMap<>(); + + protected final Object2LongOpenHashMap recentTransferNets = new Object2LongOpenHashMap<>(); + protected final int transferTimeout; + + protected final NetLogicData mergedData = new NetLogicData(); + + public MultiNodeHelper(int transferTimeout) { + this.transferTimeout = transferTimeout; + } + + public boolean traverse(IGraphNet net, long queryTick, boolean simulate) { + var iter = recentTransferNets.object2LongEntrySet().fastIterator(); + boolean allowed = true; + while (iter.hasNext()) { + var next = iter.next(); + if (net.clashesWith(next.getKey())) { + if (next.getLongValue() <= queryTick) { + iter.remove(); + } else { + allowed = false; + break; + } + } + } + if (allowed && !simulate) { + recentTransferNets.put(net, queryTick + transferTimeout); + } + return allowed; + } + + @Override + public void markLogicEntryAsUpdated(NetLogicEntry entry, boolean fullChange) { + // TODO have a helper or something on clientside to avoid redundant packets + handledDatas.forEach((k, v) -> v.data.markLogicEntryAsUpdated(entry, fullChange)); + } + + public void addNode(@NotNull NetNode node) { + List> toSet = new ObjectArrayList<>(); + for (NetLogicEntry entry : node.getData().getEntries()) { + if (entry.mergedToMultiNodeHelper()) { + NetLogicEntry existing = mergedData.getLogicEntryNullable(entry.getType()); + if (existing != null) { + existing.merge(node, entry); + // don't put it into the data yet because we're currently iterating through the data's entries. + toSet.add(existing); + } else { + addNewLogicEntry(entry); + } + } + } + handledDatas.put(node.getNet(), new LogicDataHandler(node)); + for (NetLogicEntry entry : toSet) { + node.getData().setLogicEntry(entry); + } + } + + public void removeNode(@NotNull NetNode node) { + LogicDataHandler removed = handledDatas.remove(node.getNet()); + if (removed != null) { + for (NetLogicEntry entry : this.mergedData.getEntries()) { + node.getData().removeLogicEntry(entry); + entry.unmerge(node); + } + } + } + + private void addNewLogicEntry(@NotNull NetLogicEntry entry) { + entry.registerToMultiNodeHelper(this); + mergedData.setLogicEntry(entry); + handledDatas.values().forEach(h -> h.data.setLogicEntry(entry)); + } + + protected class LogicDataHandler implements NetLogicData.ILogicDataListener { + + public final WeakReference nodeRef; + public final @NotNull NetLogicData data; + + public LogicDataHandler(@NotNull NetNode node) { + this.data = node.getData(); + data.addListener(this); + this.nodeRef = new WeakReference<>(node); + } + + @Override + public void markChanged(NetLogicEntry updatedEntry, boolean removed, boolean fullChange) { + if (!fullChange || !updatedEntry.mergedToMultiNodeHelper()) return; + NetNode node = nodeRef.get(); + if (node == null) return; + NetLogicEntry existing = mergedData.getLogicEntryNullable(updatedEntry.getType()); + if (removed) { + if (existing != null) mergedData.removeLogicEntry(existing); + } else { + if (existing != null) { + if (existing != updatedEntry) { + existing.merge(node, updatedEntry); + data.setLogicEntry(existing); + } + } else { + addNewLogicEntry(updatedEntry); + } + } + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/graph/GraphEdge.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/graph/GraphEdge.java new file mode 100644 index 0000000000..ce3c986c1e --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/graph/GraphEdge.java @@ -0,0 +1,75 @@ +package com.gregtechceu.gtceu.api.graphnet.graph; + +import com.gregtechceu.gtceu.api.graphnet.net.NetEdge; + +import lombok.Getter; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jgrapht.graph.DefaultWeightedEdge; + +import java.util.Objects; + +public final class GraphEdge extends DefaultWeightedEdge { + + @ApiStatus.Internal + @Getter + public final NetEdge wrapped; + + public GraphEdge(@NotNull NetEdge wrapped) { + this.wrapped = wrapped; + wrapped.wrapper = this; + } + + @Nullable + @Contract("null->null") + public static GraphEdge unwrap(NetEdge e) { + return e == null ? null : e.wrapper; + } + + @ApiStatus.Internal + public GraphEdge() { + this.wrapped = null; + } + + @Override + public GraphVertex getSource() { + return (GraphVertex) super.getSource(); + } + + @Override + public GraphVertex getTarget() { + return (GraphVertex) super.getTarget(); + } + + public @Nullable GraphVertex getOppositeVertex(@NotNull GraphVertex node) { + if (getSource() == node) return getTarget(); + else if (getTarget() == node) return getSource(); + else return null; + } + + /** + * Use this very sparingly. It's significantly better to go through {@link org.jgrapht.Graph#getEdgeWeight(Object)} + * instead, unless you are doing nbt serialization for example. + * + * @return the edge weight. + */ + @Override + public double getWeight() { + return super.getWeight(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + GraphEdge graphEdge = (GraphEdge) o; + return Objects.equals(getSource(), graphEdge.getSource()) && Objects.equals(getTarget(), graphEdge.getTarget()); + } + + @Override + public int hashCode() { + return Objects.hash(wrapped); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/graph/GraphVertex.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/graph/GraphVertex.java new file mode 100644 index 0000000000..e2c3b22359 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/graph/GraphVertex.java @@ -0,0 +1,47 @@ +package com.gregtechceu.gtceu.api.graphnet.graph; + +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; + +import lombok.Getter; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; + +public final class GraphVertex { + + @ApiStatus.Internal + @Getter + public final NetNode wrapped; + + public GraphVertex(@NotNull NetNode wrapped) { + this.wrapped = wrapped; + wrapped.wrapper = this; + } + + @ApiStatus.Internal + public GraphVertex() { + wrapped = null; + } + + @Nullable + @Contract("null->null") + public static GraphVertex unwrap(NetNode n) { + return n == null ? null : n.wrapper; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + GraphVertex graphVertex = (GraphVertex) o; + return Objects.equals(wrapped, graphVertex.wrapped); + } + + @Override + public int hashCode() { + return Objects.hash(wrapped); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/graph/INetGraph.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/graph/INetGraph.java new file mode 100644 index 0000000000..01480f8d18 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/graph/INetGraph.java @@ -0,0 +1,10 @@ +package com.gregtechceu.gtceu.api.graphnet.graph; + +import org.jgrapht.Graph; + +public interface INetGraph extends Graph { + + default boolean isDirected() { + return getType().isDirected(); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/graph/NetDirectedGraph.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/graph/NetDirectedGraph.java new file mode 100644 index 0000000000..5f75bb8435 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/graph/NetDirectedGraph.java @@ -0,0 +1,21 @@ +package com.gregtechceu.gtceu.api.graphnet.graph; + +import com.gregtechceu.gtceu.api.graphnet.net.IGraphNet; + +import org.jgrapht.graph.SimpleDirectedWeightedGraph; + +import java.util.function.Function; +import java.util.function.Supplier; + +public class NetDirectedGraph extends SimpleDirectedWeightedGraph implements INetGraph { + + public NetDirectedGraph(Supplier vertexSupplier, Supplier edgeSupplier) { + super(vertexSupplier, edgeSupplier); + } + + public static Function standardBuilder() { + return iGraphNet -> new NetDirectedGraph( + () -> new GraphVertex(iGraphNet.getDefaultNodeType().getNew(iGraphNet)), + () -> new GraphEdge(iGraphNet.getDefaultEdgeType().getNew(iGraphNet))); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/graph/NetUndirectedGraph.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/graph/NetUndirectedGraph.java new file mode 100644 index 0000000000..5dad2d3bc2 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/graph/NetUndirectedGraph.java @@ -0,0 +1,21 @@ +package com.gregtechceu.gtceu.api.graphnet.graph; + +import com.gregtechceu.gtceu.api.graphnet.net.IGraphNet; + +import org.jgrapht.graph.SimpleWeightedGraph; + +import java.util.function.Function; +import java.util.function.Supplier; + +public class NetUndirectedGraph extends SimpleWeightedGraph implements INetGraph { + + public NetUndirectedGraph(Supplier vertexSupplier, Supplier edgeSupplier) { + super(vertexSupplier, edgeSupplier); + } + + public static Function standardBuilder() { + return iGraphNet -> new NetUndirectedGraph( + () -> new GraphVertex(iGraphNet.getDefaultNodeType().getNew(iGraphNet)), + () -> new GraphEdge(iGraphNet.getDefaultEdgeType().getNew(iGraphNet))); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/group/GroupData.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/group/GroupData.java new file mode 100644 index 0000000000..4847f0ada1 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/group/GroupData.java @@ -0,0 +1,97 @@ +package com.gregtechceu.gtceu.api.graphnet.group; + +import com.gregtechceu.gtceu.api.graphnet.net.NetEdge; +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; + +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.MustBeInvokedByOverriders; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Set; + +public abstract class GroupData { + + private static final Pair EMPTY = ImmutablePair.of(null, null); + + private NetGroup group; + + @MustBeInvokedByOverriders + public void withGroup(@NotNull NetGroup group) { + this.group = group; + } + + @Nullable + public NetGroup getGroup() { + return group; + } + + /** + * Used to determine if merging two groups is allowed. Will be called in both directions. If the merge is allowed, + * {@link #mergeAcross(GroupData, NetEdge)} will be called later after the graph is modified. + * + * @param other the group data of the other group + * @return whether they can be merged. Completely blocks edge creation if false. + */ + protected boolean mergeAllowed(@Nullable GroupData other) { + return other == null || other.getClass() == this.getClass(); + } + + /** + * Used to determine if merging two groups is allowed. Will test both directions. If the merge is allowed, + * {@link #mergeAcross(GroupData, NetEdge)} will be called later after the graph is modified. + * + * @param source the first group data + * @param target the second group data + * @return which datas authorized the merge. Completely blocks edge creation if none. + */ + @NotNull + public static MergeDirection mergeAllowed(@Nullable GroupData source, @Nullable GroupData target) { + if (source != null && source.mergeAllowed(target)) return MergeDirection.SOURCE; + if (target != null && target.mergeAllowed(source)) return MergeDirection.TARGET; + return (source == null && target == null) ? MergeDirection.NULL : MergeDirection.NONE; + } + + /** + * Called when a new edge bridges the interior of a net group rather than connecting two separate net groups. + * + * @param edge the bridging edge + */ + public void notifyOfBridgingEdge(@NotNull NetEdge edge) {} + + /** + * Called when an edge belonging to a group is removed, before the graph is modified. If this splits the group, + * {@link #splitAcross(Set, Set)} will be called later after the graph is modified. + * + * @param edge the edge removed. + */ + public void notifyOfRemovedEdge(@NotNull NetEdge edge) {} + + /** + * Merge data across an edge. Accompanies the process of merging groups. + * + * @param other the group data to merge with. + * @param edge the edge merged across + * @return the result of the merge. + */ + @Nullable + protected GroupData mergeAcross(@Nullable GroupData other, @NotNull NetEdge edge) { + if (other != null) return null; + else return this; + } + + /** + * Split data across an edge. Accompanies the process of splitting groups. + * + * @param sourceNodes the first set of nodes. + * @param targetNodes the second set of nodes. + * @return a pair, where the first value is the group data associated with the first set of nodes, and the second + * value is the group data associated with the second set of nodes. + */ + @NotNull + public Pair splitAcross(@NotNull Set sourceNodes, + @NotNull Set targetNodes) { + return EMPTY; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/group/GroupGraphView.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/group/GroupGraphView.java new file mode 100644 index 0000000000..1eeab2d05a --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/group/GroupGraphView.java @@ -0,0 +1,260 @@ +package com.gregtechceu.gtceu.api.graphnet.group; + +import com.gregtechceu.gtceu.api.graphnet.graph.GraphEdge; +import com.gregtechceu.gtceu.api.graphnet.graph.GraphVertex; +import com.gregtechceu.gtceu.api.graphnet.graph.INetGraph; +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; + +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import org.jetbrains.annotations.NotNull; +import org.jgrapht.Graph; +import org.jgrapht.GraphType; + +import java.util.*; +import java.util.function.Supplier; + +public class GroupGraphView implements Graph { + + protected final @NotNull NetGroup group; + + protected final EdgeSetView edgeView = new EdgeSetView(); + + protected final Set addedVertices = new ObjectOpenHashSet<>(); + + public GroupGraphView(@NotNull NetGroup group) { + this.group = group; + } + + protected INetGraph backer() { + return group.net.getGraph(); + } + + @Override + public Set getAllEdges(GraphVertex sourceVertex, GraphVertex targetVertex) { + return backer().getAllEdges(sourceVertex, targetVertex); + } + + @Override + public GraphEdge getEdge(GraphVertex sourceVertex, GraphVertex targetVertex) { + return backer().getEdge(sourceVertex, targetVertex); + } + + @Override + public Supplier getVertexSupplier() { + return backer().getVertexSupplier(); + } + + @Override + public Supplier getEdgeSupplier() { + return backer().getEdgeSupplier(); + } + + @Override + public GraphEdge addEdge(GraphVertex sourceVertex, GraphVertex targetVertex) { + return backer().addEdge(sourceVertex, targetVertex); + } + + @Override + public boolean addEdge(GraphVertex sourceVertex, GraphVertex targetVertex, GraphEdge graphEdge) { + return backer().addEdge(sourceVertex, targetVertex, graphEdge); + } + + @Override + public GraphVertex addVertex() { + GraphVertex vertex = backer().addVertex(); + addedVertices.add(vertex); + return vertex; + } + + @Override + public boolean addVertex(GraphVertex vertex) { + addedVertices.add(vertex); + return backer().addVertex(vertex); + } + + @Override + public boolean containsEdge(GraphVertex sourceVertex, GraphVertex targetVertex) { + return containsVertex(sourceVertex) && containsVertex(targetVertex) && + backer().containsEdge(sourceVertex, targetVertex); + } + + @Override + public boolean containsEdge(GraphEdge graphEdge) { + return containsVertex(graphEdge.getSource()) && containsVertex(graphEdge.getTarget()) && + backer().containsEdge(graphEdge); + } + + @Override + public boolean containsVertex(GraphVertex vertex) { + return addedVertices.contains(vertex) || group.getNodes().contains(NetNode.unwrap(vertex)); + } + + @Override + public Set edgeSet() { + return edgeView; + } + + @Override + public int degreeOf(GraphVertex vertex) { + if (backer().isDirected()) return inDegreeOf(vertex) + outDegreeOf(vertex); + int degree = 0; + Set edges = backer().edgesOf(vertex); + for (GraphEdge e : edges) { + if (!containsEdge(e)) continue; + if (backer().getEdgeSource(e).equals(backer().getEdgeTarget(e))) { + degree += 2; + } else { + degree += 1; + } + } + return degree; + } + + @Override + public Set edgesOf(GraphVertex vertex) { + Set s = new ObjectOpenHashSet<>(backer().edgesOf(vertex)); + s.removeIf(e -> !containsEdge(e)); + return s; + } + + @Override + public int inDegreeOf(GraphVertex vertex) { + if (!backer().isDirected()) return degreeOf(vertex); + return incomingEdgesOf(vertex).size(); + } + + @Override + public Set incomingEdgesOf(GraphVertex vertex) { + Set s = new ObjectOpenHashSet<>(backer().incomingEdgesOf(vertex)); + s.removeIf(e -> !containsEdge(e)); + return s; + } + + @Override + public int outDegreeOf(GraphVertex vertex) { + if (!backer().isDirected()) return degreeOf(vertex); + return outgoingEdgesOf(vertex).size(); + } + + @Override + public Set outgoingEdgesOf(GraphVertex vertex) { + Set s = new ObjectOpenHashSet<>(backer().outgoingEdgesOf(vertex)); + s.removeIf(e -> !containsEdge(e)); + return s; + } + + @Override + public boolean removeAllEdges(Collection edges) { + return backer().removeAllEdges(edges); + } + + @Override + public Set removeAllEdges(GraphVertex sourceVertex, GraphVertex targetVertex) { + return backer().removeAllEdges(sourceVertex, targetVertex); + } + + @Override + public boolean removeAllVertices(Collection vertices) { + return backer().removeAllVertices(vertices) | addedVertices.removeAll(vertices); + } + + @Override + public GraphEdge removeEdge(GraphVertex sourceVertex, GraphVertex targetVertex) { + return backer().removeEdge(sourceVertex, targetVertex); + } + + @Override + public boolean removeEdge(GraphEdge graphEdge) { + return backer().removeEdge(graphEdge); + } + + @Override + public boolean removeVertex(GraphVertex vertex) { + return backer().removeVertex(vertex) | addedVertices.remove(vertex); + } + + @Override + public Set vertexSet() { + Set set = new ObjectOpenHashSet<>(group.getNodes().size() + addedVertices.size()); + set.addAll(addedVertices); + for (NetNode node : group.getNodes()) { + set.add(GraphVertex.unwrap(node)); + } + return set; + } + + @Override + public GraphVertex getEdgeSource(GraphEdge graphEdge) { + return backer().getEdgeSource(graphEdge); + } + + @Override + public GraphVertex getEdgeTarget(GraphEdge graphEdge) { + return backer().getEdgeTarget(graphEdge); + } + + @Override + public GraphType getType() { + return backer().getType(); + } + + @Override + public double getEdgeWeight(GraphEdge graphEdge) { + return backer().getEdgeWeight(graphEdge); + } + + @Override + public void setEdgeWeight(GraphEdge graphEdge, double weight) { + backer().setEdgeWeight(graphEdge, weight); + } + + @Override + public void setEdgeWeight(GraphVertex sourceVertex, GraphVertex targetVertex, double weight) { + backer().setEdgeWeight(sourceVertex, targetVertex, weight); + } + + private final class EdgeSetView extends AbstractSet { + + @Override + public @NotNull Iterator iterator() { + return new Iterator<>() { + + final Iterator backer = group.net.getGraph().edgeSet().iterator(); + GraphEdge next; + + @Override + public boolean hasNext() { + if (next != null) return true; + return calcNext(); + } + + @Override + public GraphEdge next() { + if (next == null) { + if (!calcNext()) throw new NoSuchElementException(); + } + GraphEdge e = next; + next = null; + return e; + } + + private boolean calcNext() { + do { + if (!backer.hasNext()) return false; + next = backer.next(); + } while (!containsEdge(next)); + return true; + } + }; + } + + @Override + public int size() { + int size = 0; + for (GraphEdge ignored : this) { + size++; + } + return size; + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/group/MergeDirection.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/group/MergeDirection.java new file mode 100644 index 0000000000..1c8e11940d --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/group/MergeDirection.java @@ -0,0 +1,21 @@ +package com.gregtechceu.gtceu.api.graphnet.group; + +public enum MergeDirection { + + NONE, + SOURCE, + TARGET, + NULL; + + public boolean allowsEdgeCreation() { + return this != NONE; + } + + public boolean source() { + return this == SOURCE; + } + + public boolean target() { + return this == TARGET; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/group/NetGroup.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/group/NetGroup.java new file mode 100644 index 0000000000..f72ec73675 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/group/NetGroup.java @@ -0,0 +1,265 @@ +package com.gregtechceu.gtceu.api.graphnet.group; + +import com.gregtechceu.gtceu.api.graphnet.graph.GraphEdge; +import com.gregtechceu.gtceu.api.graphnet.net.IGraphNet; +import com.gregtechceu.gtceu.api.graphnet.net.NetEdge; +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; +import com.gregtechceu.gtceu.api.graphnet.traverse.EdgeDirection; +import com.gregtechceu.gtceu.api.graphnet.traverse.NetBreadthIterator; + +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnmodifiableView; +import org.jgrapht.Graphs; + +import java.util.Collection; +import java.util.Collections; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public final class NetGroup { + + public final IGraphNet net; + + final @NotNull Set nodes; + + private final @NotNull Int2ObjectMap> sortingNodes; + + private @Nullable GroupData data; + + private GroupGraphView graphView; + + public NetGroup(IGraphNet net) { + this(net, new ObjectOpenHashSet<>(), new Int2ObjectOpenHashMap<>()); + } + + public NetGroup(@NotNull IGraphNet net, @NotNull Set nodes) { + this(net, nodes, net.getBlankGroupData()); + } + + public NetGroup(@NotNull IGraphNet net, @NotNull Set nodes, @Nullable GroupData data) { + this(net, nodes, new Int2ObjectOpenHashMap<>(), data); + for (NetNode node : nodes) { + initialSort(node); + } + } + + public NetGroup(@NotNull IGraphNet net, @NotNull Set nodes, + @NotNull Int2ObjectMap> sortingNodes) { + this(net, nodes, sortingNodes, net.getBlankGroupData()); + } + + public NetGroup(@NotNull IGraphNet net, @NotNull Set nodes, + @NotNull Int2ObjectMap> sortingNodes, @Nullable GroupData data) { + this.net = net; + this.data = data; + if (data != null) data.withGroup(this); + this.nodes = nodes; + this.sortingNodes = sortingNodes; + nodes.forEach(this::onAddedToGroup); + } + + private void initialSort(NetNode node) { + int key = node.getSortingKey(); + Set s = this.sortingNodes.get(key); + if (s == null) this.sortingNodes.put(key, s = new ObjectOpenHashSet<>()); + s.add(node); + } + + public void addNode(NetNode node) { + this.nodes.add(node); + onAddedToGroup(node); + initialSort(node); + } + + private void addNodes(Collection nodes) { + this.nodes.addAll(nodes); + for (NetNode node : nodes) { + onAddedToGroup(node); + initialSort(node); + } + } + + @ApiStatus.Internal + public void removeNode(NetNode node) { + this.nodes.remove(node); + } + + private void removeNodes(Collection nodes) { + this.nodes.removeAll(nodes); + } + + private void clearNodes() { + this.nodes.clear(); + } + + private void onAddedToGroup(@NotNull NetNode node) { + node.setGroup(this); + } + + public void notifySortingChange(NetNode node, int oldKey, int newKey) { + Set old = this.sortingNodes.get(oldKey); + if (old != null) { + old.remove(node); + if (old.size() == 0) this.sortingNodes.remove(oldKey); + } + Set n = this.sortingNodes.get(newKey); + if (n == null) this.sortingNodes.put(newKey, n = new ObjectOpenHashSet<>()); + n.add(node); + } + + public static MergeDirection isEdgeAllowed(@NotNull NetNode source, @NotNull NetNode target) { + NetGroup sourceGroup = source.getGroupUnsafe(); + NetGroup targetGroup = target.getGroupUnsafe(); + + if (sourceGroup == null || targetGroup == null || sourceGroup == targetGroup) return MergeDirection.NULL; + return GroupData.mergeAllowed(sourceGroup.getData(), targetGroup.getData()); + } + + /** + * Merges the groups on either side of an edge if necessary. + * + * @param edge the edge to merge across + */ + public static void mergeEdge(@NotNull NetEdge edge, @NotNull MergeDirection direction) { + NetNode source = edge.getSource(); + NetNode target = edge.getTarget(); + assert source != null; + assert target != null; + NetGroup sourceGroup = source.getGroupUnsafe(); + NetGroup targetGroup = target.getGroupUnsafe(); + if (sourceGroup == targetGroup) { + if (sourceGroup == null) { + sourceGroup = source.getGroupSafe(); + } else { + GroupData data = sourceGroup.getData(); + if (data != null) data.notifyOfBridgingEdge(edge); + return; + } + } + if (sourceGroup != null) { + sourceGroup.mergeNode(target, edge, direction.source()); + } else { + targetGroup.mergeNode(source, edge, direction.target()); + } + } + + private void mergeNode(@NotNull NetNode node, @NotNull NetEdge edge, boolean dataMergeTo) { + NetGroup group = node.getGroupUnsafe(); + if (group != null) { + this.addNodes(group.getNodes()); + GroupData data = group.getData(); + if (data != null) { + if (this.data == null) this.data = data; + else this.data = dataMergeTo ? this.data.mergeAcross(data, edge) : data.mergeAcross(this.data, edge); + } + } else addNode(node); + } + + /** + * Split this group by removing a node. Automatically removes the node from the backing graph. + * + * @param source node to remove + */ + public void splitNode(NetNode source) { + if (!this.net.containsNode(source)) return; + Stream stream = this.net.getGraph().edgesOf(source.wrapper).stream(); + GroupData data = getData(); + if (data != null) stream = stream.peek(e -> data.notifyOfRemovedEdge(e.wrapped)); + ObjectLinkedOpenHashSet targets = stream + .map(a -> Graphs.getOppositeVertex(net.getGraph(), a, source.wrapper).getWrapped()) + .filter(Objects::nonNull) + .collect(Collectors.toCollection(ObjectLinkedOpenHashSet::new)); + this.net.getBacker().removeVertex(source.wrapper); + this.removeNode(source); + while (!targets.isEmpty()) { + NetNode target = targets.removeLast(); + Set targetGroup = new ObjectOpenHashSet<>(); + NetBreadthIterator i = new NetBreadthIterator(target, EdgeDirection.ALL); + NetNode temp; + while (i.hasNext()) { + temp = i.next(); + if (temp == source) continue; + targetGroup.add(temp); + // if we find a target node in our search, remove it from the list + targets.remove(temp); + } + this.removeNodes(targetGroup); + if (!targetGroup.isEmpty()) { + new NetGroup(this.net, targetGroup); + } + } + } + + /** + * Split this group by removing an edge. Automatically removes the edge from the graph. + * + * @param source source of the edge + * @param target target of the edge + * @return Whether the edge existed in the graph + */ + public boolean splitEdge(@NotNull NetNode source, @NotNull NetNode target) { + GroupData data = getData(); + NetEdge edge = this.net.getEdge(source, target); + if (edge == null) return false; + if (data != null) data.notifyOfRemovedEdge(edge); + if (this.net.getBacker().removeEdge(source.wrapper, target.wrapper) != null) { + Set targetGroup = new ObjectOpenHashSet<>(); + NetBreadthIterator i = new NetBreadthIterator(target, EdgeDirection.ALL); + NetNode temp; + while (i.hasNext()) { + temp = i.next(); + // if there's another complete path to the source node from the target node, there's no need to split + if (source == temp) return true; + targetGroup.add(temp); + } + this.removeNodes(targetGroup); + if (targetGroup.size() != 0) { + if (data == null) new NetGroup(this.net, targetGroup); + else { + Pair split = data.splitAcross(this.nodes, targetGroup); + this.data = split.getLeft(); + new NetGroup(this.net, targetGroup, split.getRight()); + } + } + return true; + } + return false; + } + + @NotNull + @UnmodifiableView + public Set getNodes() { + return nodes; + } + + @NotNull + @UnmodifiableView + public Set getNodesUnderKey(int key) { + Set set = sortingNodes.get(key); + return set == null ? Collections.emptySet() : set; + } + + @NotNull + @UnmodifiableView + public Int2ObjectMap> getSortingNodes() { + return sortingNodes; + } + + public @Nullable GroupData getData() { + return this.data; + } + + public GroupGraphView getGraphView() { + if (graphView == null) graphView = new GroupGraphView(this); + return graphView; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/group/NodeCacheGroupData.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/group/NodeCacheGroupData.java new file mode 100644 index 0000000000..f8f5cca044 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/group/NodeCacheGroupData.java @@ -0,0 +1,43 @@ +package com.gregtechceu.gtceu.api.graphnet.group; + +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.NotNull; + +import java.util.Set; + +public abstract class NodeCacheGroupData extends GroupData { + + protected final @NotNull Object2ObjectOpenHashMap cache; + + public NodeCacheGroupData() { + this(new Object2ObjectOpenHashMap<>()); + } + + public NodeCacheGroupData(@NotNull Object2ObjectOpenHashMap cache) { + this.cache = cache; + } + + @NotNull + public T getOrCreate(@NotNull NetNode node) { + return cache.computeIfAbsent(node, this::getNew); + } + + protected abstract T getNew(@NotNull NetNode node); + + public void invalidateAll() { + cache.clear(); + cache.trim(16); + } + + @Override + public @NotNull Pair splitAcross(@NotNull Set sourceNodes, + @NotNull Set targetNodes) { + return ImmutablePair.of(buildFilteredCache(sourceNodes), buildFilteredCache(targetNodes)); + } + + protected abstract @NotNull NodeCacheGroupData buildFilteredCache(@NotNull Set filterNodes); +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/group/PathCacheGroupData.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/group/PathCacheGroupData.java new file mode 100644 index 0000000000..ccab6e86f2 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/group/PathCacheGroupData.java @@ -0,0 +1,172 @@ +package com.gregtechceu.gtceu.api.graphnet.group; + +import com.gregtechceu.gtceu.api.graphnet.net.NetEdge; +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; +import com.gregtechceu.gtceu.api.graphnet.path.NetPath; +import com.gregtechceu.gtceu.api.graphnet.path.PathBuilder; +import com.gregtechceu.gtceu.api.graphnet.path.SingletonNetPath; +import com.gregtechceu.gtceu.api.graphnet.path.StandardNetPath; +import com.gregtechceu.gtceu.api.graphnet.traverse.EdgeDirection; +import com.gregtechceu.gtceu.api.graphnet.traverse.NetIterator; +import com.gregtechceu.gtceu.api.graphnet.traverse.NetIteratorSupplier; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Set; + +public class PathCacheGroupData extends NodeCacheGroupData { + + protected final NetIteratorSupplier iteratorSupplier; + + public PathCacheGroupData(NetIteratorSupplier iteratorSupplier) { + this(iteratorSupplier, new Object2ObjectOpenHashMap<>()); + } + + public PathCacheGroupData(NetIteratorSupplier iteratorSupplier, + @NotNull Object2ObjectOpenHashMap cache) { + super(cache); + this.iteratorSupplier = iteratorSupplier; + } + + @Override + protected SecondaryCache getNew(@NotNull NetNode node) { + return new SecondaryCache(node); + } + + public void notifyTopologicalChange() { + cache.forEach((key, value) -> value.notifyTopologicalChange()); + } + + protected PathBuilder createBuilder(@NotNull NetNode origin) { + return new StandardNetPath.Builder(origin); + } + + protected NetPath buildSingleton(@NotNull NetNode singleton) { + return new SingletonNetPath(singleton); + } + + protected NetPath buildPath(@NotNull NetNode intersect, @NotNull NetIterator targetFrontier, + @NotNull NetIterator searchFrontier) { + PathBuilder builder = createBuilder(intersect); + // first, assemble the path leading to the target frontier origin + NetNode link = intersect; + while (true) { + NetEdge span = targetFrontier.getSpanningTreeEdge(link); + if (span == null) break; + link = span.getOppositeNode(link); + if (link == null) return null; + builder.addToEnd(link, span); + } + // second, assemble the path leading to the search frontier origin + link = intersect; + while (true) { + NetEdge span = searchFrontier.getSpanningTreeEdge(link); + if (span == null) break; + link = span.getOppositeNode(link); + if (link == null) return null; + builder.addToStart(link, span); + } + return builder.build(); + } + + @Override + public void notifyOfBridgingEdge(@NotNull NetEdge edge) { + notifyTopologicalChange(); + invalidateAll(); + } + + @Override + public void notifyOfRemovedEdge(@NotNull NetEdge edge) { + notifyTopologicalChange(); + this.cache.values().removeIf(c -> { + c.values().removeIf(p -> p.getOrderedEdges().contains(edge)); + return c.isEmpty(); + }); + } + + @Override + protected @Nullable GroupData mergeAcross(@Nullable GroupData other, @NotNull NetEdge edge) { + if (other instanceof PathCacheGroupData data) { + this.cache.putAll(data.cache); + } + notifyTopologicalChange(); + return this; + } + + @Override + public @NotNull Pair splitAcross(@NotNull Set sourceNodes, + @NotNull Set targetNodes) { + notifyTopologicalChange(); + return super.splitAcross(sourceNodes, targetNodes); + } + + protected @NotNull PathCacheGroupData buildFilteredCache(@NotNull Set filterNodes) { + Object2ObjectOpenHashMap child = new Object2ObjectOpenHashMap<>(this.cache); + child.entrySet().removeIf(entry -> { + if (!filterNodes.contains(entry.getKey())) return true; + SecondaryCache cache = entry.getValue(); + cache.keySet().retainAll(filterNodes); + return cache.isEmpty(); + }); + return new PathCacheGroupData(iteratorSupplier, child); + } + + public class SecondaryCache extends Object2ObjectOpenHashMap { + + protected final @NotNull NetNode source; + protected @Nullable NetIterator searchFrontier; + + protected NetPath singleton; + + protected int frontierPosition; + + public SecondaryCache(@NotNull NetNode source) { + this.source = source; + } + + @Nullable + public NetPath getOrCompute(@NotNull NetNode target) { + if (target == source) { + if (singleton == null) singleton = buildSingleton(source); + return singleton; + } + + if (searchFrontier == null) searchFrontier = iteratorSupplier.create(source, EdgeDirection.OUTGOING); + + NetPath existing = this.get(target); + if (existing != null) return existing; + NetIterator targetFrontier = iteratorSupplier.create(target, EdgeDirection.INCOMING); + int frontierPosition = 0; + // first, attempt to bring the target frontier up to date with the search frontier. + while (frontierPosition < this.frontierPosition && targetFrontier.hasNext()) { + NetNode node = targetFrontier.next(); + frontierPosition++; + if (searchFrontier.hasSeen(node)) { + NetPath built = buildPath(node, targetFrontier, searchFrontier); + this.put(target, built); + return built; + } + } + // second, move both frontiers forward until intersect or exhaustion of iterators. + while (searchFrontier.hasNext() && targetFrontier.hasNext()) { + searchFrontier.next(); + NetNode node = targetFrontier.next(); + this.frontierPosition++; + if (searchFrontier.hasSeen(node)) { + NetPath built = buildPath(node, targetFrontier, searchFrontier); + this.put(target, built); + return built; + } + } + return null; + } + + public void notifyTopologicalChange() { + this.searchFrontier = null; + this.frontierPosition = 0; + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/AbstractByteLogicData.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/AbstractByteLogicData.java new file mode 100644 index 0000000000..43ab68e12d --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/AbstractByteLogicData.java @@ -0,0 +1,69 @@ +package com.gregtechceu.gtceu.api.graphnet.logic; + +import net.minecraft.nbt.ByteTag; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; + +import org.jetbrains.annotations.NotNull; + +import java.util.function.Supplier; + +public abstract class AbstractByteLogicData> extends NetLogicEntry { + + private byte value; + + protected AbstractByteLogicData() {} + + protected AbstractByteLogicData(byte init) { + this.value = init; + } + + protected T setValue(byte value) { + this.value = value; + return (T) this; + } + + public byte getValue() { + return this.value; + } + + @Override + public ByteTag serializeNBT() { + return ByteTag.valueOf(this.value); + } + + @Override + public void deserializeNBT(ByteTag nbt) { + this.value = nbt.getAsByte(); + } + + @Override + public void encode(FriendlyByteBuf buf, boolean fullChange) { + buf.writeByte(this.value); + } + + @Override + public void decode(FriendlyByteBuf buf, boolean fullChange) { + this.value = buf.readByte(); + } + + @Override + public abstract @NotNull AbstractByteLogicData.ByteLogicType getType(); + + public static class ByteLogicType> extends NetLogicType { + + public ByteLogicType(@NotNull ResourceLocation name, @NotNull Supplier<@NotNull T> supplier, + @NotNull T defaultable) { + super(name, supplier, defaultable); + } + + public ByteLogicType(@NotNull String namespace, @NotNull String name, @NotNull Supplier<@NotNull T> supplier, + @NotNull T defaultable) { + super(namespace, name, supplier, defaultable); + } + + public T getWith(byte value) { + return getNew().setValue(value); + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/AbstractDoubleLogicData.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/AbstractDoubleLogicData.java new file mode 100644 index 0000000000..d85ec8cd8c --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/AbstractDoubleLogicData.java @@ -0,0 +1,74 @@ +package com.gregtechceu.gtceu.api.graphnet.logic; + +import net.minecraft.nbt.DoubleTag; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; + +import org.jetbrains.annotations.NotNull; + +import java.util.function.Supplier; + +public abstract class AbstractDoubleLogicData> + extends NetLogicEntry { + + private double value; + + protected AbstractDoubleLogicData() {} + + protected AbstractDoubleLogicData(double init) { + this.value = init; + } + + public T getWith(double value) { + return getType().getNew().setValue(value); + } + + protected T setValue(double value) { + this.value = value; + return (T) this; + } + + public double getValue() { + return this.value; + } + + @Override + public DoubleTag serializeNBT() { + return DoubleTag.valueOf(this.value); + } + + @Override + public void deserializeNBT(DoubleTag nbt) { + this.value = nbt.getAsDouble(); + } + + @Override + public void encode(FriendlyByteBuf buf, boolean fullChange) { + buf.writeDouble(value); + } + + @Override + public void decode(FriendlyByteBuf buf, boolean fullChange) { + this.value = buf.readDouble(); + } + + @Override + public abstract @NotNull DoubleLogicType getType(); + + public static class DoubleLogicType> extends NetLogicType { + + public DoubleLogicType(@NotNull ResourceLocation name, @NotNull Supplier<@NotNull T> supplier, + @NotNull T defaultable) { + super(name, supplier, defaultable); + } + + public DoubleLogicType(@NotNull String namespace, @NotNull String name, @NotNull Supplier<@NotNull T> supplier, + @NotNull T defaultable) { + super(namespace, name, supplier, defaultable); + } + + public T getWith(double value) { + return getNew().setValue(value); + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/AbstractFloatLogicData.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/AbstractFloatLogicData.java new file mode 100644 index 0000000000..96886b261b --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/AbstractFloatLogicData.java @@ -0,0 +1,75 @@ +package com.gregtechceu.gtceu.api.graphnet.logic; + +import net.minecraft.nbt.FloatTag; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Supplier; + +public abstract class AbstractFloatLogicData> extends NetLogicEntry { + + private float value; + + protected AbstractFloatLogicData() {} + + protected AbstractFloatLogicData(float init) { + this.value = init; + } + + public T getWith(float value) { + return getType().getNew().setValue(value); + } + + @Contract("_ -> this") + public T setValue(float value) { + this.value = value; + return (T) this; + } + + public float getValue() { + return this.value; + } + + @Override + public FloatTag serializeNBT() { + return FloatTag.valueOf(this.value); + } + + @Override + public void deserializeNBT(FloatTag nbt) { + this.value = nbt.getAsFloat(); + } + + @Override + public void encode(FriendlyByteBuf buf, boolean fullChange) { + buf.writeFloat(this.value); + } + + @Override + public void decode(FriendlyByteBuf buf, boolean fullChange) { + this.value = buf.readFloat(); + } + + @Override + public abstract @NotNull AbstractFloatLogicData.FloatLogicType getType(); + + public static class FloatLogicType> extends NetLogicType { + + public FloatLogicType(@NotNull ResourceLocation name, @NotNull Supplier<@NotNull T> supplier, + @NotNull T defaultable) { + super(name, supplier, defaultable); + } + + public FloatLogicType(@NotNull String namespace, @NotNull String name, @NotNull Supplier<@NotNull T> supplier, + @NotNull T defaultable) { + super(namespace, name, supplier, defaultable); + } + + public T getWith(float value) { + return getNew().setValue(value); + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/AbstractIntLogicData.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/AbstractIntLogicData.java new file mode 100644 index 0000000000..f1290eadfb --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/AbstractIntLogicData.java @@ -0,0 +1,73 @@ +package com.gregtechceu.gtceu.api.graphnet.logic; + +import net.minecraft.nbt.IntTag; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; + +import org.jetbrains.annotations.NotNull; + +import java.util.function.Supplier; + +public abstract class AbstractIntLogicData> extends NetLogicEntry { + + private int value; + + protected AbstractIntLogicData() {} + + protected AbstractIntLogicData(int init) { + this.value = init; + } + + public T getWith(int value) { + return getType().getNew().setValue(value); + } + + protected T setValue(int value) { + this.value = value; + return (T) this; + } + + public int getValue() { + return this.value; + } + + @Override + public IntTag serializeNBT() { + return IntTag.valueOf(this.value); + } + + @Override + public void deserializeNBT(IntTag nbt) { + this.value = nbt.getAsInt(); + } + + @Override + public void encode(FriendlyByteBuf buf, boolean fullChange) { + buf.writeVarInt(this.value); + } + + @Override + public void decode(FriendlyByteBuf buf, boolean fullChange) { + this.value = buf.readVarInt(); + } + + @Override + public abstract @NotNull IntLogicType getType(); + + public static class IntLogicType> extends NetLogicType { + + public IntLogicType(@NotNull ResourceLocation name, @NotNull Supplier<@NotNull T> supplier, + @NotNull T defaultable) { + super(name, supplier, defaultable); + } + + public IntLogicType(@NotNull String namespace, @NotNull String name, @NotNull Supplier<@NotNull T> supplier, + @NotNull T defaultable) { + super(namespace, name, supplier, defaultable); + } + + public T getWith(int value) { + return getNew().setValue(value); + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/AbstractLongLogicData.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/AbstractLongLogicData.java new file mode 100644 index 0000000000..8848f524e4 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/AbstractLongLogicData.java @@ -0,0 +1,75 @@ +package com.gregtechceu.gtceu.api.graphnet.logic; + +import net.minecraft.nbt.LongTag; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Supplier; + +public abstract class AbstractLongLogicData> extends NetLogicEntry { + + private long value; + + protected AbstractLongLogicData() {} + + protected AbstractLongLogicData(long init) { + this.value = init; + } + + public T getWith(long value) { + return getType().getNew().setValue(value); + } + + @Contract("_ -> this") + public T setValue(long value) { + this.value = value; + return (T) this; + } + + public long getValue() { + return this.value; + } + + @Override + public LongTag serializeNBT() { + return LongTag.valueOf(this.value); + } + + @Override + public void deserializeNBT(LongTag nbt) { + this.value = nbt.getAsLong(); + } + + @Override + public void encode(FriendlyByteBuf buf, boolean fullChange) { + buf.writeVarLong(this.value); + } + + @Override + public void decode(FriendlyByteBuf buf, boolean fullChange) { + this.value = buf.readVarLong(); + } + + @Override + public abstract @NotNull LongLogicType getType(); + + public static class LongLogicType> extends NetLogicType { + + public LongLogicType(@NotNull ResourceLocation name, @NotNull Supplier<@NotNull T> supplier, + @NotNull T defaultable) { + super(name, supplier, defaultable); + } + + public LongLogicType(@NotNull String namespace, @NotNull String name, @NotNull Supplier<@NotNull T> supplier, + @NotNull T defaultable) { + super(namespace, name, supplier, defaultable); + } + + public T getWith(long value) { + return getNew().setValue(value); + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/AbstractTransientLogicData.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/AbstractTransientLogicData.java new file mode 100644 index 0000000000..e41b63d019 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/AbstractTransientLogicData.java @@ -0,0 +1,29 @@ +package com.gregtechceu.gtceu.api.graphnet.logic; + +import net.minecraft.nbt.Tag; +import net.minecraft.network.FriendlyByteBuf; + +import org.jetbrains.annotations.Nullable; + +public abstract class AbstractTransientLogicData> + extends NetLogicEntry { + + @Override + public final void deserializeNBT(Tag nbt) {} + + @Override + public final @Nullable Tag serializeNBT() { + return null; + } + + @Override + public boolean shouldEncode() { + return false; + } + + @Override + public void encode(FriendlyByteBuf buf, boolean fullChange) {} + + @Override + public void decode(FriendlyByteBuf buf, boolean fullChange) {} +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/ChannelCountLogic.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/ChannelCountLogic.java new file mode 100644 index 0000000000..52751ae3fb --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/ChannelCountLogic.java @@ -0,0 +1,23 @@ +package com.gregtechceu.gtceu.api.graphnet.logic; + +import com.gregtechceu.gtceu.GTCEu; + +import org.jetbrains.annotations.NotNull; + +public final class ChannelCountLogic extends AbstractIntLogicData { + + public static final IntLogicType TYPE = new IntLogicType<>(GTCEu.MOD_ID, "ChannelCount", + ChannelCountLogic::new, new ChannelCountLogic().setValue(1)); + + @Override + public @NotNull IntLogicType getType() { + return TYPE; + } + + @Override + public ChannelCountLogic union(NetLogicEntry other) { + if (other instanceof ChannelCountLogic l) { + return this.getValue() < l.getValue() ? this : l; + } else return this; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/INetLogicEntryListener.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/INetLogicEntryListener.java new file mode 100644 index 0000000000..801ce2e418 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/INetLogicEntryListener.java @@ -0,0 +1,6 @@ +package com.gregtechceu.gtceu.api.graphnet.logic; + +public interface INetLogicEntryListener { + + void markLogicEntryAsUpdated(NetLogicEntry entry, boolean fullChange); +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/NetLogicData.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/NetLogicData.java new file mode 100644 index 0000000000..eb72a1e1bd --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/NetLogicData.java @@ -0,0 +1,253 @@ +package com.gregtechceu.gtceu.api.graphnet.logic; + +import com.gregtechceu.gtceu.utils.reference.WeakHashSet; + +import com.lowdragmc.lowdraglib.syncdata.IContentChangeAware; +import com.lowdragmc.lowdraglib.syncdata.ITagSerializable; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.util.StringRepresentable; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectCollection; +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Note - since the internal map representation encodes keys using {@link StringRepresentable#getSerializedName()} on + * logics, + * making a logics class return two different names is a valid way to register multiple instances. + */ +public final class NetLogicData implements ITagSerializable, IContentChangeAware, INetLogicEntryListener { + + @Nullable + @Getter + @Setter + private Runnable onContentsChanged = () -> {}; + + private final Object2ObjectOpenHashMap, NetLogicEntry> logicEntrySet; + + private final WeakHashSet listeners = new WeakHashSet<>(); + + public NetLogicData() { + logicEntrySet = new Object2ObjectOpenHashMap<>(4); + } + + private NetLogicData(Object2ObjectOpenHashMap, NetLogicEntry> logicEntrySet) { + this.logicEntrySet = logicEntrySet; + } + + /** + * If the {@link NetLogicEntry#union(NetLogicEntry)} operation is not supported for this entry, + * nothing happens if an entry is already present. + */ + public NetLogicData mergeLogicEntry(NetLogicEntry entry) { + NetLogicEntry current = logicEntrySet.get(entry.getType()); + if (current == null) return setLogicEntry(entry); + + if (entry.getClass().isInstance(current)) { + entry = current.union(entry); + if (entry == null) return this; + } + return setLogicEntry(entry); + } + + public NetLogicData setLogicEntry(NetLogicEntry entry) { + entry.registerToNetLogicData(this); + logicEntrySet.put(entry.getType(), entry); + this.markLogicEntryAsUpdated(entry, true); + return this; + } + + /** + * Returns all registered logic entries; this should be treated in read-only manner. + */ + public ObjectCollection> getEntries() { + return logicEntrySet.values(); + } + + public void clearData() { + logicEntrySet.clear(); + logicEntrySet.trim(4); + if (onContentsChanged != null) { + onContentsChanged.run(); + } + } + + public NetLogicData removeLogicEntry(@NotNull NetLogicEntry key) { + return removeLogicEntry(key.getType()); + } + + public NetLogicData removeLogicEntry(@NotNull NetLogicType key) { + NetLogicEntry entry = logicEntrySet.remove(key); + if (entry != null) { + entry.deregisterFromNetLogicData(this); + this.listeners.forEach(l -> l.markChanged(entry, true, true)); + logicEntrySet.trim(); + } + if (onContentsChanged != null) { + onContentsChanged.run(); + } + return this; + } + + @Override + public void markLogicEntryAsUpdated(NetLogicEntry entry, boolean fullChange) { + this.listeners.forEach(l -> l.markChanged(entry, false, fullChange)); + if (onContentsChanged != null) { + onContentsChanged.run(); + } + } + + public boolean hasLogicEntry(@NotNull NetLogicType type) { + return logicEntrySet.containsKey(type); + } + + public boolean hasLogicEntry(@NotNull NetLogicEntry key) { + return logicEntrySet.containsKey(key.getType()); + } + + public > T getLogicEntryNullable(@NotNull NetLogicType type) { + return type.cast(logicEntrySet.get(type)); + } + + @NotNull + public > T getLogicEntryDefaultable(@NotNull NetLogicType type) { + return type.cast(logicEntrySet.getOrDefault(type, type.getDefault())); + } + + @Contract("null, null -> null; !null, _ -> new; _, !null -> new") + public static @Nullable NetLogicData unionNullable(@Nullable NetLogicData sourceData, + @Nullable NetLogicData targetData) { + if (sourceData == null && targetData == null) return null; + return union(sourceData == null ? targetData : sourceData, sourceData == null ? null : targetData); + } + + @Contract("_, _ -> new") + public static @NotNull NetLogicData union(@NotNull NetLogicData sourceData, @Nullable NetLogicData targetData) { + Object2ObjectOpenHashMap, NetLogicEntry> newLogic = new Object2ObjectOpenHashMap<>( + sourceData.logicEntrySet); + if (targetData != null) { + for (NetLogicType key : newLogic.keySet()) { + newLogic.computeIfPresent(key, (k, v) -> v.union(targetData.logicEntrySet.get(k))); + } + targetData.logicEntrySet.forEach((key, value) -> newLogic.computeIfAbsent(key, k -> value.union(null))); + } + return new NetLogicData(newLogic); + } + + @Contract("_, _ -> new") + public static @NotNull NetLogicData union(@NotNull NetLogicData first, @NotNull NetLogicData... others) { + Object2ObjectOpenHashMap, NetLogicEntry> newLogic = new Object2ObjectOpenHashMap<>( + first.logicEntrySet); + for (NetLogicData other : others) { + for (NetLogicType key : newLogic.keySet()) { + newLogic.computeIfPresent(key, (k, v) -> v.union(other.logicEntrySet.get(k))); + } + other.logicEntrySet.forEach((key, value) -> newLogic.computeIfAbsent(key, k -> value.union(null))); + } + return new NetLogicData(newLogic); + } + + @Override + public ListTag serializeNBT() { + ListTag list = new ListTag(); + for (NetLogicEntry entry : getEntries()) { + CompoundTag tag = new CompoundTag(); + tag.putString("Type", entry.getType().getSerializedName()); + Tag nbt = entry.serializeNBT(); + if (nbt != null) tag.put("Tag", nbt); + list.add(tag); + } + return list; + } + + @Override + public void deserializeNBT(ListTag nbt) { + for (int i = 0; i < nbt.size(); i++) { + CompoundTag tag = nbt.getCompound(i); + NetLogicType type = NetLogicRegistry.getTypeNullable(tag.getString("Type")); + if (type == null) continue; + NetLogicEntry entry = this.logicEntrySet.get(type); + if (entry == null) entry = type.getNew(); + entry.deserializeNBTNaive(tag.get("Tag")); + this.logicEntrySet.put(type, entry); + } + } + + public void encode(FriendlyByteBuf buf) { + int count = 0; + for (NetLogicEntry entry : getEntries()) { + if (entry.shouldEncode()) count++; + } + buf.writeVarInt(count); + for (NetLogicEntry entry : getEntries()) { + if (entry.shouldEncode()) writeEntry(buf, entry, true); + } + } + + public void decode(FriendlyByteBuf buf) { + this.logicEntrySet.clear(); + int entryCount = buf.readVarInt(); + for (int i = 0; i < entryCount; i++) { + readEntry(buf); + } + this.logicEntrySet.trim(); + } + + public static void writeEntry(@NotNull FriendlyByteBuf buf, @NotNull NetLogicEntry entry, + boolean fullChange) { + buf.writeVarInt(NetLogicRegistry.getNetworkID(entry)); + buf.writeBoolean(fullChange); + entry.encode(buf, fullChange); + } + + /** + * @return the net logic entry decoded to. + */ + @Nullable + public NetLogicEntry readEntry(@NotNull FriendlyByteBuf buf) { + int id = buf.readVarInt(); + boolean fullChange = buf.readBoolean(); + NetLogicType type = NetLogicRegistry.getType(id); + NetLogicEntry existing = this.getLogicEntryNullable(type); + boolean add = false; + if (existing == null) { + // never partially decode into a new entry + if (!fullChange) return null; + existing = type.getNew(); + add = true; + } + try { + existing.decode(buf, fullChange); + } catch (Exception ignored) { + NetLogicRegistry.throwDecodingError(); + } + // make sure to add after decoding, so we don't notify listeners with an empty logic entry + if (add) this.setLogicEntry(existing); + return existing; + } + + /** + * Adds a listener to a weak set which is then notified for as long as it is not collected by the garbage collector. + * + * @param listener the listener. + * @return the listener, for convenience when working with lambdas. + */ + public ILogicDataListener addListener(ILogicDataListener listener) { + this.listeners.add(listener); + return listener; + } + + @FunctionalInterface + public interface ILogicDataListener { + + void markChanged(NetLogicEntry updatedEntry, boolean removed, boolean fullChange); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/NetLogicEntry.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/NetLogicEntry.java new file mode 100644 index 0000000000..0f450074f7 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/NetLogicEntry.java @@ -0,0 +1,116 @@ +package com.gregtechceu.gtceu.api.graphnet.logic; + +import com.gregtechceu.gtceu.api.graphnet.MultiNodeHelper; +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.logic.TemperatureLogic; + +import com.lowdragmc.lowdraglib.networking.IPacket; + +import net.minecraft.nbt.Tag; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraftforge.common.util.INBTSerializable; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Note - all extenders of this class are suggested to be final, in order to avoid unexpected + * {@link #union(NetLogicEntry)} behavior. + */ +public abstract class NetLogicEntry, N extends Tag> + implements INBTSerializable, IPacket { + + public abstract @NotNull NetLogicType getType(); + + public void deserializeNBTNaive(@Nullable Tag nbt) { + if (nbt != null) deserializeNBT((N) nbt); + } + + @Override + @Nullable + public abstract N serializeNBT(); + + /** + * Returns null if the operation is not supported. + */ + @Nullable + public T union(NetLogicEntry other) { + return null; + } + + /** + * Controls whether this logic entry should be merged to a MultiNodeHelper, if one is declared. + * The data entry must support {@link #merge(NetNode, NetLogicEntry)} with other entries of the same type, + * so that new nodes being added to the MultiNodeHelper can merge in. The multi node helper will ensure that + * all nodes registered to it contain the same object that their entries have been merged to, and when a node + * leaves the multi node helper {@link #unmerge(NetNode)} will be called for it. Server-Client sync is handled + * by the MultiNodeHelper, do not sync through NetLogicData. See {@link #registerToMultiNodeHelper(MultiNodeHelper)} + * + * @return whether logic entry should be merged to a MultiNodeHelper. + */ + public boolean mergedToMultiNodeHelper() { + return false; + } + + /** + * Called when this logic entry is added to a MultiNodeHelper. Any data syncing should go through the + * MultiNodeHelper after this method is called. + */ + public void registerToMultiNodeHelper(MultiNodeHelper helper) {} + + /** + * Should be used exclusively for {@link com.gregtechceu.gtceu.api.graphnet.MultiNodeHelper} logic. + * + * @param otherOwner the net node being merged in + * @param other the logic being merged in + */ + public void merge(NetNode otherOwner, NetLogicEntry other) {} + + /** + * Should be used exclusively for {@link com.gregtechceu.gtceu.api.graphnet.MultiNodeHelper} logic.
+ * Cannot be passed a logic entry since said logic entry would just be the instance this is being called for; + * if your logic needs to keep track then populate a map during {@link #merge(NetNode, NetLogicEntry)}. + * Keep in mind that this can be called for the data's original owner, despite + * {@link #merge(NetNode, NetLogicEntry)} not being called for the original owner. + * + * @param entryOwner the node being unmerged. + */ + public void unmerge(NetNode entryOwner) {} + + public void registerToNetLogicData(NetLogicData data) {} + + public void deregisterFromNetLogicData(NetLogicData data) {} + + public T cast(NetLogicEntry entry) { + return (T) entry; + } + + /** + * Controls whether this {@link NetLogicEntry} will be synced to the client or not. + */ + public boolean shouldEncode() { + return true; + } + + @Override + public final void encode(FriendlyByteBuf buf) { + encode(buf, true); + } + + /** + * @param fullChange allows for less-full buffers to be sent and received. + * Useful for logics that can be partially modified, see {@link TemperatureLogic} + */ + public abstract void encode(FriendlyByteBuf buf, boolean fullChange); + + @Override + public final void decode(FriendlyByteBuf buf) { + decode(buf, true); + } + + /** + * @param fullChange allows for less-full buffers to be sent and received. + * Useful for logics that can be partially modified, see {@link TemperatureLogic} + */ + public abstract void decode(FriendlyByteBuf buf, boolean fullChange); +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/NetLogicRegistrationEvent.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/NetLogicRegistrationEvent.java new file mode 100644 index 0000000000..624d5c2eff --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/NetLogicRegistrationEvent.java @@ -0,0 +1,23 @@ +package com.gregtechceu.gtceu.api.graphnet.logic; + +import net.minecraftforge.eventbus.api.Event; +import net.minecraftforge.fml.event.IModBusEvent; + +import it.unimi.dsi.fastutil.objects.ObjectRBTreeSet; + +import java.util.Comparator; + +public final class NetLogicRegistrationEvent extends Event implements IModBusEvent { + + private final ObjectRBTreeSet> gather = new ObjectRBTreeSet<>( + Comparator.comparing(NetLogicType::getSerializedName)); + + public void accept(NetLogicType type) { + if (!gather.add(type)) + throw new IllegalStateException("Detected a name collision during Net Logic registration!"); + } + + ObjectRBTreeSet> getGather() { + return gather; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/NetLogicRegistry.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/NetLogicRegistry.java new file mode 100644 index 0000000000..a770793e09 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/NetLogicRegistry.java @@ -0,0 +1,97 @@ +package com.gregtechceu.gtceu.api.graphnet.logic; + +import com.lowdragmc.lowdraglib.LDLib; + +import net.minecraft.client.Minecraft; +import net.minecraft.network.chat.Component; +import net.minecraftforge.fml.ModLoader; + +import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Set; + +public final class NetLogicRegistry { + + private static final Int2ObjectArrayMap> REGISTRY; + + private static final Object2IntOpenHashMap NAMES_TO_NETWORK_IDS; + + static { + NetLogicRegistrationEvent event = new NetLogicRegistrationEvent(); + ModLoader.get().postEvent(event); + Set> gather = event.getGather(); + NAMES_TO_NETWORK_IDS = new Object2IntOpenHashMap<>(gather.size()); + REGISTRY = new Int2ObjectArrayMap<>(gather.size()); + int id = 1; + for (NetLogicType type : gather) { + NAMES_TO_NETWORK_IDS.put(type.getSerializedName(), id); + REGISTRY.put(id, type); + id++; + } + } + + public static String getName(int networkID) { + return REGISTRY.get(networkID).getSerializedName(); + } + + public static int getNetworkID(@NotNull String name) { + int id = NAMES_TO_NETWORK_IDS.getInt(name); + if (id == -1) throwUnregisteredError(name); + return id; + } + + public static int getNetworkID(@NotNull NetLogicType type) { + return getNetworkID(type.getSerializedName()); + } + + public static int getNetworkID(@NotNull NetLogicEntry entry) { + return getNetworkID(entry.getType()); + } + + public static @Nullable NetLogicType getTypeNullable(int networkID) { + return REGISTRY.get(networkID); + } + + public static @Nullable NetLogicType getTypeNullable(@NotNull String name) { + return getTypeNullable(getNetworkID(name)); + } + + public static @NotNull NetLogicType getType(int networkID) { + NetLogicType type = REGISTRY.get(networkID); + if (type == null) throwNonexistenceError(); + assert type != null; + return type; + } + + public static @NotNull NetLogicType getType(@NotNull String name) { + return getType(getNetworkID(name)); + } + + public static void throwNonexistenceError() { + if (LDLib.isRemote()) disconnect(); + throw new RuntimeException("Could not find the type of an encoded NetLogicEntry. " + + "This suggests that the server and client have different GT versions or modifications."); + } + + public static void throwDecodingError() { + if (LDLib.isRemote()) disconnect(); + throw new RuntimeException("Failed to decode an encoded NetLogicEntry. " + + "This suggests that the server and client have different GT versions or modifications."); + } + + public static void throwUnregisteredError(String n) { + throw new RuntimeException("Could not determine the network ID of a to-encode NetLogicEntry. " + + "This suggests that the NetLogicEntry does not have a registered type. The offending name is: " + n); + } + + private static void disconnect() { + if (Minecraft.getInstance().getConnection() != null) + Minecraft.getInstance().getConnection() + .onDisconnect(Component.translatable("gtceu.universal.net_logic_disconnect")); + } + + private NetLogicRegistry() {} +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/NetLogicType.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/NetLogicType.java new file mode 100644 index 0000000000..a1522287c5 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/NetLogicType.java @@ -0,0 +1,47 @@ +package com.gregtechceu.gtceu.api.graphnet.logic; + +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.StringRepresentable; + +import org.jetbrains.annotations.NotNull; + +import java.util.function.Supplier; + +public class NetLogicType> implements StringRepresentable { + + private final @NotNull String name; + private final @NotNull Supplier<@NotNull T> supplier; + private final @NotNull T defaultable; + + public NetLogicType(@NotNull ResourceLocation name, @NotNull Supplier<@NotNull T> supplier, + @NotNull T defaultable) { + this.name = name.toString(); + this.supplier = supplier; + this.defaultable = defaultable; + } + + public NetLogicType(@NotNull String namespace, @NotNull String name, @NotNull Supplier<@NotNull T> supplier, + @NotNull T defaultable) { + this.name = namespace + ":" + name; + this.supplier = supplier; + this.defaultable = defaultable; + } + + @SuppressWarnings("unchecked") + public T cast(NetLogicEntry entry) { + return (T) entry; + } + + public final @NotNull T getNew() { + return supplier.get(); + } + + public final @NotNull T getDefault() { + return defaultable; + } + + @Override + public final @NotNull String getSerializedName() { + return name; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/ThroughputLogic.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/ThroughputLogic.java new file mode 100644 index 0000000000..4a7d7db91c --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/ThroughputLogic.java @@ -0,0 +1,23 @@ +package com.gregtechceu.gtceu.api.graphnet.logic; + +import com.gregtechceu.gtceu.GTCEu; + +import org.jetbrains.annotations.NotNull; + +public final class ThroughputLogic extends AbstractLongLogicData { + + public static final LongLogicType TYPE = new LongLogicType<>(GTCEu.MOD_ID, "Throughput", + ThroughputLogic::new, new ThroughputLogic()); + + @Override + public @NotNull LongLogicType getType() { + return TYPE; + } + + @Override + public ThroughputLogic union(NetLogicEntry other) { + if (other instanceof ThroughputLogic l) { + return this.getValue() < l.getValue() ? this : l; + } else return this; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/WeightFactorLogic.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/WeightFactorLogic.java new file mode 100644 index 0000000000..9142924257 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/logic/WeightFactorLogic.java @@ -0,0 +1,23 @@ +package com.gregtechceu.gtceu.api.graphnet.logic; + +import com.gregtechceu.gtceu.GTCEu; + +import org.jetbrains.annotations.NotNull; + +public final class WeightFactorLogic extends AbstractDoubleLogicData { + + public static final DoubleLogicType TYPE = new DoubleLogicType<>(GTCEu.MOD_ID, "WeightFactor", + WeightFactorLogic::new, new WeightFactorLogic().setValue(0)); + + @Override + public @NotNull DoubleLogicType getType() { + return TYPE; + } + + @Override + public WeightFactorLogic union(NetLogicEntry other) { + if (other instanceof WeightFactorLogic l) { + return TYPE.getWith(this.getValue() + l.getValue()); + } else return this; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/net/BlankNetNode.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/net/BlankNetNode.java new file mode 100644 index 0000000000..76676096f9 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/net/BlankNetNode.java @@ -0,0 +1,26 @@ +package com.gregtechceu.gtceu.api.graphnet.net; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.graphnet.GraphClassType; + +import org.jetbrains.annotations.NotNull; + +public final class BlankNetNode extends NetNode { + + public static final GraphClassType TYPE = new GraphClassType<>(GTCEu.MOD_ID, "BlankNode", + BlankNetNode::new); + + public BlankNetNode(@NotNull IGraphNet net) { + super(net); + } + + @Override + public @NotNull Object getEquivalencyData() { + return this; + } + + @Override + public @NotNull GraphClassType getType() { + return TYPE; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/net/BlockPosNode.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/net/BlockPosNode.java new file mode 100644 index 0000000000..cda9b846c0 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/net/BlockPosNode.java @@ -0,0 +1,50 @@ +package com.gregtechceu.gtceu.api.graphnet.net; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.graphnet.GraphClassType; + +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; + +import org.jetbrains.annotations.NotNull; + +public class BlockPosNode extends NetNode { + + public static final GraphClassType TYPE = new GraphClassType<>(GTCEu.MOD_ID, "BlockPosNode", + BlockPosNode::new); + + private @NotNull BlockPos pos; + + public BlockPosNode(IGraphNet net) { + super(net); + pos = BlockPos.ZERO; + } + + public BlockPosNode setPos(BlockPos pos) { + this.pos = pos; + return this; + } + + @Override + public CompoundTag serializeNBT() { + CompoundTag tag = super.serializeNBT(); + tag.putLong("Pos", pos.asLong()); + return tag; + } + + @Override + public void deserializeNBT(CompoundTag nbt) { + super.deserializeNBT(nbt); + this.setPos(BlockPos.of(nbt.getLong("Pos"))); + } + + @Override + public @NotNull BlockPos getEquivalencyData() { + return pos; + } + + @Override + public @NotNull GraphClassType getType() { + return TYPE; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/net/IGraphNet.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/net/IGraphNet.java new file mode 100644 index 0000000000..4c401165c0 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/net/IGraphNet.java @@ -0,0 +1,148 @@ +package com.gregtechceu.gtceu.api.graphnet.net; + +import com.gregtechceu.gtceu.api.graphnet.GraphClassType; +import com.gregtechceu.gtceu.api.graphnet.GraphNetBacker; +import com.gregtechceu.gtceu.api.graphnet.MultiNodeHelper; +import com.gregtechceu.gtceu.api.graphnet.graph.INetGraph; +import com.gregtechceu.gtceu.api.graphnet.group.GroupData; +import com.gregtechceu.gtceu.api.graphnet.logic.NetLogicData; +import com.gregtechceu.gtceu.api.graphnet.logic.WeightFactorLogic; + +import org.jetbrains.annotations.*; + +public interface IGraphNet { + + /** + * Adds a node to the graphnet. + * + * @param node The node to add. + */ + void addNode(@NotNull NetNode node); + + /** + * Gets the net node with the given equivalency data, if one exists. + * + * @param equivalencyData the equivalency data to match. + * @return the matching net node, if one exists. + */ + @Nullable + NetNode getNode(@NotNull Object equivalencyData); + + /** + * Removes a node from the graphnet. + * + * @param node The node to remove. + */ + void removeNode(@NotNull NetNode node); + + /** + * Links two nodes by an edge. + * + * @param source Source node. + * @param target Target node. + * @param bothWays If the graph is directional, passing in true will create both the forwards and backwards edge. + * @return the created edge, if it was created. Returns null if bothWays is set to true. + */ + @Nullable + @Contract("_, _, false -> _; _, _, true -> null") + NetEdge addEdge(@NotNull NetNode source, @NotNull NetNode target, boolean bothWays); + + /** + * Returns the edge linking two nodes together, if one exists. + * + * @param source Source node. + * @param target Target node. + * @return the linking edge, if one exists. + */ + @Nullable + NetEdge getEdge(@NotNull NetNode source, @NotNull NetNode target); + + /** + * Removes the edge linking two nodes together, if one exists. + * + * @param source Source node. + * @param target Target node. + * @param bothWays If the graph is directional, passing in true will remove both the forwards and backwards edge. + */ + void removeEdge(@NotNull NetNode source, @NotNull NetNode target, boolean bothWays); + + /** + * Gets the {@link INetGraph} backing this graphnet. This should NEVER be modified directly, but can be queried. + * + * @return the backing net graph + */ + default @UnmodifiableView INetGraph getGraph() { + return getBacker().getGraph(); + } + + /** + * Gets the {@link GraphNetBacker} backing this graphnet. This should NEVER be used except inside the graphnet impl. + * + * @return the backing graphnet backer + */ + @ApiStatus.Internal + GraphNetBacker getBacker(); + + /** + * Get a blank group data for this graph.
+ * Make sure to override this if your NetGroups use data. + * + * @return The correct data variant. + */ + @Nullable + default GroupData getBlankGroupData() { + return null; + } + + /** + * Get a default node data for this graph. Generally used for immediate nbt deserialization. + * + * @return A default node data object. + */ + @NotNull + @Contract("->new") + default NetLogicData getDefaultNodeData() { + return new NetLogicData().setLogicEntry(WeightFactorLogic.TYPE.getWith(1)); + } + + /** + * Returns whether a node exists in this graph. + * + * @param node the node in question. + * @return whether the node exists. + */ + default boolean containsNode(NetNode node) { + return getGraph().containsVertex(node.wrapper); + } + + /** + * Used in {@link MultiNodeHelper} to determine if a node can be traversed, based on the nets that have been + * recently traversed in the {@link MultiNodeHelper}. + * + * @param net a recently traversed net + * @return if node traversal should be blocked. + */ + default boolean clashesWith(IGraphNet net) { + return false; + } + + /** + * @return a new node with no data, to be either nbt deserialized or initialized in some other way. + */ + @NotNull + GraphClassType getDefaultNodeType(); + + /** + * @return a new edge with no data, to be either nbt deserialized or initialized in some other way. + */ + @NotNull + default GraphClassType getDefaultEdgeType() { + return NetEdge.TYPE; + } + + /** + * Should only be used by the internal {@link GraphNetBacker} backing this graphnet. + */ + @ApiStatus.Internal + void setDirty(); +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/net/NetEdge.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/net/NetEdge.java new file mode 100644 index 0000000000..d0e8d0cb06 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/net/NetEdge.java @@ -0,0 +1,111 @@ +package com.gregtechceu.gtceu.api.graphnet.net; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.graphnet.GraphClassType; +import com.gregtechceu.gtceu.api.graphnet.graph.GraphEdge; +import com.gregtechceu.gtceu.api.graphnet.logic.NetLogicData; +import com.gregtechceu.gtceu.api.graphnet.predicate.EdgePredicateHandler; +import com.gregtechceu.gtceu.api.graphnet.predicate.test.IPredicateTestObject; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.Tag; +import net.minecraftforge.common.util.INBTSerializable; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class NetEdge implements INBTSerializable { + + public static final GraphClassType TYPE = new GraphClassType<>(GTCEu.MOD_ID, "NetEdge", + n -> new NetEdge()); + + /** + * For interacting with the internal graph representation ONLY, do not use or set this field otherwise. + */ + @ApiStatus.Internal + public @Nullable GraphEdge wrapper; + + private @Nullable EdgePredicateHandler predicateHandler; + + private @Nullable NetLogicData data; + + @Nullable + @Contract("null->null") + public static NetEdge unwrap(GraphEdge e) { + return e == null ? null : e.wrapped; + } + + public @Nullable NetNode getSource() { + if (wrapper == null) return null; + return wrapper.getSource().wrapped; + } + + public @Nullable NetNode getTarget() { + if (wrapper == null) return null; + return wrapper.getTarget().wrapped; + } + + public @Nullable NetNode getOppositeNode(@NotNull NetNode node) { + if (getSource() == node) return getTarget(); + else if (getTarget() == node) return getSource(); + else return null; + } + + public double getWeight() { + return wrapper == null ? Double.POSITIVE_INFINITY : wrapper.getWeight(); + } + + /** + * Should only be used on fake edges that are not registered to the graph. + */ + public void setData(@NotNull NetLogicData data) { + if (this.wrapper == null) this.data = data; + } + + /** + * This data is transient and should not be written to. + */ + public @NotNull NetLogicData getData() { + if (this.data == null) { + this.data = NetLogicData.unionNullable(getSource() == null ? null : getSource().getData(), + getTarget() == null ? null : getTarget().getData()); + // if we can't calculate it, create a new one just to guarantee nonnullness + if (this.data == null) this.data = new NetLogicData(); + } + return this.data; + } + + @NotNull + public EdgePredicateHandler getPredicateHandler() { + if (predicateHandler == null) predicateHandler = new EdgePredicateHandler(); + return predicateHandler; + } + + public boolean test(IPredicateTestObject object) { + if (predicateHandler == null) return true; + else return predicateHandler.test(object); + } + + @Override + public CompoundTag serializeNBT() { + CompoundTag tag = new CompoundTag(); + // we don't need to write our NetLogicData to NBT because we can regenerate it from our nodes + if (predicateHandler != null && !predicateHandler.shouldIgnore()) + tag.put("Predicate", predicateHandler.serializeNBT()); + return tag; + } + + @Override + public void deserializeNBT(CompoundTag nbt) { + if (nbt.contains("Predicate")) { + this.predicateHandler = new EdgePredicateHandler(); + this.predicateHandler.deserializeNBT(nbt.getList("Predicate", Tag.TAG_COMPOUND)); + } + } + + public GraphClassType getType() { + return TYPE; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/net/NetNode.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/net/NetNode.java new file mode 100644 index 0000000000..1726f98d7d --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/net/NetNode.java @@ -0,0 +1,131 @@ +package com.gregtechceu.gtceu.api.graphnet.net; + +import com.gregtechceu.gtceu.api.graphnet.GraphClassType; +import com.gregtechceu.gtceu.api.graphnet.graph.GraphVertex; +import com.gregtechceu.gtceu.api.graphnet.group.NetGroup; +import com.gregtechceu.gtceu.api.graphnet.logic.NetLogicData; + +import com.lowdragmc.lowdraglib.syncdata.IContentChangeAware; +import com.lowdragmc.lowdraglib.syncdata.ITagSerializable; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; + +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; + +public abstract class NetNode implements ITagSerializable, IContentChangeAware { + + @Getter + @Setter + public Runnable onContentsChanged = () -> {}; + + /** + * For interacting with the internal graph representation ONLY, do not use or set this field otherwise. + */ + @ApiStatus.Internal + public GraphVertex wrapper; + + /** + * Sorts nodes into distinct groups in NetGroups for later use. + */ + @Getter + protected int sortingKey = 0; + + @Getter + private final @NotNull IGraphNet net; + @Getter + private final @NotNull NetLogicData data; + @Setter + private @Nullable NetGroup group = null; + + public NetNode(@NotNull IGraphNet net) { + this.net = net; + this.data = net.getDefaultNodeData(); + } + + /** + * Sets the distinct group in a NetGroup this node will be sorted into. + */ + public void setSortingKey(int key) { + if (key != sortingKey) { + NetGroup group = getGroupUnsafe(); + if (group != null) group.notifySortingChange(this, sortingKey, key); + sortingKey = key; + } + } + + public boolean traverse(long queryTick, boolean simulate) { + return true; + } + + @Nullable + @Contract("null->null") + public static NetNode unwrap(GraphVertex n) { + return n == null ? null : n.wrapped; + } + + @NotNull + public NetGroup getGroupSafe() { + if (this.group == null) { + new NetGroup(this.getNet()).addNode(this); + // addNodes automatically sets our group to the new group + } + return this.group; + } + + @Nullable + public NetGroup getGroupUnsafe() { + return this.group; + } + + /** + * Use this to remove references that would keep this node from being collected by the garbage collector. + * This is called when a node is removed from the graph and should be discarded. + */ + public void onRemove() {} + + @Override + public CompoundTag serializeNBT() { + CompoundTag tag = new CompoundTag(); + tag.put("Data", this.data.serializeNBT()); + tag.putInt("SortingKey", sortingKey); + return tag; + } + + @Override + public void deserializeNBT(CompoundTag nbt) { + this.sortingKey = nbt.getInt("SortingKey"); + this.data.clearData(); + this.data.deserializeNBT((ListTag) nbt.get("Data")); + } + + /** + * Used to determine if two nodes are equal, for graph purposes. + * Should not change over the lifetime of a node, except when {@link #deserializeNBT(CompoundTag)} is called. + * + * @return equivalency data. Needs to work with {@link Objects#equals(Object, Object)} + */ + public abstract Object getEquivalencyData(); + + public abstract @NotNull GraphClassType getType(); + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + NetNode node = (NetNode) o; + return Objects.equals(getEquivalencyData(), node.getEquivalencyData()); + } + + @Override + public int hashCode() { + return Objects.hash(getEquivalencyData()); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/net/WorldSavedNet.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/net/WorldSavedNet.java new file mode 100644 index 0000000000..a0f6c8c588 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/net/WorldSavedNet.java @@ -0,0 +1,100 @@ +package com.gregtechceu.gtceu.api.graphnet.net; + +import com.gregtechceu.gtceu.api.graphnet.GraphNetBacker; +import com.gregtechceu.gtceu.api.graphnet.graph.INetGraph; +import com.gregtechceu.gtceu.api.graphnet.graph.NetDirectedGraph; +import com.gregtechceu.gtceu.api.graphnet.graph.NetUndirectedGraph; +import com.gregtechceu.gtceu.api.graphnet.logic.WeightFactorLogic; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.level.saveddata.SavedData; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.function.Function; + +/** + * Unused demonstration net that would allow for edges bridging dimensions inside the graph representation. + */ +@SuppressWarnings("unused") +public abstract class WorldSavedNet extends SavedData implements IGraphNet { + + protected final GraphNetBacker backer; + + //@formatter:off + /* USERS: ADD THESE METHODS TO YOUR CLASS + public static WorldSavedNet get(ServerLevel level, Function graphBuilder) { + return level.getDataStorage().computeIfAbsent(tag -> new WorldSavedNet(tag, graphBuilder), () -> + new WorldSavedNet(graphBuilder), name); + } + + public static WorldSavedNet get(ServerLevel level, boolean directed) { + return get(level, directed ? NetDirectedGraph.standardBuilder() : NetUndirectedGraph.standardBuilder()); + } + */ + //@formatter:on + + public WorldSavedNet(Function graphBuilder) { + this.backer = new GraphNetBacker(this, graphBuilder.apply(this)); + } + + public WorldSavedNet(boolean directed) { + this(directed ? NetDirectedGraph.standardBuilder() : NetUndirectedGraph.standardBuilder()); + } + + @Override + public void addNode(@NotNull NetNode node) { + this.backer.addNode(node); + } + + @Override + public @Nullable NetNode getNode(@NotNull Object equivalencyData) { + return backer.getNode(equivalencyData); + } + + @Override + public void removeNode(@NotNull NetNode node) { + this.backer.removeNode(node); + } + + @Override + public NetEdge addEdge(@NotNull NetNode source, @NotNull NetNode target, boolean bothWays) { + double weight = source.getData().getLogicEntryDefaultable(WeightFactorLogic.TYPE).getValue() + + target.getData().getLogicEntryDefaultable(WeightFactorLogic.TYPE).getValue(); + NetEdge edge = backer.addEdge(source, target, weight); + if (bothWays) { + if (this.getGraph().isDirected()) { + backer.addEdge(target, source, weight); + } + return null; + } else return edge; + } + + @Override + public @Nullable NetEdge getEdge(@NotNull NetNode source, @NotNull NetNode target) { + return backer.getEdge(source, target); + } + + @Override + public void removeEdge(@NotNull NetNode source, @NotNull NetNode target, boolean bothWays) { + this.backer.removeEdge(source, target); + if (bothWays && this.getGraph().isDirected()) { + this.backer.removeEdge(target, source); + } + } + + public void load(@NotNull CompoundTag nbt) { + backer.readFromNBT(nbt); + } + + @Override + public @NotNull CompoundTag save(@NotNull CompoundTag compound) { + return backer.writeToNBT(compound); + } + + @Override + public GraphNetBacker getBacker() { + return backer; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/path/NetPath.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/path/NetPath.java new file mode 100644 index 0000000000..b03badf00f --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/path/NetPath.java @@ -0,0 +1,43 @@ +package com.gregtechceu.gtceu.api.graphnet.path; + +import com.gregtechceu.gtceu.api.graphnet.logic.NetLogicData; +import com.gregtechceu.gtceu.api.graphnet.net.NetEdge; +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; + +import com.google.common.collect.ImmutableCollection; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; + +public interface NetPath { + + @NotNull + @Unmodifiable + ImmutableCollection getOrderedNodes(); + + @NotNull + default N getSourceNode() { + ImmutableCollection nodes = getOrderedNodes(); + return (N) nodes.asList().get(0); + } + + @NotNull + default N getTargetNode() { + ImmutableCollection nodes = getOrderedNodes(); + return (N) nodes.asList().get(nodes.size() - 1); + } + + @NotNull + @Unmodifiable + ImmutableCollection getOrderedEdges(); + + double getWeight(); + + @NotNull + NetPath reversed(); + + NetLogicData getUnifiedNodeData(); + + @Nullable + NetLogicData getUnifiedEdgeData(); +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/path/PathBuilder.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/path/PathBuilder.java new file mode 100644 index 0000000000..82550a3286 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/path/PathBuilder.java @@ -0,0 +1,21 @@ +package com.gregtechceu.gtceu.api.graphnet.path; + +import com.gregtechceu.gtceu.api.graphnet.net.NetEdge; +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +public interface PathBuilder { + + @Contract("_, _ -> this") + PathBuilder addToEnd(@NotNull NetNode node, @NotNull NetEdge edge); + + @Contract("_, _ -> this") + PathBuilder addToStart(@NotNull NetNode node, @NotNull NetEdge edge); + + @Contract("-> this") + PathBuilder reverse(); + + NetPath build(); +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/path/SingletonNetPath.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/path/SingletonNetPath.java new file mode 100644 index 0000000000..e2690a957f --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/path/SingletonNetPath.java @@ -0,0 +1,70 @@ +package com.gregtechceu.gtceu.api.graphnet.path; + +import com.gregtechceu.gtceu.api.graphnet.logic.NetLogicData; +import com.gregtechceu.gtceu.api.graphnet.logic.WeightFactorLogic; +import com.gregtechceu.gtceu.api.graphnet.net.NetEdge; +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; + +import com.google.common.collect.ImmutableCollection; +import com.google.common.collect.ImmutableSet; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; + +public class SingletonNetPath implements NetPath { + + protected final NetNode node; + protected final ImmutableSet singleton; + + protected final double weight; + + public SingletonNetPath(NetNode node) { + this(node, node.getData().getLogicEntryDefaultable(WeightFactorLogic.TYPE).getValue()); + } + + public SingletonNetPath(NetNode node, double weight) { + this.node = node; + this.singleton = ImmutableSet.of(node); + this.weight = weight; + } + + @Override + public @NotNull @Unmodifiable ImmutableCollection getOrderedNodes() { + return (ImmutableCollection) singleton; + } + + @Override + public @NotNull N getSourceNode() { + return (N) node; + } + + @Override + public @NotNull N getTargetNode() { + return (N) node; + } + + @Override + public @NotNull @Unmodifiable ImmutableCollection getOrderedEdges() { + return ImmutableSet.of(); + } + + @Override + public double getWeight() { + return weight; + } + + @Override + public @NotNull NetPath reversed() { + return this; + } + + @Override + public NetLogicData getUnifiedNodeData() { + return node.getData(); + } + + @Override + public @Nullable NetLogicData getUnifiedEdgeData() { + return null; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/path/StandardNetPath.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/path/StandardNetPath.java new file mode 100644 index 0000000000..2b9d5758a0 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/path/StandardNetPath.java @@ -0,0 +1,183 @@ +package com.gregtechceu.gtceu.api.graphnet.path; + +import com.gregtechceu.gtceu.api.graphnet.logic.NetLogicData; +import com.gregtechceu.gtceu.api.graphnet.net.NetEdge; +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; + +import com.google.common.collect.ImmutableCollection; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +public class StandardNetPath implements NetPath { + + protected final ImmutableCollection nodes; + + protected final ImmutableCollection edges; + + protected final double weight; + + protected final int hash; + + protected StandardNetPath reversed; + + protected NetLogicData unifiedNodeData; + protected NetLogicData unifiedEdgeData; + + public StandardNetPath(@NotNull ImmutableCollection nodes, @NotNull ImmutableCollection edges, + double weight) { + this.nodes = nodes; + this.edges = edges; + this.weight = weight; + ImmutableList listForm = nodes.asList(); + this.hash = Objects.hash(nodes, edges, weight, listForm.get(0), listForm.get(listForm.size() - 1)); + } + + public StandardNetPath(@NotNull StandardNetPath reverse) { + ImmutableCollection.Builder builderNodes = reverse.nodes instanceof ImmutableSet ? + ImmutableSet.builder() : ImmutableList.builder(); + List nodesList = reverse.nodes.asList(); + for (int i = nodesList.size(); i > 0; i--) { + builderNodes.add(nodesList.get(i - 1)); + } + ImmutableCollection.Builder builderEdges = reverse.edges instanceof ImmutableSet ? + ImmutableSet.builder() : ImmutableList.builder(); + List edgesList = reverse.edges.asList(); + for (int i = edgesList.size(); i > 0; i--) { + builderEdges.add(edgesList.get(i - 1)); + } + this.nodes = builderNodes.build(); + this.edges = builderEdges.build(); + this.weight = reverse.weight; + ImmutableList listForm = nodes.asList(); + this.hash = Objects.hash(nodes, edges, weight, listForm.get(0), listForm.get(listForm.size() - 1)); + this.reversed = reverse; + } + + @Override + public @NotNull @Unmodifiable ImmutableCollection getOrderedNodes() { + return (ImmutableCollection) nodes; + } + + @Override + public @NotNull @Unmodifiable ImmutableCollection getOrderedEdges() { + return (ImmutableCollection) edges; + } + + @Override + public double getWeight() { + return weight; + } + + @Override + public NetLogicData getUnifiedNodeData() { + if (unifiedNodeData == null) { + ImmutableList nodesList = nodes.asList(); + if (nodes.size() == 1) { + unifiedNodeData = nodesList.get(0).getData(); + } else if (nodes.size() == 2) { + unifiedNodeData = NetLogicData.union(nodesList.get(0).getData(), nodesList.get(1).getData()); + } else { + unifiedNodeData = NetLogicData.union(nodesList.get(0).getData(), + nodes.stream().skip(1).map(NetNode::getData).toArray(NetLogicData[]::new)); + } + } + return unifiedNodeData; + } + + @Override + @Nullable + public NetLogicData getUnifiedEdgeData() { + if (unifiedEdgeData == null) { + ImmutableList edgesList = edges.asList(); + if (edges.size() == 0) { + return null; + } else if (edges.size() == 1) { + unifiedEdgeData = edgesList.get(0).getData(); + } else if (edges.size() == 2) { + unifiedEdgeData = NetLogicData.union(edgesList.get(0).getData(), edgesList.get(1).getData()); + } else { + unifiedEdgeData = NetLogicData.union(edgesList.get(0).getData(), + edges.stream().skip(1).map(NetEdge::getData).toArray(NetLogicData[]::new)); + } + } + return unifiedEdgeData; + } + + @Override + public @NotNull StandardNetPath reversed() { + if (reversed == null) { + reversed = new StandardNetPath(this); + } + return reversed; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + StandardNetPath that = (StandardNetPath) o; + return Double.compare(that.weight, weight) == 0 && that.getSourceNode() == getSourceNode() && + that.getTargetNode() == getTargetNode() && Objects.equals(nodes, that.nodes) && + Objects.equals(edges, that.edges); + } + + @Override + public int hashCode() { + return hash; + } + + public static final class Builder implements PathBuilder { + + public final List nodes = new ObjectArrayList<>(); + public final List edges = new ObjectArrayList<>(); + + public Builder(@NotNull NetNode startingNode) { + nodes.add(startingNode); + } + + @Override + @Contract("_, _ -> this") + public Builder addToEnd(@NotNull NetNode node, @NotNull NetEdge edge) { + NetNode end = nodes.get(nodes.size() - 1); + if (edge.getOppositeNode(node) != end) + throw new IllegalArgumentException("Edge does not link last node and new node!"); + nodes.add(node); + edges.add(edge); + return this; + } + + @Override + @Contract("_, _ -> this") + public Builder addToStart(@NotNull NetNode node, @NotNull NetEdge edge) { + NetNode end = nodes.get(0); + if (edge.getOppositeNode(node) != end) + throw new IllegalArgumentException("Edge does not link last node and new node!"); + nodes.add(0, node); + edges.add(0, edge); + return this; + } + + @Override + @Contract("-> this") + public Builder reverse() { + Collections.reverse(nodes); + Collections.reverse(edges); + return this; + } + + @Override + public StandardNetPath build() { + return new StandardNetPath(ImmutableSet.copyOf(nodes), ImmutableSet.copyOf(edges), + edges.stream().mapToDouble(NetEdge::getWeight).sum()); + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/IPipeNetNodeHandler.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/IPipeNetNodeHandler.java new file mode 100644 index 0000000000..ea64c17e55 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/IPipeNetNodeHandler.java @@ -0,0 +1,53 @@ +package com.gregtechceu.gtceu.api.graphnet.pipenet; + +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IPipeStructure; + +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.BlockGetter; + +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +public interface IPipeNetNodeHandler { + + @NotNull + Collection getOrCreateFromNets(ServerLevel level, BlockPos pos, IPipeStructure structure); + + @NotNull + Collection getFromNets(ServerLevel level, BlockPos pos, IPipeStructure structure); + + void removeFromNets(ServerLevel level, BlockPos pos, IPipeStructure structure); + + void addInformation(@NotNull ItemStack stack, BlockGetter worldIn, @NotNull List tooltip, + @NotNull TooltipFlag flagIn, + IPipeStructure structure); + + IPipeNetNodeHandler EMPTY = new IPipeNetNodeHandler() { + + @Override + public @NotNull Collection getOrCreateFromNets(ServerLevel level, BlockPos pos, + IPipeStructure structure) { + return Collections.emptyList(); + } + + @Override + public @NotNull Collection getFromNets(ServerLevel level, BlockPos pos, + IPipeStructure structure) { + return Collections.emptyList(); + } + + @Override + public void removeFromNets(ServerLevel level, BlockPos pos, IPipeStructure structure) {} + + @Override + public void addInformation(@NotNull ItemStack stack, BlockGetter worldIn, @NotNull List tooltip, + @NotNull TooltipFlag flagIn, IPipeStructure structure) {} + }; +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/NodeExposingCapabilities.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/NodeExposingCapabilities.java new file mode 100644 index 0000000000..d1e38e0a87 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/NodeExposingCapabilities.java @@ -0,0 +1,16 @@ +package com.gregtechceu.gtceu.api.graphnet.pipenet; + +import net.minecraft.core.Direction; +import net.minecraftforge.common.capabilities.ICapabilityProvider; + +import org.jetbrains.annotations.NotNull; + +public interface NodeExposingCapabilities { + + @NotNull + ICapabilityProvider getProvider(); + + default Direction exposedFacing() { + return null; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/NodeWithCovers.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/NodeWithCovers.java new file mode 100644 index 0000000000..1afbbe1931 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/NodeWithCovers.java @@ -0,0 +1,11 @@ +package com.gregtechceu.gtceu.api.graphnet.pipenet; + +import com.gregtechceu.gtceu.api.capability.ICoverable; + +import org.jetbrains.annotations.Nullable; + +public interface NodeWithCovers { + + @Nullable + ICoverable getCoverable(); +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/NodeWithFacingToOthers.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/NodeWithFacingToOthers.java new file mode 100644 index 0000000000..76cbfcdfd6 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/NodeWithFacingToOthers.java @@ -0,0 +1,14 @@ +package com.gregtechceu.gtceu.api.graphnet.pipenet; + +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; + +import net.minecraft.core.Direction; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface NodeWithFacingToOthers { + + @Nullable + Direction getFacingToOther(@NotNull NetNode other); +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/WorldPipeCapConnectionNode.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/WorldPipeCapConnectionNode.java new file mode 100644 index 0000000000..a56b9571e7 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/WorldPipeCapConnectionNode.java @@ -0,0 +1,109 @@ +package com.gregtechceu.gtceu.api.graphnet.pipenet; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.graphnet.GraphClassType; +import com.gregtechceu.gtceu.api.graphnet.net.IGraphNet; +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; +import com.gregtechceu.gtceu.utils.FacingPos; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.ICapabilityProvider; +import net.minecraftforge.common.util.LazyOptional; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class WorldPipeCapConnectionNode extends NetNode implements NodeWithFacingToOthers, NodeExposingCapabilities { + + public static final int SORTING_KEY = 432; + + public static final GraphClassType TYPE = new GraphClassType<>(GTCEu.MOD_ID, + "WorldPipeCapConnectionNode", + WorldPipeCapConnectionNode::resolve); + + private @NotNull FacingPos posAndFacing; + + public WorldPipeCapConnectionNode(WorldPipeNet net) { + super(net); + sortingKey = SORTING_KEY; + posAndFacing = FacingPos.ZERO; + } + + private static WorldPipeCapConnectionNode resolve(IGraphNet net) { + if (net instanceof WorldPipeNet w) return new WorldPipeCapConnectionNode(w); + GTCEu.LOGGER.error( + "Attempted to initialize a WorldPipeCapConnectionNode to a non-WorldPipeNet. If relevant NPEs occur later, this is most likely the cause."); + return null; + } + + public WorldPipeNode getParent() { + return getNet().getNode(getEquivalencyData().getPos()); + } + + public WorldPipeCapConnectionNode setPosAndFacing(FacingPos posAndFacing) { + this.posAndFacing = posAndFacing; + return this; + } + + @Override + public CompoundTag serializeNBT() { + CompoundTag tag = super.serializeNBT(); + tag.putLong("Pos", posAndFacing.getPos().asLong()); + tag.putByte("Facing", (byte) posAndFacing.getDirection().get3DDataValue()); + return tag; + } + + @Override + public void deserializeNBT(CompoundTag nbt) { + super.deserializeNBT(nbt); + this.setPosAndFacing(new FacingPos(BlockPos.of(nbt.getLong("Pos")), + Direction.from3DDataValue(nbt.getByte("Facing")))); + } + + @Override + public @NotNull WorldPipeNet getNet() { + return (WorldPipeNet) super.getNet(); + } + + @Override + public @NotNull FacingPos getEquivalencyData() { + return posAndFacing; + } + + @Override + public @NotNull GraphClassType getType() { + return TYPE; + } + + @Override + public @Nullable Direction getFacingToOther(@NotNull NetNode other) { + if (other instanceof WorldPipeNode n && n.getEquivalencyData().equals(posAndFacing.getPos())) + return posAndFacing.getDirection().getOpposite(); + else return null; + } + + @Override + public @NotNull ICapabilityProvider getProvider() { + WorldPipeNode parent = getParent(); + if (parent == null) return EMPTY; + ICapabilityProvider prov = parent.getBlockEntity().getTargetWithCapabilities(parent, + posAndFacing.getDirection()); + return prov != null ? prov : EMPTY; + } + + @Override + public Direction exposedFacing() { + return posAndFacing.getDirection().getOpposite(); + } + + private static final ICapabilityProvider EMPTY = new ICapabilityProvider() { + + @Override + public @NotNull LazyOptional getCapability(Capability capability, Direction facing) { + return LazyOptional.empty(); + } + }; +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/WorldPipeNet.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/WorldPipeNet.java new file mode 100644 index 0000000000..89cb490cb0 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/WorldPipeNet.java @@ -0,0 +1,251 @@ +package com.gregtechceu.gtceu.api.graphnet.pipenet; + +import com.gregtechceu.gtceu.api.blockentity.IDirtyNotifiable; +import com.gregtechceu.gtceu.api.capability.ICoverable; +import com.gregtechceu.gtceu.api.cover.CoverBehavior; +import com.gregtechceu.gtceu.api.graphnet.GraphClassType; +import com.gregtechceu.gtceu.api.graphnet.MultiNodeHelper; +import com.gregtechceu.gtceu.api.graphnet.graph.INetGraph; +import com.gregtechceu.gtceu.api.graphnet.net.IGraphNet; +import com.gregtechceu.gtceu.api.graphnet.net.NetEdge; +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; +import com.gregtechceu.gtceu.api.graphnet.net.WorldSavedNet; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeCapabilityWrapper; +import com.gregtechceu.gtceu.api.graphnet.pipenet.predicate.BlockedPredicate; +import com.gregtechceu.gtceu.api.graphnet.predicate.EdgePredicate; +import com.gregtechceu.gtceu.api.graphnet.predicate.NetPredicateType; +import com.gregtechceu.gtceu.api.graphnet.traverse.EdgeDirection; +import com.gregtechceu.gtceu.common.cover.ShutterCover; +import com.gregtechceu.gtceu.utils.reference.WeakHashSet; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.registries.Registries; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.Level; + +import it.unimi.dsi.fastutil.Hash; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.Reference2ObjectMap; +import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap; +import lombok.Getter; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Stream; + +public abstract class WorldPipeNet extends WorldSavedNet { + + public static final int MULTI_NET_TIMEOUT = 10; + + private static final Reference2ObjectMap, Set> dimensionNets = new Reference2ObjectOpenHashMap<>(); + + @Getter + private ServerLevel level; + private ResourceKey fallbackDimension; + + public WorldPipeNet(Function graphBuilder) { + super(graphBuilder); + } + + public WorldPipeNet(boolean directed) { + super(directed); + } + + public void setLevel(ServerLevel level) { + if (getLevel() == level) return; + this.level = level; + dimensionNets.compute(getDimension(), (k, v) -> { + if (v == null) v = new WeakHashSet<>(); + v.add(this); + return v; + }); + } + + protected ResourceKey getDimension() { + if (level == null) { + return Objects.requireNonNullElse(fallbackDimension, Level.OVERWORLD); + } else return level.dimension(); + } + + @Override + public void load(@NotNull CompoundTag nbt) { + fallbackDimension = ResourceKey.create(Registries.DIMENSION, new ResourceLocation(nbt.getString("Dimension"))); + super.load(nbt); + } + + @Override + public @NotNull CompoundTag save(@NotNull CompoundTag compound) { + compound.putString("Dimension", getDimension().location().toString()); + return super.save(compound); + } + + /** + * Called when a PipeTileEntity is marked dirty through {@link IDirtyNotifiable#markAsDirty()}, which is generally + * when the state of its covers is changed. + * + * @param blockEntity the block entity that's been marked dirty. + * @param node the associated node. + */ + public void updatePredication(@NotNull WorldPipeNode node, @NotNull PipeBlockEntity blockEntity) { + boolean dirty = false; + for (NetEdge edge : getBacker().getTouchingEdges(node, EdgeDirection.ALL)) { + NetNode neighbor = edge.getOppositeNode(node); + if (neighbor == null) continue; + CoverBehavior cNode = null; + CoverBehavior cNeighbor = null; + if (neighbor instanceof NodeWithFacingToOthers n) { + Direction facing = n.getFacingToOther(node); + if (facing != null) { + cNode = node.getBlockEntity().getCoverHolder().getCoverAtSide(facing.getOpposite()); + ICoverable view; + if (neighbor instanceof NodeWithCovers c && (view = c.getCoverable()) != null) { + cNeighbor = view.getCoverAtSide(facing); + } + } + } + dirty |= predicateEdge(edge, node, cNode, neighbor, cNeighbor); + } + if (dirty) this.setDirty(); + } + + /** + * Preferred method to override if your net has complex custom predication rules. If the net is directed, + * this method will not be called twice, so special handling for directedness is needed. + * + * @param source the source of the edge. + * @param coverSource the cover on the source facing the target. + * @param target the target of the edge. + * @param coverTarget the cover on the target facing the source. + * @return whether the predication state has changed and this net needs to be marked dirty. + */ + protected boolean predicateEdge(@NotNull NetEdge edge, @NotNull NetNode source, + @Nullable CoverBehavior coverSource, + @NotNull NetNode target, @Nullable CoverBehavior coverTarget) { + Map, EdgePredicate> prevValue = new Object2ObjectOpenHashMap<>( + edge.getPredicateHandler().getPredicateSet()); + edge.getPredicateHandler().clearPredicates(); + coverPredication(edge, coverSource, coverTarget); + boolean edgeDifferent = !prevValue.equals(edge.getPredicateHandler().getPredicateSet()); + if (getGraph().isDirected()) { + edge = getEdge(target, source); + if (edge == null) return edgeDifferent; + if (edgeDifferent) { + prevValue.clear(); + prevValue.putAll(edge.getPredicateHandler().getPredicateSet()); + } + edge.getPredicateHandler().clearPredicates(); + coverPredication(edge, coverSource, coverTarget); + if (!edgeDifferent) { + edgeDifferent = !prevValue.equals(edge.getPredicateHandler().getPredicateSet()); + } + } + return edgeDifferent; + } + + /** + * Preferred method to override if your net has custom predication rules that only depend on covers. + * If the net is directed, this method will be called twice, so no special handling for directedness is + * needed. + * + * @param edge the edge to predicate + * @param a the cover on the source of the edge + * @param b the cover on the sink of the edge + */ + protected void coverPredication(@NotNull NetEdge edge, @Nullable CoverBehavior a, @Nullable CoverBehavior b) { + if (a instanceof ShutterCover aS && aS.isWorkingEnabled() || + b instanceof ShutterCover bS && bS.isWorkingEnabled()) { + edge.getPredicateHandler().setPredicate(BlockedPredicate.TYPE.getNew()); + } + } + + public abstract PipeCapabilityWrapper buildCapabilityWrapper(@NotNull PipeBlockEntity owner, + @NotNull WorldPipeNode node); + + @Override + public @NotNull GraphClassType getDefaultNodeType() { + return WorldPipeNode.TYPE; + } + + public @Nullable WorldPipeNode getNode(@NotNull BlockPos equivalencyData) { + return (WorldPipeNode) getNode((Object) equivalencyData); + } + + public @NotNull WorldPipeNode getOrCreateNode(@NotNull BlockPos pos) { + WorldPipeNode node = getNode(pos); + if (node == null) { + node = new WorldPipeNode(this); + node.setPos(pos); + addNode(node); + } + return node; + } + + protected Stream<@NotNull WorldPipeNet> sameDimensionNetsStream() { + return dimensionNets.getOrDefault(this.getDimension(), Collections.emptySet()).stream() + .filter(Objects::nonNull); + } + + public void synchronizeNode(WorldPipeNode node) { + // basically, if another net has a node in the exact same position, then we know it's the same block. + // thus, we set up a multi net node handler for the node in order to manage the overlap + // this is disk-load safe, since this method is called during nbt deserialization. + sameDimensionNetsStream().map(n -> n.getNode(node.getEquivalencyData())).filter(Objects::nonNull) + .forEach(n -> { + if (n.overlapHelper != node.overlapHelper) { + if (node.overlapHelper == null) { + // n handler is not null + node.overlapHelper = n.overlapHelper; + n.overlapHelper.addNode(node); + return; + } + } else if (n.overlapHelper == null) { + // both handlers are null + node.overlapHelper = new MultiNodeHelper(MULTI_NET_TIMEOUT); + node.overlapHelper.addNode(n); + } + // n handler does not match cast handler + n.overlapHelper = node.overlapHelper; + n.overlapHelper.addNode(node); + }); + } + + /** + * Get the network ID for this net. Must be unique and deterministic between server and client, but can change + * between mod versions. + * + * @return the net's network id. + */ + public abstract int getNetworkID(); + + @Contract(value = " -> new", pure = true) + public static @NotNull Object2ObjectOpenCustomHashMap getSensitiveHashMap() { + return new Object2ObjectOpenCustomHashMap<>(SensitiveStrategy.INSTANCE); + } + + protected static class SensitiveStrategy implements Hash.Strategy { + + public static final SensitiveStrategy INSTANCE = new SensitiveStrategy(); + + @Override + public int hashCode(NetNode o) { + return Objects.hash(o, o.getNet()); + } + + @Override + public boolean equals(NetNode a, NetNode b) { + return a.equals(b) && a.getNet().equals(b.getNet()); + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/WorldPipeNode.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/WorldPipeNode.java new file mode 100644 index 0000000000..b4f245dfbe --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/WorldPipeNode.java @@ -0,0 +1,135 @@ +package com.gregtechceu.gtceu.api.graphnet.pipenet; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.capability.ICoverable; +import com.gregtechceu.gtceu.api.data.tag.TagPrefix; +import com.gregtechceu.gtceu.api.graphnet.GraphClassType; +import com.gregtechceu.gtceu.api.graphnet.MultiNodeHelper; +import com.gregtechceu.gtceu.api.graphnet.net.BlockPosNode; +import com.gregtechceu.gtceu.api.graphnet.net.IGraphNet; +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.IWorldPipeNetTile; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; +import com.gregtechceu.gtceu.common.data.GTBlockEntities; +import com.gregtechceu.gtceu.common.data.GTMaterialBlocks; +import com.gregtechceu.gtceu.common.data.GTMaterials; +import com.gregtechceu.gtceu.utils.GTUtil; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraftforge.common.capabilities.ICapabilityProvider; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.ref.WeakReference; + +public class WorldPipeNode extends BlockPosNode + implements NodeWithFacingToOthers, NodeWithCovers, NodeExposingCapabilities { + + public static final GraphClassType TYPE = new GraphClassType<>(GTCEu.MOD_ID, "WorldPipeNode", + WorldPipeNode::resolve); + + private static final PipeBlockEntity FALLBACK = new PipeBlockEntity(GTBlockEntities.PIPE.get(), BlockPos.ZERO, + GTMaterialBlocks.MATERIAL_PIPE_BLOCKS.get(TagPrefix.pipeNormal, GTMaterials.Aluminium).getDefaultState()); + + @Nullable + MultiNodeHelper overlapHelper; + + private WeakReference tileReference; + + public WorldPipeNode(WorldPipeNet net) { + super(net); + } + + private static WorldPipeNode resolve(IGraphNet net) { + if (net instanceof WorldPipeNet w) return new WorldPipeNode(w); + GTCEu.LOGGER.error( + "Attempted to initialize a WorldPipeNode to a non-WorldPipeNet. If relevant NPEs occur later, this is most likely the cause."); + return null; + } + + public @NotNull IWorldPipeNetTile getBlockEntity() { + IWorldPipeNetTile tile = getBlockEntity(true); + if (tile == null) { + // something went very wrong, return the fallback to prevent NPEs and remove us from the net. + getNet().removeNode(this); + tile = FALLBACK; + } + return tile; + } + + @Nullable + public IWorldPipeNetTile getBlockEntityNoLoading() { + return getBlockEntity(false); + } + + private IWorldPipeNetTile getBlockEntity(boolean allowLoading) { + if (tileReference != null) { + IWorldPipeNetTile tile = tileReference.get(); + if (tile != null) return tile; + } + Level level = getNet().getLevel(); + if (!allowLoading && !level.isLoaded(getEquivalencyData())) return null; + BlockEntity tile = level.getBlockEntity(getEquivalencyData()); + if (tile instanceof IWorldPipeNetTile pipe) { + this.tileReference = new WeakReference<>(pipe); + return pipe; + } else return null; + } + + @Override + public void onRemove() { + if (this.overlapHelper != null) { + this.overlapHelper.removeNode(this); + this.overlapHelper = null; + } + } + + @Override + public @NotNull WorldPipeNet getNet() { + return (WorldPipeNet) super.getNet(); + } + + @Override + public WorldPipeNode setPos(BlockPos pos) { + super.setPos(pos); + this.getNet().synchronizeNode(this); + return this; + } + + @Override + public boolean traverse(long queryTick, boolean simulate) { + if (overlapHelper != null) { + return overlapHelper.traverse(this.getNet(), queryTick, simulate); + } else return true; + } + + @Override + public @NotNull BlockPos getEquivalencyData() { + return super.getEquivalencyData(); + } + + @Override + public @NotNull GraphClassType getType() { + return TYPE; + } + + @Override + public @Nullable Direction getFacingToOther(@NotNull NetNode other) { + return other instanceof WorldPipeNode n ? + GTUtil.getFacingToNeighbor(this.getEquivalencyData(), n.getEquivalencyData()) : null; + } + + @Override + public @Nullable ICoverable getCoverable() { + return getBlockEntity().getCoverHolder(); + } + + @Override + public @NotNull ICapabilityProvider getProvider() { + return getBlockEntity(); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/logic/LossFunction.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/logic/LossFunction.java new file mode 100644 index 0000000000..817537b717 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/logic/LossFunction.java @@ -0,0 +1,111 @@ +package com.gregtechceu.gtceu.api.graphnet.pipenet.logic; + +/** + * A bunch of loss functions. By the power of Wolfram Alpha. + * Demonstration Graph + */ +public enum LossFunction { + + // DO NOT REORDER FUNCTIONS, THE ORDER IS USED FOR NBT SERIALIZATION + /** + * x value is lost every tick. + *
+ * A constant rate. + */ + ARITHMETIC { + + @Override + public float applyLoss(float value, float factorX, float factorY, int timePassed) { + float initialThermalEnergy = value; + value -= Math.signum(value) * factorX; + if (value < initialThermalEnergy) return 0; + return tolerate(value); + } + }, + /** + * x% of value is lost every tick. + *
+ * Faster than {@link LossFunction#ARITHMETIC} at large values, but slower at small values. + */ + GEOMETRIC { + + @Override + public float applyLoss(float value, float factorX, float factorY, int timePassed) { + value *= Math.pow(1 - (factorX / 100), timePassed); + return tolerate(value); + } + }, + /** + * value is raised to the power of 1 - x every tick. + *
+ * Faster than {@link LossFunction#GEOMETRIC} at large values, but incredibly slow at small values. + */ + POWER { + + @Override + public float applyLoss(float value, float factorX, float factorY, int timePassed) { + value = (float) (Math.signum(value) * + Math.pow(Math.abs(value), Math.pow(1 - factorX, timePassed))); + return tolerate(value); + } + }, + /** + * x% of value is lost, then y more, every tick. + *
+ * Slightly faster than {@link LossFunction#GEOMETRIC} at large values, + * slightly faster than {@link LossFunction#ARITHMETIC} at small values. + */ + GEOMETRIC_ARITHMETIC { + + @Override + public float applyLoss(float value, float factorX, float factorY, int timePassed) { + float initialThermalEnergy = value; + + float a = 1 - (factorX / 100); + float b = Math.signum(value) * factorY; + value = (float) ((b - Math.pow(a, timePassed) * + (-a * value + b + value)) / (a - 1)); + + if (value < initialThermalEnergy) return 0; + return tolerate(value); + } + }, + /** + * value is raised to the power of 1 - x, then y% more is lost, every tick. + *
+ * Slightly faster than {@link LossFunction#POWER} at large values, + * slightly faster than {@link LossFunction#GEOMETRIC} at small values. + */ + POWER_GEOMETRIC { + + @Override + public float applyLoss(float value, float factorX, float factorY, int timePassed) { + float c = 1 - factorX; + value = (float) (Math.pow(1 - (factorY / 100), (Math.pow(c, timePassed) - 1) / (c - 1)) * + Math.pow(Math.abs(value), Math.pow(c, timePassed)) * Math.signum(value)); + return tolerate(value); + } + }, + /** + * The evaluation of value = value - x * (value ^ y) is recursively found for every tick passed. + */ + WEAK_SCALING { + + @Override + public float applyLoss(float value, float factorX, float factorY, int timePassed) { + for (int i = 0; i < timePassed; i++) { + if (value < 0) value += factorX * Math.pow(-value, factorY); + else if (value > 0) value -= factorX * Math.pow(value, factorY); + } + return tolerate(value); + } + }; + + public static final float TOLERANCE = 0.1f; + + protected float tolerate(float value) { + return Math.abs(value) < TOLERANCE ? 0 : value; + } + + public abstract float applyLoss(float value, float factorX, float factorY, int timePassed); +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/logic/TemperatureLogic.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/logic/TemperatureLogic.java new file mode 100644 index 0000000000..72f6eb5ce8 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/logic/TemperatureLogic.java @@ -0,0 +1,291 @@ +package com.gregtechceu.gtceu.api.graphnet.pipenet.logic; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.graphnet.MultiNodeHelper; +import com.gregtechceu.gtceu.api.graphnet.logic.INetLogicEntryListener; +import com.gregtechceu.gtceu.api.graphnet.logic.NetLogicData; +import com.gregtechceu.gtceu.api.graphnet.logic.NetLogicEntry; +import com.gregtechceu.gtceu.api.graphnet.logic.NetLogicType; +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IBurnable; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IFreezable; +import com.gregtechceu.gtceu.client.particle.GTOverheatParticle; +import com.gregtechceu.gtceu.utils.GTUtil; + +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; + +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.ref.WeakReference; + +@Accessors(chain = true) +public final class TemperatureLogic extends NetLogicEntry { + + public static final TemperatureLogicType TYPE = new TemperatureLogicType(); + + public static final int DEFAULT_TEMPERATURE = 298; + + private WeakReference netListener; + private boolean isMultiNodeHelper = false; + + @Getter + @Setter + private int temperatureMaximum; + @Getter + @Setter + private @Nullable Integer partialBurnTemperature; + @Getter + @Setter + private int temperatureMinimum; + @Getter + @Setter + private float thermalEnergy; + @Getter + @Setter + private int thermalMass; + + @Getter + @Setter + private @NotNull TemperatureLossFunction restorationFunction = new TemperatureLossFunction(); + @Getter + @Setter + private int functionPriority; + @Getter + private long lastRestorationTick; + + @Override + public @NotNull TemperatureLogicType getType() { + return TYPE; + } + + @Contract("_ -> this") + public TemperatureLogic setInitialThermalEnergy(float energy) { + this.thermalEnergy = energy; + return this; + } + + @Override + public void registerToMultiNodeHelper(MultiNodeHelper helper) { + this.isMultiNodeHelper = true; + this.netListener = new WeakReference<>(helper); + } + + @Override + public void registerToNetLogicData(NetLogicData data) { + if (!isMultiNodeHelper) this.netListener = new WeakReference<>(data); + } + + @Override + public void deregisterFromNetLogicData(NetLogicData data) { + if (!isMultiNodeHelper) this.netListener = new WeakReference<>(null); + } + + public @NotNull TemperatureLogic getNew() { + return new TemperatureLogic(); + } + + public boolean isOverMaximum(int temperature) { + return temperature > getTemperatureMaximum(); + } + + public boolean isOverPartialBurnThreshold(int temperature) { + return getPartialBurnTemperature() != null && temperature > getPartialBurnTemperature(); + } + + public boolean isUnderMinimum(int temperature) { + return temperature < getTemperatureMinimum(); + } + + public void defaultHandleTemperature(Level world, BlockPos pos) { + int temp = getTemperature(GTUtil.getCurrentServerTick()); + if (isUnderMinimum(temp)) { + BlockState state = world.getBlockState(pos); + if (state.getBlock() instanceof IFreezable freezable) { + freezable.fullyFreeze(state, world, pos); + } else { + world.removeBlock(pos, false); + } + } else if (isOverMaximum(temp)) { + BlockState state = world.getBlockState(pos); + if (state.getBlock() instanceof IBurnable burnable) { + burnable.fullyBurn(state, world, pos); + } else { + world.removeBlock(pos, false); + } + } else if (isOverPartialBurnThreshold(temp)) { + BlockState state = world.getBlockState(pos); + if (state.getBlock() instanceof IBurnable burnable) { + burnable.partialBurn(state, world, pos); + } + } + } + + public void applyThermalEnergy(float energy, long tick) { + restoreTemperature(tick); + this.thermalEnergy += energy; + // since the decay logic is synced and deterministic, + // the only time client and server will desync is on external changes. + INetLogicEntryListener listener = this.netListener.get(); + if (listener != null) listener.markLogicEntryAsUpdated(this, false); + } + + public void moveTowardsTemperature(int temperature, long tick, float mult, boolean noParticle) { + int temp = getTemperature(tick); + float thermalEnergy = (float) (this.thermalMass * (temperature - temp) * + (1 - Math.pow(0.5, mult / this.thermalMass))); + if (noParticle) { + float thermalMax = this.thermalMass * (GTOverheatParticle.TEMPERATURE_CUTOFF - DEFAULT_TEMPERATURE); + if (thermalEnergy + this.thermalEnergy > thermalMax) { + thermalEnergy = thermalMax - this.thermalEnergy; + } + } + applyThermalEnergy(thermalEnergy, tick); + } + + public int getTemperature(long tick) { + restoreTemperature(tick); + return (int) (this.thermalEnergy / this.thermalMass) + DEFAULT_TEMPERATURE; + } + + private void restoreTemperature(long tick) { + long timePassed = tick - lastRestorationTick; + // sometimes the tick time randomly warps backward for no explicable reason, on both server and client. + if (timePassed > 0) { + float energy = this.thermalEnergy; + this.lastRestorationTick = tick; + if (timePassed >= Integer.MAX_VALUE) { + this.thermalEnergy = 0; + } else this.thermalEnergy = restorationFunction.restoreTemperature(energy, (int) timePassed); + } + } + + @Override + public boolean mergedToMultiNodeHelper() { + return true; + } + + @Override + public void merge(NetNode otherOwner, NetLogicEntry unknown) { + if (!(unknown instanceof TemperatureLogic other)) return; + if (other.getTemperatureMinimum() > this.getTemperatureMinimum()) + this.setTemperatureMinimum(other.getTemperatureMinimum()); + if (other.getTemperatureMaximum() < this.getTemperatureMaximum()) + this.setTemperatureMaximum(other.getTemperatureMaximum()); + // since merge also occurs during nbt load, ignore the other's thermal energy. + if (other.getThermalMass() < this.getThermalMass()) this.setThermalMass(other.getThermalMass()); + if (other.getFunctionPriority() > this.getFunctionPriority()) { + this.setRestorationFunction(other.getRestorationFunction()); + this.setFunctionPriority(other.getFunctionPriority()); + } + if (other.getPartialBurnTemperature() != null && (this.getPartialBurnTemperature() == null || + other.getPartialBurnTemperature() < this.getPartialBurnTemperature())) + this.setPartialBurnTemperature(other.getPartialBurnTemperature()); + } + + @Override + public CompoundTag serializeNBT() { + CompoundTag tag = new CompoundTag(); + tag.putFloat("ThermalEnergy", this.thermalEnergy); + tag.putInt("TemperatureMax", this.temperatureMaximum); + tag.putInt("TemperatureMin", this.temperatureMinimum); + tag.putInt("ThermalMass", this.thermalMass); + tag.put("RestorationFunction", this.restorationFunction.serializeNBT()); + tag.putInt("FunctionPrio", this.functionPriority); + if (partialBurnTemperature != null) tag.putInt("PartialBurn", partialBurnTemperature); + return tag; + } + + @Override + public void deserializeNBT(CompoundTag nbt) { + this.thermalEnergy = nbt.getFloat("ThermalEnergy"); + this.temperatureMaximum = nbt.getInt("TemperatureMax"); + this.temperatureMinimum = nbt.getInt("TemperatureMin"); + this.thermalMass = nbt.getInt("ThermalMass"); + this.restorationFunction = new TemperatureLossFunction(nbt.getCompound("RestorationFunction")); + this.functionPriority = nbt.getInt("FunctionPrio"); + if (nbt.contains("PartialBurn")) { + this.partialBurnTemperature = nbt.getInt("PartialBurn"); + } else this.partialBurnTemperature = null; + } + + @Override + public void encode(FriendlyByteBuf buf, boolean fullChange) { + buf.writeFloat(this.thermalEnergy); + if (fullChange) { + buf.writeVarInt(this.temperatureMaximum); + buf.writeVarInt(this.temperatureMinimum); + buf.writeVarInt(this.thermalMass); + this.restorationFunction.encode(buf); + buf.writeVarInt(this.functionPriority); + // laughs in java 9 + // noinspection ReplaceNullCheck + if (this.partialBurnTemperature == null) buf.writeVarInt(-1); + else buf.writeVarInt(this.partialBurnTemperature); + } + } + + @Override + public void decode(FriendlyByteBuf buf, boolean fullChange) { + this.thermalEnergy = buf.readFloat(); + if (fullChange) { + this.temperatureMaximum = buf.readVarInt(); + this.temperatureMinimum = buf.readVarInt(); + this.thermalMass = buf.readVarInt(); + this.restorationFunction.decode(buf); + this.functionPriority = buf.readVarInt(); + int partialBurn = buf.readVarInt(); + if (partialBurn != -1) this.partialBurnTemperature = partialBurn; + else this.partialBurnTemperature = null; + } + } + + public static class TemperatureLogicType extends NetLogicType { + + public TemperatureLogicType() { + super(GTCEu.MOD_ID, "Temperature", TemperatureLogic::new, new TemperatureLogic()); + } + + public TemperatureLogic getWith(@NotNull TemperatureLossFunction temperatureRestorationFunction, + int temperatureMaximum) { + return getWith(temperatureRestorationFunction, temperatureMaximum, 1); + } + + public TemperatureLogic getWith(@NotNull TemperatureLossFunction temperatureRestorationFunction, + int temperatureMaximum, int temperatureMinimum) { + return getWith(temperatureRestorationFunction, temperatureMaximum, temperatureMinimum, 1000); + } + + public TemperatureLogic getWith(@NotNull TemperatureLossFunction temperatureRestorationFunction, + int temperatureMaximum, int temperatureMinimum, int thermalMass) { + return getWith(temperatureRestorationFunction, temperatureMaximum, temperatureMinimum, thermalMass, 0); + } + + public TemperatureLogic getWith(@NotNull TemperatureLossFunction temperatureRestorationFunction, + int temperatureMaximum, int temperatureMinimum, int thermalMass, + @Nullable Integer partialBurnTemperature) { + return getWith(temperatureRestorationFunction, temperatureMaximum, temperatureMinimum, thermalMass, + partialBurnTemperature, 0); + } + + public TemperatureLogic getWith(@NotNull TemperatureLossFunction temperatureRestorationFunction, + int temperatureMaximum, int temperatureMinimum, int thermalMass, + @Nullable Integer partialBurnTemperature, int functionPriority) { + return getNew() + .setRestorationFunction(temperatureRestorationFunction) + .setTemperatureMaximum(temperatureMaximum) + .setTemperatureMinimum(temperatureMinimum) + .setThermalMass(thermalMass) + .setPartialBurnTemperature(partialBurnTemperature) + .setFunctionPriority(functionPriority); + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/logic/TemperatureLossFunction.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/logic/TemperatureLossFunction.java new file mode 100644 index 0000000000..f9596d0c0f --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/logic/TemperatureLossFunction.java @@ -0,0 +1,89 @@ +package com.gregtechceu.gtceu.api.graphnet.pipenet.logic; + +import com.lowdragmc.lowdraglib.networking.IPacket; +import com.lowdragmc.lowdraglib.syncdata.ITagSerializable; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.FriendlyByteBuf; + +import it.unimi.dsi.fastutil.floats.Float2ObjectArrayMap; + +public class TemperatureLossFunction implements ITagSerializable, IPacket { + + private static final Float2ObjectArrayMap CABLE_LOSS_CACHE = new Float2ObjectArrayMap<>(); + private static final Float2ObjectArrayMap PIPE_LOSS_CACHE = new Float2ObjectArrayMap<>(); + + private LossFunction function; + private float factorX; + private float factorY; + + public TemperatureLossFunction(LossFunction function, float factorX) { + this.function = function; + this.factorX = factorX; + } + + public TemperatureLossFunction(LossFunction function, float factorX, float factorY) { + this.function = function; + this.factorX = factorX; + this.factorY = factorY; + } + + public TemperatureLossFunction() {} + + public TemperatureLossFunction(CompoundTag tag) { + deserializeNBT(tag); + } + + public float restoreTemperature(float energy, int timePassed) { + return function.applyLoss(energy, factorX, factorY, timePassed); + } + + public static TemperatureLossFunction getOrCreateCable(float factor) { + TemperatureLossFunction function = CABLE_LOSS_CACHE.get(factor); + if (function == null) { + function = new TemperatureLossFunction(LossFunction.WEAK_SCALING, factor, 0.35f); + CABLE_LOSS_CACHE.put(factor, function); + } + return function; + } + + public static TemperatureLossFunction getOrCreatePipe(float factor) { + TemperatureLossFunction function = PIPE_LOSS_CACHE.get(factor); + if (function == null) { + // since pipes are hollow the exponent is larger + function = new TemperatureLossFunction(LossFunction.WEAK_SCALING, factor, 0.45f); + PIPE_LOSS_CACHE.put(factor, function); + } + return function; + } + + @Override + public CompoundTag serializeNBT() { + CompoundTag tag = new CompoundTag(); + tag.putInt("Ordinal", function.ordinal()); + tag.putFloat("X", factorX); + if (factorY != 0) tag.putFloat("Y", factorY); + return tag; + } + + @Override + public void deserializeNBT(CompoundTag nbt) { + function = LossFunction.values()[nbt.getInt("Ordinal")]; + factorX = nbt.getFloat("X"); + factorY = nbt.getFloat("Y"); + } + + @Override + public void encode(FriendlyByteBuf buf) { + buf.writeVarInt(function.ordinal()); + buf.writeFloat(factorX); + buf.writeFloat(factorY); + } + + @Override + public void decode(FriendlyByteBuf buf) { + function = LossFunction.values()[buf.readVarInt()]; + factorX = buf.readFloat(); + factorY = buf.readFloat(); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/IBurnable.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/IBurnable.java new file mode 100644 index 0000000000..a1ec86632f --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/IBurnable.java @@ -0,0 +1,22 @@ +package com.gregtechceu.gtceu.api.graphnet.pipenet.physical; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; + +public interface IBurnable { + + /** + * Called when the block should be partially burned.
+ * Allows for partial burns with reference to temperature logic, used in insulated cables for example. + */ + default void partialBurn(BlockState state, Level world, BlockPos pos) {} + + /** + * Called when the block should be fully burned. + */ + default void fullyBurn(BlockState state, Level world, BlockPos pos) { + world.setBlockAndUpdate(pos, Blocks.FIRE.defaultBlockState()); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/IFreezable.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/IFreezable.java new file mode 100644 index 0000000000..5e770de9b7 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/IFreezable.java @@ -0,0 +1,16 @@ +package com.gregtechceu.gtceu.api.graphnet.pipenet.physical; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; + +public interface IFreezable { + + /** + * Called when the block should be fully frozen. + */ + default void fullyFreeze(BlockState state, Level world, BlockPos pos) { + world.setBlockAndUpdate(pos, Blocks.FROSTED_ICE.defaultBlockState()); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/IInsulatable.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/IInsulatable.java new file mode 100644 index 0000000000..0b51c49317 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/IInsulatable.java @@ -0,0 +1,6 @@ +package com.gregtechceu.gtceu.api.graphnet.pipenet.physical; + +public interface IInsulatable { + + boolean isInsulated(); +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/IPipeCapabilityObject.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/IPipeCapabilityObject.java new file mode 100644 index 0000000000..0ac6cb6e4d --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/IPipeCapabilityObject.java @@ -0,0 +1,13 @@ +package com.gregtechceu.gtceu.api.graphnet.pipenet.physical; + +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeCapabilityWrapper; + +import net.minecraftforge.common.capabilities.ICapabilityProvider; + +import org.jetbrains.annotations.NotNull; + +public interface IPipeCapabilityObject extends ICapabilityProvider { + + void init(@NotNull PipeBlockEntity tile, @NotNull PipeCapabilityWrapper wrapper); +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/IPipeChanneledStructure.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/IPipeChanneledStructure.java new file mode 100644 index 0000000000..247a8f4a7b --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/IPipeChanneledStructure.java @@ -0,0 +1,6 @@ +package com.gregtechceu.gtceu.api.graphnet.pipenet.physical; + +public interface IPipeChanneledStructure extends IPipeStructure { + + int getChannelCount(); +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/IPipeMaterialStructure.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/IPipeMaterialStructure.java new file mode 100644 index 0000000000..706ddfc421 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/IPipeMaterialStructure.java @@ -0,0 +1,8 @@ +package com.gregtechceu.gtceu.api.graphnet.pipenet.physical; + +import com.gregtechceu.gtceu.api.data.tag.TagPrefix; + +public interface IPipeMaterialStructure extends IPipeStructure { + + TagPrefix getPrefix(); +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/IPipeStructure.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/IPipeStructure.java new file mode 100644 index 0000000000..c9569b311f --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/IPipeStructure.java @@ -0,0 +1,72 @@ +package com.gregtechceu.gtceu.api.graphnet.pipenet.physical; + +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; +import com.gregtechceu.gtceu.api.gui.GuiTextures; +import com.gregtechceu.gtceu.client.renderer.pipe.PipeModelRedirector; +import com.gregtechceu.gtceu.utils.GTUtil; + +import com.lowdragmc.lowdraglib.gui.texture.ResourceTexture; + +import net.minecraft.core.Direction; +import net.minecraft.util.StringRepresentable; +import net.minecraft.world.phys.shapes.Shapes; +import net.minecraft.world.phys.shapes.VoxelShape; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +public interface IPipeStructure extends StringRepresentable { + + /** + * Used as reference for misc things, e.g. rendering the backing of a cover. + * + * @return render thickness + */ + float getRenderThickness(); + + boolean isPaintable(); + + PipeModelRedirector getModel(); + + default ResourceTexture getPipeTexture(boolean isBlock) { + return isBlock ? GuiTextures.TOOL_WIRE_CONNECT : GuiTextures.TOOL_WIRE_BLOCK; + } + + /** + * Allows for controlling what sides can be connected to based on current connections, + * such as in the case of optical and laser pipes. + */ + default boolean canConnectTo(Direction side, byte connectionMask) { + return true; + } + + @Contract("_ -> new") + default VoxelShape getPipeBoxes(@NotNull PipeBlockEntity tileContext) { + VoxelShape pipeBoxes = Shapes.empty(); + float thickness = getRenderThickness(); + if ((tileContext.getCoverAdjustedConnectionMask() & 63) < 63) { + pipeBoxes = Shapes.or(pipeBoxes, getSideBox(null, thickness)); + } + for (Direction facing : GTUtil.DIRECTIONS) { + if (tileContext.isConnectedCoverAdjusted(facing)) + pipeBoxes = Shapes.or(pipeBoxes, getSideBox(facing, thickness)); + } + return pipeBoxes; + } + + static VoxelShape getSideBox(Direction side, float thickness) { + float min = (1.0f - thickness) / 2.0f, max = min + thickness; + float faceMin = 0f, faceMax = 1f; + + if (side == null) + return Shapes.box(min, min, min, max, max, max); + return switch (side) { + case WEST -> Shapes.box(faceMin, min, min, min, max, max); + case EAST -> Shapes.box(max, min, min, faceMax, max, max); + case NORTH -> Shapes.box(min, min, faceMin, max, max, min); + case SOUTH -> Shapes.box(min, min, max, max, max, faceMax); + case UP -> Shapes.box(min, max, min, max, faceMax, max); + case DOWN -> Shapes.box(min, faceMin, min, max, min, max); + }; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/PipeStructureRegistrationEvent.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/PipeStructureRegistrationEvent.java new file mode 100644 index 0000000000..90bc5caf41 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/PipeStructureRegistrationEvent.java @@ -0,0 +1,27 @@ +package com.gregtechceu.gtceu.api.graphnet.pipenet.physical; + +import net.minecraftforge.eventbus.api.Event; +import net.minecraftforge.fml.event.IModBusEvent; + +import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; +import java.util.Set; + +public final class PipeStructureRegistrationEvent extends Event implements IModBusEvent { + + private final Map, Set> registry = new Object2ObjectLinkedOpenHashMap<>(); + + public void register(@NotNull T structure) { + // noinspection unchecked + Set structures = (Set) registry.computeIfAbsent(structure.getClass(), + k -> new ObjectLinkedOpenHashSet<>()); + structures.add(structure); + } + + Map, Set> getRegistry() { + return registry; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/PipeStructureRegistry.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/PipeStructureRegistry.java new file mode 100644 index 0000000000..4b98f129e4 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/PipeStructureRegistry.java @@ -0,0 +1,26 @@ +package com.gregtechceu.gtceu.api.graphnet.pipenet.physical; + +import net.minecraftforge.fml.ModLoader; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.UnmodifiableView; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +@SuppressWarnings("unchecked") +public final class PipeStructureRegistry { + + private static final Map, Set> REGISTRY = fireEvent(); + + public static @NotNull @UnmodifiableView Set getStructures(Class structureClass) { + return (Set) REGISTRY.getOrDefault(structureClass, Collections.emptySet()); + } + + private static Map, Set> fireEvent() { + PipeStructureRegistrationEvent event = new PipeStructureRegistrationEvent(); + ModLoader.get().postEvent(event); + return event.getRegistry(); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/block/ActivablePipeBlock.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/block/ActivablePipeBlock.java new file mode 100644 index 0000000000..dc1641bf4b --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/block/ActivablePipeBlock.java @@ -0,0 +1,43 @@ +package com.gregtechceu.gtceu.api.graphnet.pipenet.physical.block; + +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IPipeStructure; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.ActivablePipeBlockEntity; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; +import com.gregtechceu.gtceu.common.data.GTBlockEntities; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.ref.WeakReference; + +public abstract class ActivablePipeBlock extends PipeBlock { + + public ActivablePipeBlock(BlockBehaviour.Properties properties, IPipeStructure structure) { + super(properties, structure); + } + + @Override + public @Nullable BlockEntity newBlockEntity(BlockPos pos, BlockState state) { + return new ActivablePipeBlockEntity(GTBlockEntities.ACTIVATABLE_PIPE.get(), pos, state); + } + + @Override + public @Nullable ActivablePipeBlockEntity getBlockEntity(@NotNull BlockGetter world, @NotNull BlockPos pos) { + if (lastTilePos.get().equals(pos)) { + PipeBlockEntity tile = lastTile.get().get(); + if (tile != null && !tile.isRemoved()) return (ActivablePipeBlockEntity) tile; + } + BlockEntity tile = world.getBlockEntity(pos); + if (tile instanceof ActivablePipeBlockEntity pipe) { + lastTilePos.set(pos.immutable()); + lastTile.set(new WeakReference<>(pipe)); + return pipe; + } else return null; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/block/MaterialPipeBlockItem.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/block/MaterialPipeBlockItem.java new file mode 100644 index 0000000000..fcedbf4233 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/block/MaterialPipeBlockItem.java @@ -0,0 +1,49 @@ +package com.gregtechceu.gtceu.api.graphnet.pipenet.physical.block; + +import com.gregtechceu.gtceu.api.data.chemical.material.Material; + +import net.minecraft.client.color.item.ItemColor; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import org.jetbrains.annotations.NotNull; + +public class MaterialPipeBlockItem extends PipeBlockItem { + + public MaterialPipeBlockItem(PipeMaterialBlock block, Item.Properties properties) { + super(block, properties); + } + + @Override + public @NotNull PipeMaterialBlock getBlock() { + return (PipeMaterialBlock) super.getBlock(); + } + + @Override + public Component getName(ItemStack stack) { + Material material = getBlock().material; + return material == null ? Component.literal("unnamed") : + getBlock().getStructure().getPrefix().getLocalizedName(material); + } + + @Override + public Component getDescription() { + Material material = getBlock().material; + return material == null ? Component.literal("unnamed") : + getBlock().getStructure().getPrefix().getLocalizedName(material); + } + + @OnlyIn(Dist.CLIENT) + public static ItemColor tintColor() { + return (itemStack, index) -> { + if (itemStack.getItem() instanceof MaterialPipeBlockItem materialBlockItem) { + return materialBlockItem.getBlock().tinted(materialBlockItem.getBlock().defaultBlockState(), null, null, + index); + } + return -1; + }; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/block/PipeBlock.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/block/PipeBlock.java new file mode 100644 index 0000000000..fa3550265b --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/block/PipeBlock.java @@ -0,0 +1,583 @@ +package com.gregtechceu.gtceu.api.graphnet.pipenet.physical.block; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.block.MaterialBlock; +import com.gregtechceu.gtceu.api.cover.CoverBehavior; +import com.gregtechceu.gtceu.api.data.tag.TagPrefix; +import com.gregtechceu.gtceu.api.graphnet.pipenet.IPipeNetNodeHandler; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNet; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.logic.TemperatureLogic; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IPipeChanneledStructure; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IPipeStructure; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeCoverHolder; +import com.gregtechceu.gtceu.api.item.tool.GTToolType; +import com.gregtechceu.gtceu.api.item.tool.ToolHelper; +import com.gregtechceu.gtceu.client.ClientProxy; +import com.gregtechceu.gtceu.common.data.GTBlockEntities; +import com.gregtechceu.gtceu.common.item.CoverPlaceBehavior; +import com.gregtechceu.gtceu.config.ConfigHolder; +import com.gregtechceu.gtceu.data.recipe.VanillaRecipeHelper; +import com.gregtechceu.gtceu.utils.EntityDamageUtil; +import com.gregtechceu.gtceu.utils.GTUtil; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.SpawnPlacements; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.level.*; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.EntityBlock; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityTicker; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.storage.loot.LootParams; +import net.minecraft.world.level.storage.loot.parameters.LootContextParams; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.EntityCollisionContext; +import net.minecraft.world.phys.shapes.Shapes; +import net.minecraft.world.phys.shapes.VoxelShape; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import lombok.Getter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.ref.WeakReference; +import java.util.*; + +import static com.gregtechceu.gtceu.api.block.BlockProperties.SERVER_TICK; + +public abstract class PipeBlock extends Block implements EntityBlock { + + // do not touch these two unless you know what you are doing + protected final ThreadLocal lastTilePos = ThreadLocal.withInitial(() -> new BlockPos(0, 0, 0)); + protected final ThreadLocal> lastTile = ThreadLocal + .withInitial(() -> new WeakReference<>(null)); + + @Getter + private final IPipeStructure structure; + + public PipeBlock(BlockBehaviour.Properties properties, IPipeStructure structure) { + super(properties); + this.structure = structure; + this.registerDefaultState(this.defaultBlockState().setValue(SERVER_TICK, false)); + } + + // net logic // + + public void doPlacementLogic(PipeBlockEntity tile, Direction placedBlockSearchSide) { + for (Direction facing : GTUtil.DIRECTIONS) { + BlockEntity neighbor = tile.getNeighbor(facing); + if (neighbor instanceof PipeBlockEntity other) { + // first check -- does the other tile have a cover that would prevent connection + CoverBehavior cover = other.getCoverHolder().getCoverAtSide(facing.getOpposite()); + if (cover != null && !cover.canPipePassThrough()) continue; + // second check -- connect to matching mark pipes if side matches or config allows. + if (tile.getPaintingColor() == other.getPaintingColor() && (facing == placedBlockSearchSide || + !ConfigHolder.INSTANCE.machines.gt6StylePipesCables)) { + if (coverCheck(tile, other, facing)) connectTile(tile, other, facing); + continue; + } + // third check -- connect to pipes with an open connection, no matter the mark status. + if (other.isConnected(facing.getOpposite())) { + if (coverCheck(tile, other, facing)) connectTile(tile, other, facing); + } + } else if (facing == placedBlockSearchSide) { + // if the placed on tile supports one of our capabilities, connect to it. + tile.updateActiveStatus(facing, true); + } + } + } + + @Override + public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, + BlockHitResult hit) { + ItemStack itemStack = player.getItemInHand(hand); + BlockEntity entity = level.getBlockEntity(pos); + + PipeBlockEntity pipeBlockEntity = null; + if (entity instanceof PipeBlockEntity pbe) { + pipeBlockEntity = pbe; + } + if (pipeBlockEntity == null) { + return InteractionResult.FAIL; + } + + if (pipeBlockEntity.getFrameMaterial() == null) { + var frameBlock = MaterialBlock.getFrameboxFromItem(itemStack); + if (frameBlock != null) { + pipeBlockEntity.setFrameMaterial(frameBlock.material); + if (!player.isCreative()) itemStack.shrink(1); + SoundType type = VanillaRecipeHelper.isMaterialWood(frameBlock.material) ? SoundType.WOOD : + SoundType.METAL; + level.playSound(player, pos, + type.getPlaceSound(), SoundSource.BLOCKS, + (type.getVolume() + 1.0F) / 2.0F, type.getPitch() * 0.8F); + player.swing(hand); + return InteractionResult.SUCCESS; + } + } + + if (itemStack.getItem() instanceof PipeBlockItem itemPipe) { + BlockPos offsetPos = pos.offset(hit.getDirection().getNormal()); + BlockState stateAtSide = level.getBlockState(offsetPos); + if (stateAtSide.getBlock() instanceof MaterialBlock matBlock && matBlock.tagPrefix == TagPrefix.frameGt) { + boolean wasPlaced = matBlock.replaceWithFramedPipe(level, offsetPos, stateAtSide, player, itemStack, + hit); + if (wasPlaced) { + connectTile(pipeBlockEntity, pipeBlockEntity.getPipeNeighbor(hit.getDirection(), false), + hit.getDirection()); + } + return wasPlaced ? InteractionResult.CONSUME : InteractionResult.FAIL; + } + } + + Set types = ToolHelper.getToolTypes(itemStack); + if (!types.isEmpty() && ToolHelper.canUse(itemStack)) { + var result = pipeBlockEntity.onToolClick(types, itemStack, new UseOnContext(player, hand, hit)); + if (result.getSecond() == InteractionResult.CONSUME && player instanceof ServerPlayer serverPlayer) { + ToolHelper.playToolSound(result.getFirst(), serverPlayer); + + if (!serverPlayer.isCreative()) { + ToolHelper.damageItem(itemStack, serverPlayer, 1); + } + } + return result.getSecond(); + } + return InteractionResult.PASS; + } + + /* + * @Override + * public void onBlockClicked(@NotNull Level worldIn, @NotNull BlockPos pos, @NotNull Player playerIn) { + * PipeBlockEntity tile = getBlockEntity(worldIn, pos); + * if (tile != null) { + * RayTraceAABB trace = collisionRayTrace(playerIn, worldIn, pos); + * if (trace == null) { + * super.onBlockClicked(worldIn, pos, playerIn); + * return; + * } + * Direction facing = trace.sideHit; + * Direction actualSide = CoverRayTracer.determineGridSideHit(trace); + * if (actualSide != null) facing = actualSide; + * Cover cover = tile.getCoverHolder().getCoverAtSide(facing); + * if (cover != null) { + * if (cover.onLeftClick(playerIn, trace)) return; + * } + * } + * super.onBlockClicked(worldIn, pos, playerIn); + * } + */ + /** + * Should be called to verify if a connection can be formed before + * {@link #connectTile(PipeBlockEntity, PipeBlockEntity, Direction)} is called. + * + * @return whether the connection is allowed. + */ + public static boolean coverCheck(@NotNull PipeBlockEntity tile, @Nullable PipeBlockEntity tileAcross, + Direction facing) { + CoverBehavior tileCover = tile.getCoverHolder().getCoverAtSide(facing); + CoverBehavior acrossCover = tileAcross != null ? + tileAcross.getCoverHolder().getCoverAtSide(facing.getOpposite()) : + null; + return (tileCover == null || tileCover.canPipePassThrough()) && + (acrossCover == null || acrossCover.canPipePassThrough()); + } + + public static void connectTile(@NotNull PipeBlockEntity tile, @Nullable PipeBlockEntity tileAcross, + Direction facing) { + // abort connection if either tile refuses it. + if (!tile.canConnectTo(facing) || tileAcross != null && !tileAcross.canConnectTo(facing.getOpposite())) return; + + // if one of the pipes is larger than the other, render it closed. + tile.setConnected(facing, tileAcross != null && + tile.getStructure().getRenderThickness() > tileAcross.getStructure().getRenderThickness()); + if (tileAcross == null) return; + tileAcross.setConnected(facing.getOpposite(), + tileAcross.getStructure().getRenderThickness() > tile.getStructure().getRenderThickness()); + if (tile.getLevel().isClientSide) return; + + boolean blocked1 = tile.isBlocked(facing); + boolean blocked2 = tileAcross.isBlocked(facing.getOpposite()); + + Map tile2Nodes = new Object2ObjectOpenHashMap<>(); + for (WorldPipeNode node : getNodesForTile(tileAcross)) { + tile2Nodes.put(node.getNet(), node); + } + + for (WorldPipeNode node : getNodesForTile(tile)) { + WorldPipeNet net = node.getNet(); + WorldPipeNode other = tile2Nodes.get(net); + if (other == null) continue; + if (!blocked1 && !blocked2) { + net.addEdge(node, other, true); + } else if (net.getGraph().isDirected()) { + if (!blocked1) net.addEdge(other, node, false); + else if (!blocked2) net.addEdge(node, other, false); + } + } + } + + public static void disconnectTile(@NotNull PipeBlockEntity tile, @Nullable PipeBlockEntity tileAcross, + Direction facing) { + tile.setDisconnected(facing); + if (tileAcross == null) return; + tileAcross.setDisconnected(facing.getOpposite()); + if (tile.getLevel().isClientSide) return; + + Map tile2Nodes = new Object2ObjectOpenHashMap<>(); + for (WorldPipeNode node : getNodesForTile(tileAcross)) { + tile2Nodes.put(node.getNet(), node); + } + + for (WorldPipeNode node : getNodesForTile(tile)) { + WorldPipeNet net = node.getNet(); + WorldPipeNode other = tile2Nodes.get(net); + if (other == null) continue; + net.removeEdge(node, other, true); + } + } + + public static void blockTile(@NotNull PipeBlockEntity tile, @Nullable PipeBlockEntity tileAcross, + Direction facing) { + tile.setBlocked(facing); + if (tileAcross == null || tile.getLevel().isClientSide) return; + + Map tile2Nodes = new Object2ObjectOpenHashMap<>(); + for (WorldPipeNode node : getNodesForTile(tileAcross)) { + tile2Nodes.put(node.getNet(), node); + } + + for (WorldPipeNode node : getNodesForTile(tile)) { + WorldPipeNet net = node.getNet(); + WorldPipeNode other = tile2Nodes.get(net); + if (other == null) continue; + net.removeEdge(other, node, false); + } + } + + public static void unblockTile(@NotNull PipeBlockEntity tile, @Nullable PipeBlockEntity tileAcross, + Direction facing) { + tile.setUnblocked(facing); + if (tileAcross == null || tile.getLevel().isClientSide) return; + + Map tile2Nodes = new Object2ObjectOpenHashMap<>(); + for (WorldPipeNode node : getNodesForTile(tileAcross)) { + tile2Nodes.put(node.getNet(), node); + } + + for (WorldPipeNode node : getNodesForTile(tile)) { + WorldPipeNet net = node.getNet(); + WorldPipeNode other = tile2Nodes.get(net); + if (other == null) continue; + net.addEdge(other, node, false); + } + } + + public boolean allowsBlocking() { + return true; + } + + public static Collection getNodesForTile(PipeBlockEntity tile) { + assert tile.getLevel() instanceof ServerLevel; + return tile.getBlockType().getHandler(tile) + .getOrCreateFromNets((ServerLevel) tile.getLevel(), tile.getBlockPos(), tile.getStructure()); + } + + @NotNull + public abstract IPipeNetNodeHandler getHandler(PipeBlockEntity tileContext); + + @NotNull + protected abstract IPipeNetNodeHandler getHandler(@NotNull ItemStack stack); + + // misc stuff // + + @Override + public void appendHoverText(ItemStack stack, @Nullable BlockGetter level, List tooltip, + TooltipFlag flag) { + getHandler(stack).addInformation(stack, level, tooltip, flag, getStructure()); + if (getStructure() instanceof IPipeChanneledStructure channeledStructure) { + if (channeledStructure.getChannelCount() > 1) + tooltip.add(Component.translatable("gtceu.pipe.channels", channeledStructure.getChannelCount())); + } + if (GTUtil.isShiftDown()) { + tooltip.add(Component.translatable(getConnectLangKey())); + tooltip.add(Component.translatable("gtceu.tool_action.screwdriver.access_covers")); + tooltip.add(Component.translatable("gtceu.tool_action.crowbar")); + } else { + tooltip.add(Component.translatable("gtceu.tool_action.show_tooltips")); + } + } + + protected String getConnectLangKey() { + return "gtceu.tool_action.wrench.connect"; + } + + @Override + public List getDrops(BlockState state, LootParams.Builder builder) { + BlockEntity blockEntity = builder.getOptionalParameter(LootContextParams.BLOCK_ENTITY); + List drops = new ArrayList<>(super.getDrops(state, builder)); + if (blockEntity instanceof PipeBlockEntity pipeTile) { + pipeTile.getDrops(drops, state); + } + return drops; + } + + @Override + public boolean isValidSpawn(BlockState state, BlockGetter level, BlockPos pos, SpawnPlacements.Type type, + EntityType entityType) { + return false; + } + + @Override + public void entityInside(BlockState state, Level level, BlockPos pos, Entity entity) { + if (level.isClientSide || !(entity instanceof LivingEntity living)) return; + PipeBlockEntity tile = getBlockEntity(level, pos); + if (tile != null && tile.getFrameMaterial() == null && tile.getOffsetTimer() % 10 == 0) { + TemperatureLogic logic = tile.getTemperatureLogic(); + if (logic != null) { + long tick = GTCEu.getMinecraftServer().getTickCount(); + EntityDamageUtil.applyTemperatureDamage(living, logic.getTemperature(tick), 1f, 5); + } + } + } + + /* + * TODO fix + * + * @Override + * public boolean recolorBlock(@NotNull Level world, @NotNull BlockPos pos, @NotNull Direction side, + * + * @NotNull EnumDyeColor color) { + * if (getStructure().isPaintable()) { + * PipeBlockEntity tile = getBlockEntity(world, pos); + * if (tile != null && tile.getPaintingColor() != color.colorValue) { + * tile.setPaintingColor(color.colorValue, false); + * return true; + * } + * } + * return false; + * } + */ + + // collision boxes // + + @SuppressWarnings("deprecation") + @Override + public @NotNull VoxelShape getCollisionShape(BlockState state, BlockGetter level, BlockPos pos, + CollisionContext context) { + var tile = getBlockEntity(level, pos); + if (tile == null) { + return Shapes.block(); + } + List shapes = new ArrayList<>(); + shapes.add(getStructure().getPipeBoxes(tile)); + tile.getCoverBoxes(shapes::add); + VoxelShape shape = shapes.stream().reduce(Shapes.empty(), Shapes::or); + if (tile.getFrameMaterial() != null) { + return Shapes.or(shape, MaterialBlock.FRAME_COLLISION_BOX); + } + return shape; + } + + @Override + public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) { + if (context instanceof EntityCollisionContext entityCtx && entityCtx.getEntity() instanceof Player player) { + if (hasPipeCollisionChangingItem(level, pos, player)) { + return Shapes.block(); + } + } + + PipeBlockEntity tile = getBlockEntity(level, pos); + if (tile == null) { + return Shapes.block(); + } + List shapes = new ArrayList<>(); + shapes.add(getStructure().getPipeBoxes(tile)); + tile.getCoverBoxes(shapes::add); + VoxelShape shape = shapes.stream().reduce(Shapes.empty(), Shapes::or); + if (tile.getFrameMaterial() != null) { + return Shapes.or(shape, MaterialBlock.FRAME_COLLISION_BOX); + } + return shape; + } + + public boolean hasPipeCollisionChangingItem(BlockGetter world, BlockPos pos, Player player) { + return hasPipeCollisionChangingItem(world, pos, player.getMainHandItem()) || + hasPipeCollisionChangingItem(world, pos, player.getOffhandItem()) || + player.isShiftKeyDown() && isHoldingPipe(player); + } + + public boolean isHoldingPipe(Player player) { + return isPipeItem(player.getMainHandItem()) || isPipeItem(player.getOffhandItem()); + } + + public boolean isPipeItem(ItemStack stack) { + return stack.getItem() instanceof PipeBlockItem block && this.getClass().isInstance(block.getBlock()); + } + + @Nullable + public static PipeBlock getBlockFromItem(@NotNull ItemStack stack) { + if (stack.getItem() instanceof PipeBlockItem block) return block.getBlock(); + else return null; + } + + public boolean hasPipeCollisionChangingItem(BlockGetter world, BlockPos pos, ItemStack stack) { + if (isPipeTool(stack)) return true; + + PipeBlockEntity tile = getBlockEntity(world, pos); + if (tile == null) return false; + + PipeCoverHolder coverable = tile.getCoverHolder(); + final boolean hasAnyCover = coverable.hasAnyCover(); + + if (hasAnyCover && ToolHelper.isTool(stack, GTToolType.SCREWDRIVER)) return true; + final boolean acceptsCovers = coverable.acceptsCovers(); + + return CoverPlaceBehavior.isCoverBehaviorItem(stack, () -> hasAnyCover, coverDef -> acceptsCovers); + } + + public boolean isPipeTool(@NotNull ItemStack stack) { + return ToolHelper.isTool(stack, getToolClass()); + } + + public GTToolType getToolClass() { + return GTToolType.WRENCH; + } + + // blockstate // + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + builder.add(SERVER_TICK); + } + + @Override + public boolean triggerEvent(BlockState pState, Level pLevel, BlockPos pPos, int pId, int pParam) { + BlockEntity tile = pLevel.getBlockEntity(pPos); + if (tile != null) { + return tile.triggerEvent(pId, pParam); + } + return false; + } + + // tile entity // + + @Nullable + @Override + public BlockEntityTicker getTicker(Level level, BlockState state, + BlockEntityType blockEntityType) { + if (!level.isClientSide && state.getValue(SERVER_TICK)) { + return (pLevel, pPos, pState, pTile) -> { + if (pTile instanceof PipeBlockEntity pipeNode) { + pipeNode.serverTick(); + } + }; + } + return null; + } + + @Nullable + public PipeBlockEntity getBlockEntity(@NotNull BlockGetter world, @NotNull BlockPos pos) { + if (lastTilePos.get().equals(pos)) { + PipeBlockEntity tile = lastTile.get().get(); + if (tile != null && !tile.isRemoved()) return tile; + } + BlockEntity tile = world.getBlockEntity(pos); + if (tile instanceof PipeBlockEntity pipe) { + lastTilePos.set(pos.immutable()); + lastTile.set(new WeakReference<>(pipe)); + return pipe; + } else return null; + } + + @Nullable + @Override + public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { + return new PipeBlockEntity(GTBlockEntities.PIPE.get(), pos, state); + } + + /* + * @Override + * public void onNeighborChange(BlockState state, LevelReader level, BlockPos pos, BlockPos neighbor) { + * super.onNeighborChange(state, level, pos, neighbor); + * Direction facing = GTUtil.getFacingToNeighbor(pos, neighbor); + * if (facing == null) return; + * PipeBlockEntity tile = getBlockEntity(level, pos); + * if (tile != null) tile.onNeighborChanged(level.getBlockState(neighbor).getBlock(), neighbor, false); + * } + */ + + @Override + public int getLightEmission(BlockState state, BlockGetter level, BlockPos pos) { + PipeBlockEntity tile = getBlockEntity(level, pos); + if (tile != null && level instanceof Level level1) { + TemperatureLogic temperatureLogic = tile.getTemperatureLogic(); + int temp = temperatureLogic == null ? 0 : temperatureLogic + .getTemperature(!level1.isClientSide ? + level1.getServer().getTickCount() : + ClientProxy.getServerTickCount()); + // max light at 5000 K + // min light at 500 K + if (temp >= 5000) { + return 15; + } + if (temp > 500) { + return (temp - 500) * 15 / (4500); + } + } + return 0; + } + + // cover compatibility // + + @Override + public void neighborChanged(BlockState state, Level level, BlockPos pos, + Block neighborBlock, BlockPos neighborPos, boolean movedByPiston) { + PipeBlockEntity tile = getBlockEntity(level, pos); + if (tile != null) { + tile.onNeighborChanged(neighborBlock, neighborPos, movedByPiston); + tile.getCoverHolder().updateInputRedstoneSignals(); + } + super.neighborChanged(state, level, pos, neighborBlock, neighborPos, movedByPiston); + } + + @Override + public boolean shouldCheckWeakPower(BlockState state, SignalGetter level, BlockPos pos, Direction side) { + // The check in World::getRedstonePower in the vanilla code base is reversed. Setting this to false will + // actually cause getWeakPower to be called, rather than prevent it. + return true; + } + + @Override + public int getSignal(BlockState state, BlockGetter level, BlockPos pos, Direction direction) { + PipeBlockEntity tile = getBlockEntity(level, pos); + return tile != null ? tile.getCoverHolder().getOutputRedstoneSignal(direction) : 0; + } + + @Override + public boolean canConnectRedstone(BlockState state, BlockGetter level, BlockPos pos, + @Nullable Direction direction) { + PipeBlockEntity tile = getBlockEntity(level, pos); + return tile != null && tile.getCoverHolder().canConnectRedstone(direction); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/block/PipeBlockItem.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/block/PipeBlockItem.java new file mode 100644 index 0000000000..c27696e496 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/block/PipeBlockItem.java @@ -0,0 +1,70 @@ +package com.gregtechceu.gtceu.api.graphnet.pipenet.physical.block; + +import com.gregtechceu.gtceu.api.capability.ICoverable; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; +import com.gregtechceu.gtceu.api.item.component.IInteractionItem; +import com.gregtechceu.gtceu.common.data.GTItems; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.DyeColor; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.BlockHitResult; + +import org.jetbrains.annotations.NotNull; + +public class PipeBlockItem extends BlockItem { + + public PipeBlockItem(PipeBlock block, Item.Properties properties) { + super(block, properties); + } + + @Override + public @NotNull PipeBlock getBlock() { + return (PipeBlock) super.getBlock(); + } + + @Override + public boolean placeBlock(BlockPlaceContext context, BlockState state) { + Level level = context.getLevel(); + BlockPos pos = context.getClickedPos(); + Direction side = context.getClickedFace(); + + var clickedPos = pos.relative(side.getOpposite()); + var baseNode = getBlock().getBlockEntity(level, clickedPos); + if (baseNode != null) { + var sideAttach = ICoverable + .determineGridSideHit(new BlockHitResult(context.getClickLocation(), side, clickedPos, false)); + if (sideAttach != null && level.isEmptyBlock(clickedPos.relative(sideAttach))) { + pos = clickedPos.relative(sideAttach); + side = sideAttach; + context = BlockPlaceContext.at(context, clickedPos, sideAttach); + } + } + + if (super.placeBlock(context, state)) { + ItemStack offhand = context.getPlayer().getOffhandItem(); + for (int i = 0; i < DyeColor.values().length; i++) { + if (offhand.is(GTItems.SPRAY_CAN_DYES[i].get())) { + ((IInteractionItem) GTItems.SPRAY_CAN_DYES[i].get().getComponents().get(0)).use( + GTItems.SPRAY_CAN_DYES[i].get(), level, + context.getPlayer(), InteractionHand.OFF_HAND); + break; + } + } + + PipeBlockEntity tile = getBlock().getBlockEntity(level, pos); + if (tile != null) { + tile.placedBy(context.getItemInHand(), context.getPlayer()); + getBlock().doPlacementLogic(tile, side.getOpposite()); + } + return true; + } else return false; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/block/PipeMaterialBlock.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/block/PipeMaterialBlock.java new file mode 100644 index 0000000000..efc9b17736 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/block/PipeMaterialBlock.java @@ -0,0 +1,137 @@ +package com.gregtechceu.gtceu.api.graphnet.pipenet.physical.block; + +import com.gregtechceu.gtceu.api.data.chemical.material.Material; +import com.gregtechceu.gtceu.api.data.chemical.material.properties.PropertyKey; +import com.gregtechceu.gtceu.api.graphnet.pipenet.IPipeNetNodeHandler; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IPipeMaterialStructure; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.MaterialPipeBlockEntity; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; +import com.gregtechceu.gtceu.common.data.GTBlockEntities; +import com.gregtechceu.gtceu.config.ConfigHolder; + +import net.minecraft.client.color.block.BlockColor; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.BlockAndTintGetter; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.ref.WeakReference; +import java.util.List; + +public abstract class PipeMaterialBlock extends PipeBlock { + + public final Material material; + + public PipeMaterialBlock(BlockBehaviour.Properties properties, IPipeMaterialStructure structure, + Material material) { + super(properties, structure); + this.material = material; + } + + @OnlyIn(Dist.CLIENT) + public static BlockColor tintedColor() { + return (blockState, level, blockPos, index) -> { + if (blockPos != null && level != null && + level.getBlockEntity(blockPos) instanceof PipeBlockEntity pipe) { + if (pipe.getFrameMaterial() != null) { + if (index >= 3) { + return pipe.getFrameMaterial().getMaterialRGB(index - 3); + } + } + if (index == 0 && pipe.isPainted()) { + return pipe.getPaintingColor(); + } + } + if (blockState.getBlock() instanceof PipeMaterialBlock block) { + return block.tinted(blockState, level, blockPos, index); + } + return -1; + }; + } + + public int tinted(BlockState blockState, @Nullable BlockAndTintGetter blockAndTintGetter, + @Nullable BlockPos blockPos, int index) { + if (index == 0) { + return material.getMaterialRGB(); + } + return index == 1 ? material.getMaterialSecondaryRGB() : -1; + } + + @Nullable + public static PipeMaterialBlock getBlockFromItem(@NotNull ItemStack stack) { + if (stack.getItem() instanceof MaterialPipeBlockItem block) return block.getBlock(); + else return null; + } + + @Override + public IPipeMaterialStructure getStructure() { + return (IPipeMaterialStructure) super.getStructure(); + } + + @Nullable + @Override + public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { + return new MaterialPipeBlockEntity(GTBlockEntities.MATERIAL_PIPE.get(), pos, state); + } + + @Override + @NotNull + public IPipeNetNodeHandler getHandler(PipeBlockEntity tile) { + return material.getProperty(PropertyKey.PIPENET_PROPERTIES); + } + + @Override + protected @NotNull IPipeNetNodeHandler getHandler(@NotNull ItemStack stack) { + return material.getProperty(PropertyKey.PIPENET_PROPERTIES); + } + + @Override + public String getDescriptionId() { + return material == null ? "unnamed" : + getStructure().getPrefix().getUnlocalizedName(material); + } + + @Override + public MutableComponent getName() { + return material == null ? Component.literal("unnamed") : + getStructure().getPrefix().getLocalizedName(material); + } + + @Override + public void appendHoverText(ItemStack stack, @Nullable BlockGetter level, List tooltip, + TooltipFlag flag) { + super.appendHoverText(stack, level, tooltip, flag); + if (ConfigHolder.INSTANCE.dev.debug) { + if (material != null) + tooltip.add(Component + .literal("MetaItem Id: " + getStructure().getPrefix().name + material.toCamelCaseString())); + } + } + + // tile entity // + + @Override + public @Nullable MaterialPipeBlockEntity getBlockEntity(@NotNull BlockGetter world, @NotNull BlockPos pos) { + if (lastTilePos.get().equals(pos)) { + PipeBlockEntity tile = lastTile.get().get(); + if (tile != null && !tile.isRemoved()) return (MaterialPipeBlockEntity) tile; + } + BlockEntity tile = world.getBlockEntity(pos); + if (tile instanceof MaterialPipeBlockEntity pipe) { + lastTilePos.set(pos.immutable()); + lastTile.set(new WeakReference<>(pipe)); + return pipe; + } else return null; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/blockentity/ActivablePipeBlockEntity.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/blockentity/ActivablePipeBlockEntity.java new file mode 100644 index 0000000000..8011c952e3 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/blockentity/ActivablePipeBlockEntity.java @@ -0,0 +1,33 @@ +package com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity; + +import com.lowdragmc.lowdraglib.syncdata.annotation.DescSynced; +import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; + +import lombok.Getter; +import lombok.Setter; + +public class ActivablePipeBlockEntity extends PipeBlockEntity implements IActivable { + + protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( + ActivablePipeBlockEntity.class, PipeBlockEntity.MANAGED_FIELD_HOLDER); + + @DescSynced + @Getter + @Setter + private boolean active; + + public ActivablePipeBlockEntity(BlockEntityType type, BlockPos pos, BlockState blockState) { + super(type, pos, blockState); + } + + @Override + public ManagedFieldHolder getFieldHolder() { + return MANAGED_FIELD_HOLDER; + } + + // do not save activeness to nbt, it should go away on world save & load. +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/blockentity/IActivable.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/blockentity/IActivable.java new file mode 100644 index 0000000000..d3c2c1e515 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/blockentity/IActivable.java @@ -0,0 +1,8 @@ +package com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity; + +public interface IActivable { + + void setActive(boolean active); + + boolean isActive(); +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/blockentity/IWorldPipeNetTile.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/blockentity/IWorldPipeNetTile.java new file mode 100644 index 0000000000..6f439a122b --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/blockentity/IWorldPipeNetTile.java @@ -0,0 +1,33 @@ +package com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity; + +import com.gregtechceu.gtceu.api.capability.ICoverable; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNode; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraftforge.common.capabilities.ICapabilityProvider; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.EnumMap; + +public interface IWorldPipeNetTile extends ICapabilityProvider { + + @NotNull + EnumMap getTargetsWithCapabilities(WorldPipeNode destination); + + @Nullable + BlockEntity getTargetWithCapabilities(WorldPipeNode destination, Direction facing); + + PipeCapabilityWrapper getWrapperForNode(WorldPipeNode node); + + @NotNull + ICoverable getCoverHolder(); + + Level getLevel(); + + BlockPos getBlockPos(); +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/blockentity/MaterialPipeBlockEntity.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/blockentity/MaterialPipeBlockEntity.java new file mode 100644 index 0000000000..715990b4f9 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/blockentity/MaterialPipeBlockEntity.java @@ -0,0 +1,34 @@ +package com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity; + +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.block.PipeMaterialBlock; +import com.gregtechceu.gtceu.client.renderer.pipe.AbstractPipeModel; +import com.gregtechceu.gtceu.utils.GTUtil; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.client.model.data.ModelData; + +import org.jetbrains.annotations.NotNull; + +public class MaterialPipeBlockEntity extends PipeBlockEntity { + + public MaterialPipeBlockEntity(BlockEntityType type, BlockPos pos, BlockState blockState) { + super(type, pos, blockState); + } + + @Override + public @NotNull PipeMaterialBlock getBlockType() { + return (PipeMaterialBlock) super.getBlockType(); + } + + @Override + public int getDefaultPaintingColor() { + return GTUtil.convertRGBtoARGB(getBlockType().material.getMaterialRGB()); + } + + @Override + public @NotNull ModelData getModelData() { + return super.getModelData().derive().with(AbstractPipeModel.MATERIAL_PROPERTY, getBlockType().material).build(); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/blockentity/NodeManagingPCW.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/blockentity/NodeManagingPCW.java new file mode 100644 index 0000000000..bbf6311bdd --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/blockentity/NodeManagingPCW.java @@ -0,0 +1,62 @@ +package com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity; + +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeCapConnectionNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IPipeCapabilityObject; +import com.gregtechceu.gtceu.utils.FacingPos; + +import net.minecraft.core.Direction; +import net.minecraftforge.common.capabilities.Capability; + +import it.unimi.dsi.fastutil.objects.Object2ObjectMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.EnumMap; + +public class NodeManagingPCW extends PipeCapabilityWrapper { + + private final EnumMap managed = new EnumMap<>(Direction.class); + + public NodeManagingPCW(@NotNull PipeBlockEntity owner, @NotNull WorldPipeNode node, + Object2ObjectMap, IPipeCapabilityObject> capabilities, int inactiveKey, + int activeKey) { + super(owner, node, capabilities, inactiveKey, activeKey); + } + + @Override + public void invalidate() { + for (WorldPipeCapConnectionNode n : managed.values()) { + n.getNet().removeNode(n); + } + } + + @Override + protected void setActiveInternal(@NotNull Direction facing) { + super.setActiveInternal(facing); + FacingPos pos = new FacingPos(node.getEquivalencyData(), facing); + NetNode existing = node.getNet().getNode(pos); + WorldPipeCapConnectionNode connectionNode; + if (existing instanceof WorldPipeCapConnectionNode c) { + connectionNode = c; + } else { + connectionNode = new WorldPipeCapConnectionNode(node.getNet()); + connectionNode.setPosAndFacing(pos); + connectionNode.getNet().addNode(connectionNode); + } + managed.put(facing, connectionNode); + node.getNet().addEdge(node, connectionNode, true); + } + + @Override + protected void setIdleInternal(@NotNull Direction facing) { + super.setIdleInternal(facing); + WorldPipeCapConnectionNode n = managed.remove(facing); + if (n != null) node.getNet().removeNode(n); + } + + public @Nullable WorldPipeCapConnectionNode getNodeForFacing(Direction facing) { + return managed.get(facing); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/blockentity/PipeBlockEntity.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/blockentity/PipeBlockEntity.java new file mode 100644 index 0000000000..7a792abc63 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/blockentity/PipeBlockEntity.java @@ -0,0 +1,808 @@ +package com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.block.BlockProperties; +import com.gregtechceu.gtceu.api.blockentity.IPaintable; +import com.gregtechceu.gtceu.api.blockentity.ITickSubscription; +import com.gregtechceu.gtceu.api.blockentity.NeighborCacheBlockEntity; +import com.gregtechceu.gtceu.api.capability.ICoverable; +import com.gregtechceu.gtceu.api.capability.IToolable; +import com.gregtechceu.gtceu.api.capability.forge.GTCapability; +import com.gregtechceu.gtceu.api.cover.CoverBehavior; +import com.gregtechceu.gtceu.api.data.chemical.material.Material; +import com.gregtechceu.gtceu.api.data.tag.TagPrefix; +import com.gregtechceu.gtceu.api.graphnet.logic.NetLogicData; +import com.gregtechceu.gtceu.api.graphnet.logic.NetLogicEntry; +import com.gregtechceu.gtceu.api.graphnet.logic.NetLogicRegistry; +import com.gregtechceu.gtceu.api.graphnet.logic.NetLogicType; +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNet; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.logic.TemperatureLogic; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IInsulatable; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IPipeStructure; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.block.PipeBlock; +import com.gregtechceu.gtceu.api.item.tool.GTToolType; +import com.gregtechceu.gtceu.api.item.tool.IToolGridHighlight; +import com.gregtechceu.gtceu.api.machine.TickableSubscription; +import com.gregtechceu.gtceu.client.ClientProxy; +import com.gregtechceu.gtceu.client.particle.GTOverheatParticle; +import com.gregtechceu.gtceu.client.particle.GTParticleManager; +import com.gregtechceu.gtceu.client.renderer.pipe.AbstractPipeModel; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRendererBuilder; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRendererPackage; +import com.gregtechceu.gtceu.common.data.GTMaterialBlocks; +import com.gregtechceu.gtceu.utils.GTUtil; + +import com.lowdragmc.lowdraglib.gui.editor.runtime.PersistedParser; +import com.lowdragmc.lowdraglib.gui.texture.ResourceTexture; +import com.lowdragmc.lowdraglib.syncdata.IEnhancedManaged; +import com.lowdragmc.lowdraglib.syncdata.IManagedStorage; +import com.lowdragmc.lowdraglib.syncdata.annotation.DescSynced; +import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; +import com.lowdragmc.lowdraglib.syncdata.blockentity.IAsyncAutoSyncBlockEntity; +import com.lowdragmc.lowdraglib.syncdata.blockentity.IAutoPersistBlockEntity; +import com.lowdragmc.lowdraglib.syncdata.field.FieldManagedStorage; +import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.TickTask; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.shapes.Shapes; +import net.minecraft.world.phys.shapes.VoxelShape; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.client.model.data.ModelData; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.util.LazyOptional; + +import com.mojang.datafixers.util.Pair; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap; +import lombok.Getter; +import lombok.Setter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.function.Consumer; + +public class PipeBlockEntity extends NeighborCacheBlockEntity + implements IWorldPipeNetTile, ITickSubscription, IEnhancedManaged, + IAsyncAutoSyncBlockEntity, IAutoPersistBlockEntity, IToolGridHighlight, IToolable, + IPaintable { + + protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder(PipeBlockEntity.class); + @Getter + public final IManagedStorage syncStorage = new FieldManagedStorage(this); + + public static final int DEFAULT_COLOR = 0xFFFFFFFF; + + public static final int UPDATE_PIPE_LOGIC = 0; + + @Getter + private final Int2ObjectOpenHashMap netLogicDatas = new Int2ObjectOpenHashMap<>(); + + // this tile was loaded from datafixed NBT and needs to initialize its connections + @Persisted + private boolean legacy; + + // information that is only required for determining graph topology should be stored on the tile entity level, + // while information interacted with during graph traversal should be stored on the NetLogicData level. + + @Persisted + @DescSynced + @Getter + private byte connectionMask; + @Persisted + @DescSynced + @Getter + private byte renderMask; + @Persisted + @DescSynced + @Getter + private byte blockedMask; + @Persisted + @DescSynced + @Getter + @Setter + private int paintingColor = -1; + + @Getter + @Persisted + @DescSynced + private @Nullable Material frameMaterial; + + private final List serverTicks = new ArrayList<>(); + private final List waitingToAdd = new ArrayList<>(); + + @Persisted + @DescSynced + @Getter + protected final PipeCoverHolder coverHolder = new PipeCoverHolder(this); + private final Object2ObjectOpenCustomHashMap netCapabilities = WorldPipeNet + .getSensitiveHashMap(); + + @Getter + @Nullable + private TemperatureLogic temperatureLogic; + @Nullable + private GTOverheatParticle overheatParticle; + + private final int offset = (int) (Math.random() * 20); + + private long nextDamageTime = 0; + private long nextSoundTime = 0; + + public PipeBlockEntity(BlockEntityType type, BlockPos pos, BlockState blockState) { + super(type, pos, blockState, true); + } + + @Nullable + public PipeBlockEntity getPipeNeighbor(Direction facing, boolean allowChunkloading) { + BlockEntity tile = allowChunkloading ? getNeighbor(facing) : getNeighborNoChunkloading(facing); + if (tile instanceof PipeBlockEntity pipe) return pipe; + else return null; + } + + public void getDrops(@NotNull List drops, @NotNull BlockState state) { + if (getFrameMaterial() != null) + drops.add(GTMaterialBlocks.MATERIAL_BLOCKS.get(TagPrefix.frameGt, getFrameMaterial()).asStack()); + } + + @Override + public void scheduleRenderUpdate() { + super.scheduleRenderUpdate(); + requestModelDataUpdate(); + } + + @Override + public boolean triggerEvent(int id, int type) { + if (id == 1) { // chunk re render + if (level != null && level.isClientSide) { + scheduleRenderUpdate(); + } + return true; + } + return false; + } + + @Override + public void setRemoved() { + super.setRemoved(); + if (getLevel() instanceof ServerLevel serverLevel) { + getBlockType().getHandler(this) + .removeFromNets(serverLevel, this.getBlockPos(), this.getStructure()); + netCapabilities.values().forEach(PipeCapabilityWrapper::invalidate); + } else killOverheatParticle(); + // TODO I hate this so much can someone please make it so that covers go through getDrops()? + getCoverHolder().dropAllCovers(); + } + + public ItemStack getMainDrop(@NotNull BlockState state) { + return new ItemStack(state.getBlock(), 1); + } + + public ItemStack getDrop() { + return new ItemStack(getBlockType(), 1); + } + + public long getOffsetTimer() { + return GTCEu.getMinecraftServer().getTickCount() + offset; + } + + public void placedBy(ItemStack stack, Player player) {} + + public IPipeStructure getStructure() { + return getBlockType().getStructure(); + } + + // mask // + + public boolean canConnectTo(Direction facing) { + return this.getStructure().canConnectTo(facing, connectionMask); + } + + public void setConnected(Direction facing, boolean renderClosed) { + this.connectionMask |= 1 << facing.ordinal(); + updateActiveStatus(facing, false); + if (renderClosed) { + this.renderMask |= 1 << facing.ordinal(); + } else { + this.renderMask &= ~(1 << facing.ordinal()); + } + scheduleRenderUpdate(); + } + + public void setDisconnected(Direction facing) { + this.connectionMask &= ~(1 << facing.ordinal()); + this.renderMask &= ~(1 << facing.ordinal()); + updateActiveStatus(facing, false); + scheduleRenderUpdate(); + } + + public boolean isConnected(Direction side) { + return (this.connectionMask & 1 << side.ordinal()) > 0; + } + + public boolean isConnectedCoverAdjusted(Direction facing) { + CoverBehavior cover; + return ((this.connectionMask & 1 << facing.ordinal()) > 0) || + (cover = getCoverHolder().getCoverAtSide(facing)) != null && cover.forcePipeRenderConnection(); + } + + public void setRenderClosed(Direction facing, boolean closed) { + if (closed) { + this.renderMask |= 1 << facing.ordinal(); + } else { + this.renderMask &= ~(1 << facing.ordinal()); + } + } + + public boolean renderClosed(Direction facing) { + return (this.renderMask & 1 << facing.ordinal()) > 0; + } + + public byte getCoverAdjustedConnectionMask() { + byte connectionMask = this.connectionMask; + for (Direction dir : GTUtil.DIRECTIONS) { + CoverBehavior cover = getCoverHolder().getCoverAtSide(dir); + if (cover != null) { + if (cover.forcePipeRenderConnection()) connectionMask |= 1 << dir.ordinal(); + } + } + return connectionMask; + } + + public void setBlocked(Direction facing) { + this.blockedMask |= (byte) (1 << facing.ordinal()); + scheduleRenderUpdate(); + } + + public void setUnblocked(Direction facing) { + this.blockedMask &= (byte) ~(1 << facing.ordinal()); + scheduleRenderUpdate(); + } + + public boolean isBlocked(Direction facing) { + return (this.blockedMask & 1 << facing.ordinal()) > 0; + } + + public void setFrameMaterial(@Nullable Material frameMaterial) { + this.frameMaterial = frameMaterial; + scheduleRenderUpdate(); + } + + // paint // + + public int getVisualColor() { + return isPainted() ? paintingColor : getDefaultPaintingColor(); + } + + public void setPaintingColor(int paintingColor, boolean alphaSensitive) { + if (!alphaSensitive) { + paintingColor |= 0xFF000000; + } + this.paintingColor = paintingColor; + scheduleRenderUpdate(); + } + + @Override + public boolean isPainted() { + return this.paintingColor != -1; + } + + @Override + public int getDefaultPaintingColor() { + return DEFAULT_COLOR; + } + + // ticking // + + @Override + public @Nullable TickableSubscription subscribeServerTick(Runnable runnable) { + if (!isClientSide()) { + var subscription = new TickableSubscription(runnable); + waitingToAdd.add(subscription); + var blockState = getBlockState(); + if (!blockState.getValue(BlockProperties.SERVER_TICK)) { + if (getLevel() instanceof ServerLevel serverLevel) { + blockState = blockState.setValue(BlockProperties.SERVER_TICK, true); + setBlockState(blockState); + serverLevel.getServer().tell(new TickTask(0, () -> serverLevel.setBlockAndUpdate(getBlockPos(), + getBlockState().setValue(BlockProperties.SERVER_TICK, true)))); + } + } + return subscription; + } + return null; + } + + @Override + public void unsubscribe(@Nullable TickableSubscription current) { + if (current != null) { + current.unsubscribe(); + } + } + + public final void serverTick() { + if (!waitingToAdd.isEmpty()) { + serverTicks.addAll(waitingToAdd); + waitingToAdd.clear(); + } + var iter = serverTicks.iterator(); + while (iter.hasNext()) { + var tickable = iter.next(); + if (tickable.isStillSubscribed()) { + tickable.run(); + } + if (!tickable.isStillSubscribed()) { + iter.remove(); + } + } + if (serverTicks.isEmpty() && waitingToAdd.isEmpty() && !this.isRemoved()) { + getLevel().setBlockAndUpdate(getBlockPos(), getBlockState().setValue(BlockProperties.SERVER_TICK, false)); + } + } + + @Override + public void clearRemoved() { + super.clearRemoved(); + if (getLevel() instanceof ServerLevel serverLevel) { + serverLevel.getServer().tell(new TickTask(0, this::initialize)); + } + } + + @Override + public void loadCustomPersistedData(CompoundTag tag) { + if (tag.contains("connections")) { + legacy = true; + this.connectionMask = tag.getByte("connections"); + } + if (tag.contains("blockedConnections")) { + legacy = true; + this.blockedMask = tag.getByte("blockedConnections"); + } + if (tag.contains("cover")) { + legacy = true; + PersistedParser.deserializeNBT(tag.getCompound("cover"), new HashMap<>(), + PipeCoverHolder.class, this.coverHolder); + } + } + + // activeness // + + @Override + public void onNeighborChanged(Block fromBlock, BlockPos fromPos, boolean isMoving) { + super.onNeighborChanged(fromBlock, fromPos, isMoving); + Direction facing = GTUtil.getFacingToNeighbor(this.getBlockPos(), fromPos); + coverHolder.onNeighborChanged(fromBlock, fromPos, isMoving); + updateActiveStatus(facing, false); + } + + /** + * Returns a map of facings to tile entities that should have at least one of the required capabilities. + * + * @param node the node for this tile entity. Used to identify the capabilities to match. + * @return a map of facings to tile entities. + */ + public @NotNull EnumMap getTargetsWithCapabilities(WorldPipeNode node) { + PipeCapabilityWrapper wrapper = netCapabilities.get(node); + EnumMap caps = new EnumMap<>(Direction.class); + if (wrapper == null) return caps; + + for (Direction facing : GTUtil.DIRECTIONS) { + if (wrapper.isActive(facing)) { + BlockEntity tile = getNeighbor(facing); + if (tile == null) updateActiveStatus(facing, false); + else caps.put(facing, tile); + } + } + return caps; + } + + public @Nullable BlockEntity getTargetWithCapabilities(WorldPipeNode node, Direction facing) { + PipeCapabilityWrapper wrapper = netCapabilities.get(node); + if (wrapper == null || !wrapper.isActive(facing)) return null; + else return getNeighbor(facing); + } + + @Override + public PipeCapabilityWrapper getWrapperForNode(WorldPipeNode node) { + return netCapabilities.get(node); + } + + /** + * Updates the pipe's active status based on the tile entity connected to the side. + * + * @param facing the side to check. Can be null, in which case all sides will be checked. + * @param canOpenConnection whether the pipe is allowed to open a new connection if it finds a tile it can connect + * to. + */ + public void updateActiveStatus(@Nullable Direction facing, boolean canOpenConnection) { + if (facing == null) { + for (Direction side : GTUtil.DIRECTIONS) { + updateActiveStatus(side, canOpenConnection); + } + return; + } + if (!this.isConnectedCoverAdjusted(facing) && !(canOpenConnection && canConnectTo(facing))) { + setAllIdle(facing); + return; + } + + BlockEntity tile = getNeighbor(facing); + if (tile == null || tile instanceof PipeBlockEntity) { + setAllIdle(facing); + return; + } + + boolean oneActive = false; + for (var netCapability : netCapabilities.entrySet()) { + for (Capability cap : netCapability.getValue().capabilities.keySet()) { + // hardcode an exception for hazard containers since they can + // "connect" and push the pipe contents into empty space + if (tile.getCapability(cap, facing.getOpposite()).isPresent() || + cap == GTCapability.CAPABILITY_HAZARD_CONTAINER) { + oneActive = true; + netCapability.getValue().setActive(facing); + break; + } + } + } + if (canOpenConnection && oneActive) this.setConnected(facing, false); + } + + private void setAllIdle(Direction facing) { + for (var netCapability : netCapabilities.entrySet()) { + netCapability.getValue().setIdle(facing); + } + } + + // capability // + + public LazyOptional getCapabilityCoverQuery(@NotNull Capability capability, @Nullable Direction facing) { + for (PipeCapabilityWrapper wrapper : netCapabilities.values()) { + LazyOptional cap = wrapper.getCapability(capability, facing); + if (cap.isPresent()) return cap; + } + return null; + } + + @Override + public @NotNull LazyOptional getCapability(@NotNull Capability capability, @Nullable Direction facing) { + if (capability == GTCapability.CAPABILITY_COVERABLE) { + return GTCapability.CAPABILITY_COVERABLE.orEmpty(capability, LazyOptional.of(this::getCoverHolder)); + } + LazyOptional pipeCapability = LazyOptional.empty(); + for (PipeCapabilityWrapper wrapper : netCapabilities.values()) { + if ((pipeCapability = wrapper.getCapability(capability, facing)).isPresent()) break; + } + if (!pipeCapability.isPresent()) pipeCapability = super.getCapability(capability, facing); + + CoverBehavior cover = facing == null ? null : getCoverHolder().getCoverAtSide(facing); + if (cover == null) { + if (facing == null || isConnected(facing)) { + return pipeCapability; + } + return super.getCapability(capability, facing); + } + + LazyOptional coverCapability = cover.getCapability(capability, pipeCapability); + if (coverCapability == pipeCapability) { + if (isConnectedCoverAdjusted(facing)) { + return pipeCapability; + } + return super.getCapability(capability, facing); + } + return coverCapability; + } + + // data sync management // + + public NetLogicData getNetLogicData(int networkID) { + return netLogicDatas.get(networkID); + } + + public @NotNull PipeBlock getBlockType() { + return (PipeBlock) this.getBlockState().getBlock(); + } + + @Override + public void setLevel(@NotNull Level level) { + if (level == this.getLevel()) return; + super.setLevel(level); + } + + public void initialize() { + if (!getLevel().isClientSide) { + this.netLogicDatas.clear(); + this.netCapabilities.clear(); + for (WorldPipeNode node : PipeBlock.getNodesForTile(this)) { + WorldPipeNet net = node.getNet(); + this.netCapabilities.put(node, net.buildCapabilityWrapper(this, node)); + int networkID = net.getNetworkID(); + netLogicDatas.put(networkID, node.getData()); + node.getData().addListener( + (e, r, f) -> writeLogicData(networkID, e, r, f)); + // Manually resync the data, as it's loaded & the listeners are queried for the first time *before* + // we call `addListener` on the line above. + for (var entry : node.getData().getEntries()) { + node.getData().markLogicEntryAsUpdated(entry, true); + } + if (this.temperatureLogic == null) { + TemperatureLogic candidate = node.getData().getLogicEntryNullable(TemperatureLogic.TYPE); + if (candidate != null) + updateTemperatureLogic(candidate); + } + } + if (this.legacy) { + for (Direction facing : GTUtil.DIRECTIONS) { + if (this.isConnected(facing)) { + PipeBlock.connectTile(this, this.getPipeNeighbor(facing, false), facing); + BlockPos pos = this.getBlockPos().relative(facing); + ChunkAccess chunk = getLevel().getChunk(pos); + if (chunk instanceof LevelChunk levelChunk) { + BlockEntity candidate = levelChunk + .getBlockEntity(pos, LevelChunk.EntityCreationType.CHECK); + if (candidate instanceof PipeBlockEntity pipe) + PipeBlock.connectTile(this, pipe, facing); + } + } + } + } + this.netLogicDatas.trim(); + this.netCapabilities.trim(); + updateActiveStatus(null, false); + } + } + + private void writeLogicData(int networkID, NetLogicEntry entry, boolean removed, boolean fullChange) { + writeCustomData(UPDATE_PIPE_LOGIC, buf -> { + buf.writeVarInt(networkID); + buf.writeBoolean(removed); + if (removed) buf.writeVarInt(NetLogicRegistry.getNetworkID(entry.getType())); + else NetLogicData.writeEntry(buf, entry, fullChange); + }); + } + + @Override + public void receiveCustomData(int discriminator, @NotNull FriendlyByteBuf buf) { + if (discriminator == UPDATE_PIPE_LOGIC) { + // extra check just to make sure we don't affect actual net data with our writes + if (level.isClientSide) { + int networkID = buf.readVarInt(); + boolean removed = buf.readBoolean(); + if (removed) { + NetLogicType type = NetLogicRegistry.getType(buf.readVarInt()); + NetLogicData data = this.netLogicDatas.get(networkID); + if (data != null) data.removeLogicEntry(type); + } else { + NetLogicData data = this.netLogicDatas.computeIfAbsent(networkID, i -> new NetLogicData()); + NetLogicEntry read = data.readEntry(buf); + if (read instanceof TemperatureLogic tempLogic) { + updateTemperatureLogic(tempLogic); + } + } + } + } + } + + // particle // + + public void updateTemperatureLogic(@NotNull TemperatureLogic logic) { + this.temperatureLogic = logic; + if (overheatParticle == null || !overheatParticle.isAlive()) { + long tick = this.level.isClientSide ? + ClientProxy.getServerTickCount() : + GTCEu.getMinecraftServer().getTickCount();; + int temp = logic.getTemperature(tick); + if (temp > GTOverheatParticle.TEMPERATURE_CUTOFF) { + IPipeStructure structure = this.getStructure(); + overheatParticle = new GTOverheatParticle(this, logic, structure.getPipeBoxes(this), + structure instanceof IInsulatable i && i.isInsulated()); + GTParticleManager.INSTANCE.addEffect(overheatParticle); + } + } else { + overheatParticle.setTemperatureLogic(logic); + } + } + + @OnlyIn(Dist.CLIENT) + public void killOverheatParticle() { + if (overheatParticle != null) { + overheatParticle.setExpired(); + overheatParticle = null; + } + } + + @OnlyIn(Dist.CLIENT) + public boolean isOverheatParticleAlive() { + return overheatParticle != null && overheatParticle.isAlive(); + } + + // misc overrides // + + public void scheduleNeighborShapeUpdate() { + Level level = getLevel(); + BlockPos pos = getBlockPos(); + + if (level == null || pos == null) + return; + + level.getBlockState(pos).updateNeighbourShapes(level, pos, Block.UPDATE_ALL); + } + + @Override + @SuppressWarnings("ConstantConditions") // yes this CAN actually be null + public void markAsDirty() { + if (hasLevel()) { + level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), Block.UPDATE_KNOWN_SHAPE); + } + // this most notably gets called when the covers of a pipe get updated, aka the edge predicates need syncing. + for (var node : this.netCapabilities.keySet()) { + if (node instanceof WorldPipeNode n) n.getNet().updatePredication(n, this); + } + this.setChanged(); + } + + public static @Nullable PipeBlockEntity getTileNoLoading(BlockPos pos, ResourceKey dimension) { + Level world = GTCEu.getMinecraftServer().getLevel(dimension); + if (world == null || !world.isLoaded(pos)) return null; + + BlockEntity tile = world.getBlockEntity(pos); + if (tile instanceof PipeBlockEntity pipe) return pipe; + else return null; + } + + @Override + public @NotNull ModelData getModelData() { + byte frameMask = 0; + for (Direction facing : GTUtil.DIRECTIONS) { + CoverBehavior cover = getCoverHolder().getCoverAtSide(facing); + if (cover != null) { + frameMask |= 1 << facing.ordinal(); + if (cover.forcePipeRenderConnection()) this.connectionMask |= (byte) (1 << facing.ordinal()); + } + } + frameMask = (byte) ~frameMask; + return ModelData.builder() + .with(AbstractPipeModel.THICKNESS_PROPERTY, this.getStructure().getRenderThickness()) + .with(AbstractPipeModel.CONNECTED_MASK_PROPERTY, connectionMask) + .with(AbstractPipeModel.CLOSED_MASK_PROPERTY, renderMask) + .with(AbstractPipeModel.BLOCKED_MASK_PROPERTY, blockedMask) + .with(AbstractPipeModel.COLOR_PROPERTY, getPaintingColor()) + .with(AbstractPipeModel.FRAME_MATERIAL_PROPERTY, frameMaterial) + .with(AbstractPipeModel.FRAME_MASK_PROPERTY, frameMask) + .with(CoverRendererPackage.PROPERTY, getCoverHolder().createPackage()) + .build(); + } + + public void getCoverBoxes(Consumer consumer) { + for (Direction facing : GTUtil.DIRECTIONS) { + if (getCoverHolder().hasCover(facing)) { + consumer.accept(Shapes.create(CoverRendererBuilder.PLATE_AABBS.get(facing))); + } + } + } + + @Override + public boolean shouldRenderGrid(Player player, BlockPos pos, BlockState state, ItemStack held, + Set toolTypes) { + if (toolTypes.contains(getBlockType().getToolClass()) || toolTypes.contains(GTToolType.SCREWDRIVER)) + return true; + for (CoverBehavior cover : coverHolder.getCovers()) { + if (cover.shouldRenderGrid(player, pos, state, held, toolTypes)) return true; + } + return false; + } + + public ResourceTexture sideTips(Player player, BlockPos pos, BlockState state, Set toolTypes, + Direction side) { + if (toolTypes.contains(getBlockType().getToolClass())) { + if (player.isShiftKeyDown() && this.getBlockType().allowsBlocking()) { + return getStructure().getPipeTexture(isBlocked(side)); + } else { + return getStructure().getPipeTexture(isConnected(side)); + } + } + var cover = coverHolder.getCoverAtSide(side); + if (cover != null) { + return cover.sideTips(player, pos, state, toolTypes, side); + } + return null; + } + + @Override + public Pair<@Nullable GTToolType, InteractionResult> onToolClick(@NotNull Set toolTypes, + ItemStack itemStack, UseOnContext context) { + // the side hit from the machine grid + var playerIn = context.getPlayer(); + if (playerIn == null) return Pair.of(null, InteractionResult.PASS); + + var hand = context.getHand(); + var hitResult = new BlockHitResult(context.getClickLocation(), context.getClickedFace(), + context.getClickedPos(), false); + Direction gridSide = ICoverable.determineGridSideHit(hitResult); + CoverBehavior coverBehavior = gridSide == null ? null : coverHolder.getCoverAtSide(gridSide); + if (gridSide == null) gridSide = hitResult.getDirection(); + + // Prioritize covers where they apply (Screwdriver, Soft Mallet) + if (toolTypes.contains(GTToolType.SCREWDRIVER) || (itemStack.isEmpty() && playerIn.isShiftKeyDown())) { + if (coverBehavior != null) { + return Pair.of(GTToolType.SCREWDRIVER, coverBehavior.onScrewdriverClick(playerIn, hand, hitResult)); + } + } else if (toolTypes.contains(GTToolType.SOFT_MALLET)) { + if (coverBehavior != null) { + return Pair.of(GTToolType.SOFT_MALLET, coverBehavior.onSoftMalletClick(playerIn, hand, hitResult)); + } + } else if (toolTypes.contains(this.getBlockType().getToolClass())) { + if (playerIn.isShiftKeyDown() && this.getBlockType().allowsBlocking()) { + boolean isBlocked = this.isBlocked(gridSide); + if (isBlocked) { + PipeBlock.unblockTile(this, this.getPipeNeighbor(gridSide, true), gridSide); + } else { + PipeBlock.blockTile(this, this.getPipeNeighbor(gridSide, true), gridSide); + } + } else { + boolean isOpen = this.isConnected(gridSide); + if (isOpen) { + PipeBlock.disconnectTile(this, this.getPipeNeighbor(gridSide, true), gridSide); + } else { + PipeBlock.connectTile(this, this.getPipeNeighbor(gridSide, true), gridSide); + } + } + playerIn.swing(hand); + return Pair.of(this.getBlockType().getToolClass(), InteractionResult.CONSUME); + } else if (toolTypes.contains(GTToolType.CROWBAR)) { + if (coverBehavior != null) { + if (isServerSide()) { + coverHolder.removeCover(gridSide, playerIn); + playerIn.swing(hand); + return Pair.of(GTToolType.CROWBAR, InteractionResult.CONSUME); + } + } else { + if (frameMaterial != null) { + Block.popResource(getLevel(), getBlockPos(), + GTMaterialBlocks.MATERIAL_BLOCKS.get(TagPrefix.frameGt, frameMaterial).asStack()); + frameMaterial = null; + playerIn.swing(hand); + return Pair.of(GTToolType.CROWBAR, InteractionResult.CONSUME); + } + } + } + + return Pair.of(null, InteractionResult.PASS); + } + + @Override + public ManagedFieldHolder getFieldHolder() { + return MANAGED_FIELD_HOLDER; + } + + @Override + public void onChanged() { + this.markAsDirty(); + } + + @Override + public IManagedStorage getRootStorage() { + return syncStorage; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/blockentity/PipeCapabilityWrapper.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/blockentity/PipeCapabilityWrapper.java new file mode 100644 index 0000000000..2e7ca234ce --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/blockentity/PipeCapabilityWrapper.java @@ -0,0 +1,74 @@ +package com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity; + +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IPipeCapabilityObject; + +import net.minecraft.core.Direction; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.ICapabilityProvider; +import net.minecraftforge.common.util.LazyOptional; + +import it.unimi.dsi.fastutil.objects.Object2ObjectMap; +import org.jetbrains.annotations.NotNull; + +public class PipeCapabilityWrapper implements ICapabilityProvider { + + protected byte activeMask; + protected final PipeBlockEntity owner; + protected final WorldPipeNode node; + + protected final Object2ObjectMap, IPipeCapabilityObject> capabilities; + + protected final int inactiveKey; + protected final int activeKey; + + public PipeCapabilityWrapper(@NotNull PipeBlockEntity owner, @NotNull WorldPipeNode node, + Object2ObjectMap, IPipeCapabilityObject> capabilities, + int inactiveKey, int activeKey) { + this.owner = owner; + this.node = node; + this.inactiveKey = inactiveKey; + this.activeKey = activeKey; + this.capabilities = capabilities; + for (IPipeCapabilityObject o : capabilities.values()) { + o.init(owner, this); + } + } + + public void invalidate() {} + + public void setActive(@NotNull Direction facing) { + if (!isActive(facing)) { + setActiveInternal(facing); + } + } + + protected void setActiveInternal(@NotNull Direction facing) { + this.activeMask |= 1 << facing.ordinal(); + this.node.setSortingKey(this.activeMask > 0 ? activeKey : inactiveKey); + this.owner.notifyBlockUpdate(); + } + + public void setIdle(@NotNull Direction facing) { + if (isActive(facing)) { + setIdleInternal(facing); + } + } + + protected void setIdleInternal(@NotNull Direction facing) { + this.activeMask &= ~(1 << facing.ordinal()); + this.node.setSortingKey(this.activeMask > 0 ? activeKey : inactiveKey); + this.owner.notifyBlockUpdate(); + } + + public boolean isActive(@NotNull Direction facing) { + return (this.activeMask & 1 << facing.ordinal()) > 0; + } + + @Override + public @NotNull LazyOptional getCapability(@NotNull Capability capability, Direction facing) { + IPipeCapabilityObject obj = capabilities.get(capability); + if (obj == null) return LazyOptional.empty(); + return obj.getCapability(capability, facing); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/blockentity/PipeCoverHolder.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/blockentity/PipeCoverHolder.java new file mode 100644 index 0000000000..6a9f566df1 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/physical/blockentity/PipeCoverHolder.java @@ -0,0 +1,352 @@ +package com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.capability.ICoverable; +import com.gregtechceu.gtceu.api.cover.CoverBehavior; +import com.gregtechceu.gtceu.api.cover.CoverDefinition; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.block.PipeBlock; +import com.gregtechceu.gtceu.api.machine.TickableSubscription; +import com.gregtechceu.gtceu.api.registry.GTRegistries; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRendererPackage; +import com.gregtechceu.gtceu.config.ConfigHolder; +import com.gregtechceu.gtceu.utils.GTUtil; + +import com.lowdragmc.lowdraglib.gui.editor.runtime.PersistedParser; +import com.lowdragmc.lowdraglib.syncdata.IEnhancedManaged; +import com.lowdragmc.lowdraglib.syncdata.annotation.*; +import com.lowdragmc.lowdraglib.syncdata.field.FieldManagedStorage; +import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; +import com.lowdragmc.lowdraglib.syncdata.managed.IRef; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.util.LazyOptional; +import net.minecraftforge.fluids.capability.IFluidHandler; +import net.minecraftforge.items.IItemHandler; + +import lombok.Getter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.EnumMap; +import java.util.HashMap; + +public class PipeCoverHolder implements ICoverable, IEnhancedManaged { + + public static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder(PipeCoverHolder.class); + @Getter + private final FieldManagedStorage syncStorage = new FieldManagedStorage(this); + + private final PipeBlockEntity holder; + @RequireRerender + @ReadOnlyManaged(onDirtyMethod = "onCoversDirty", + serializeMethod = "serializeCovers", + deserializeMethod = "deserializeCovers") + private final EnumMap covers = new EnumMap<>(Direction.class); + private final int[] sidedRedstoneInput = new int[6]; + + public PipeCoverHolder(PipeBlockEntity holder) { + this.holder = holder; + } + + protected final void addCoverSilent(@NotNull Direction side, @NotNull CoverBehavior cover) { + // we checked before if the side already has a cover + this.covers.put(side, cover); + } + + @Override + public final boolean acceptsCovers() { + return covers.size() < GTUtil.DIRECTIONS.length; + } + + @Override + public void setCoverAtSide(@Nullable CoverBehavior coverBehavior, Direction side) { + if (coverBehavior != null) { + addCoverSilent(side, coverBehavior); + if (!getLevel().isClientSide) { + // do not sync or handle logic on client side + coverBehavior.getSyncStorage().markAllDirty(); + if (holder.isConnected(side) && !coverBehavior.canPipePassThrough()) { + PipeBlock.disconnectTile(holder, holder.getPipeNeighbor(side, true), side); + } + } + + holder.notifyBlockUpdate(); + holder.markAsDirty(); + } + } + + @Override + public boolean removeCover(boolean dropItself, Direction side, @Nullable Player player) { + CoverBehavior cover = getCoverAtSide(side); + if (cover == null) return ICoverable.super.removeCover(dropItself, side, player); + + holder.notifyBlockUpdate(); + holder.markAsDirty(); + return ICoverable.super.removeCover(side, player); + } + + @Override + public void onLoad() { + ICoverable.super.onLoad(); + for (Direction side : GTUtil.DIRECTIONS) { + this.sidedRedstoneInput[side.get3DDataValue()] = GTUtil.getRedstonePower(getLevel(), getPos(), side); + } + } + + @Override + public final int getInputRedstoneSignal(@NotNull Direction side, boolean ignoreCover) { + if (!ignoreCover && getCoverAtSide(side) != null) { + return 0; // covers block input redstone signal for machine + } + return sidedRedstoneInput[side.get3DDataValue()]; + } + + public void updateInputRedstoneSignals() { + for (Direction side : GTUtil.DIRECTIONS) { + int redstoneValue = GTUtil.getRedstonePower(getLevel(), getPos(), side); + int currentValue = sidedRedstoneInput[side.get3DDataValue()]; + if (redstoneValue != currentValue) { + this.sidedRedstoneInput[side.get3DDataValue()] = redstoneValue; + CoverBehavior cover = getCoverAtSide(side); + if (cover != null) { + cover.onRedstoneInputSignalChange(redstoneValue); + } + } + } + } + + @Override + public void notifyBlockUpdate() { + holder.notifyBlockUpdate(); + } + + @Override + public void scheduleRenderUpdate() { + holder.scheduleRenderUpdate(); + } + + @Override + public void scheduleNeighborShapeUpdate() { + holder.scheduleNeighborShapeUpdate(); + } + + @Override + public boolean canPlaceCoverOnSide(CoverDefinition definition, Direction side) { + return holder.canConnectTo(side); + } + + @Override + public double getCoverPlateThickness() { + float thickness = holder.getBlockType().getStructure().getRenderThickness(); + // no cover plate for pipes >= 1 block thick + if (thickness >= 1) return 0; + + // If the available space for the cover is less than the regular cover plate thickness, use that + + // need to divide by 2 because thickness is centered on the block, so the space is half on each side of the pipe + return Math.min(1.0 / 16.0, (1.0 - thickness) / 2); + } + + @Override + public Direction getFrontFacing() { + return null; + } + + @Override + public boolean shouldRenderBackSide() { + return false; + } + + @Override + public IItemHandler getItemHandlerCap(@Nullable Direction side, boolean useCoverCapability) { + return null; + } + + @Override + public IFluidHandler getFluidHandlerCap(@Nullable Direction side, boolean useCoverCapability) { + return null; + } + + public boolean shouldRenderCoverBackSides() { + return false; + } + + public int getPaintingColorForRendering() { + return Long.decode(ConfigHolder.INSTANCE.client.defaultPaintingColor).intValue(); + } + + public boolean canConnectRedstone(@Nullable Direction side) { + // so far null side means either upwards or downwards redstone wire connection + // so check both top cover and bottom cover + if (side == null) { + return canConnectRedstone(Direction.UP) || + canConnectRedstone(Direction.DOWN); + } + CoverBehavior cover = getCoverAtSide(side); + return cover != null && cover.canConnectRedstone(); + } + + public int getOutputRedstoneSignal(@Nullable Direction side) { + if (side == null) { + return getHighestOutputRedstoneSignal(); + } + CoverBehavior cover = getCoverAtSide(side); + return cover == null ? 0 : cover.getRedstoneSignalOutput(); + } + + public int getHighestOutputRedstoneSignal() { + int highestSignal = 0; + for (Direction side : GTUtil.DIRECTIONS) { + CoverBehavior cover = getCoverAtSide(side); + if (cover == null) continue; + highestSignal = Math.max(highestSignal, cover.getRedstoneSignalOutput()); + } + return highestSignal; + } + + @Override + public Level getLevel() { + return holder.getLevel(); + } + + @Override + public BlockPos getPos() { + return holder.getBlockPos(); + } + + @Override + public @Nullable BlockEntity getNeighbor(@NotNull Direction side) { + return holder.getNeighbor(side); + } + + @Override + public long getOffsetTimer() { + return holder.getOffsetTimer(); + } + + @Nullable + @Override + public CoverBehavior getCoverAtSide(@NotNull Direction side) { + return covers.get(side); + } + + @Override + public boolean hasAnyCover() { + return !covers.isEmpty(); + } + + @Override + public void markDirty() { + holder.markAsDirty(); + } + + @Override + public boolean isInValid() { + return holder.isRemoved(); + } + + @Override + public @NotNull LazyOptional getCapability(@NotNull Capability capability, Direction side) { + return holder.getCapabilityCoverQuery(capability, side); + } + + @OnlyIn(Dist.CLIENT) + public CoverRendererPackage createPackage() { + if (covers.isEmpty()) return CoverRendererPackage.EMPTY; + CoverRendererPackage rendererPackage = new CoverRendererPackage(shouldRenderCoverBackSides()); + for (var cover : covers.entrySet()) { + rendererPackage.addRenderer(cover.getValue().getRenderer(), cover.getKey()); + } + return rendererPackage; + } + + @Override + public @Nullable TickableSubscription subscribeServerTick(Runnable runnable) { + return holder.subscribeServerTick(runnable); + } + + @Override + public void unsubscribe(@Nullable TickableSubscription current) { + holder.unsubscribe(current); + } + + @Override + public ManagedFieldHolder getFieldHolder() { + return MANAGED_FIELD_HOLDER; + } + + @Override + public void onChanged() { + holder.onChanged(); + } + + @SuppressWarnings("unused") + private boolean onCoversDirty(EnumMap covers) { + for (CoverBehavior coverBehavior : covers.values()) { + if (coverBehavior != null) { + for (IRef ref : coverBehavior.getSyncStorage().getNonLazyFields()) { + ref.update(); + } + if (coverBehavior.getSyncStorage().hasDirtySyncFields() || + coverBehavior.getSyncStorage().hasDirtyPersistedFields()) { + return true; + } + } + } + return false; + } + + @SuppressWarnings("unused") + private CompoundTag serializeCovers(EnumMap covers) { + CompoundTag tagCompound = new CompoundTag(); + ListTag coversList = new ListTag(); + for (var entry : covers.entrySet()) { + CoverBehavior cover = entry.getValue(); + if (cover != null) { + CompoundTag tag = new CompoundTag(); + ResourceLocation coverId = cover.coverDefinition.getId(); + tag.putString("id", coverId.toString()); + tag.putByte("side", (byte) entry.getKey().get3DDataValue()); + PersistedParser.serializeNBT(tag, cover.getClass(), cover); + coversList.add(tag); + } + } + tagCompound.put("Covers", coversList); + return tagCompound; + } + + @SuppressWarnings("unused") + private EnumMap deserializeCovers(CompoundTag tagCompound) { + EnumMap map = new EnumMap<>(Direction.class); + + ListTag coversList = tagCompound.getList("Covers", Tag.TAG_COMPOUND); + for (int index = 0; index < coversList.size(); index++) { + CompoundTag tag = coversList.getCompound(index); + if (tag.contains("id", Tag.TAG_STRING)) { + Direction coverSide = Direction.from3DDataValue(tag.getByte("Side")); + ResourceLocation coverLocation = new ResourceLocation(tag.getString("CoverId")); + CoverDefinition coverDefinition = GTRegistries.COVERS.get(coverLocation); + if (coverDefinition == null) { + GTCEu.LOGGER.warn("Unable to find CoverDefinition for ResourceLocation {} at position {}", + coverLocation, holder.getBlockPos()); + } else { + CoverBehavior cover = coverDefinition.createCoverBehavior(this, coverSide); + PersistedParser.deserializeNBT(tag, new HashMap<>(), cover.getClass(), cover); + map.put(coverSide, cover); + } + } + } + return map; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/predicate/BlockedPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/predicate/BlockedPredicate.java new file mode 100644 index 0000000000..02f66751a5 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/predicate/BlockedPredicate.java @@ -0,0 +1,51 @@ +package com.gregtechceu.gtceu.api.graphnet.pipenet.predicate; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.graphnet.predicate.EdgePredicate; +import com.gregtechceu.gtceu.api.graphnet.predicate.NetPredicateType; +import com.gregtechceu.gtceu.api.graphnet.predicate.test.IPredicateTestObject; + +import net.minecraft.nbt.ByteTag; + +import org.jetbrains.annotations.NotNull; + +public final class BlockedPredicate extends EdgePredicate { + + private static final BlockedPredicate INSTANCE = new BlockedPredicate(); + + public static final NetPredicateType TYPE = new NetPredicateType<>(GTCEu.MOD_ID, "Blocked", + () -> INSTANCE, INSTANCE); + + @Override + public @NotNull NetPredicateType getType() { + return TYPE; + } + + @Override + public ByteTag serializeNBT() { + return ByteTag.valueOf((byte) 0); + } + + @Override + public void deserializeNBT(ByteTag nbt) {} + + @Override + public boolean andy() { + return true; + } + + @Override + public boolean test(IPredicateTestObject object) { + return false; + } + + @Override + public boolean equals(Object obj) { + return obj instanceof BlockedPredicate; + } + + @Override + public int hashCode() { + return 0; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/predicate/FilterPredicate.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/predicate/FilterPredicate.java new file mode 100644 index 0000000000..212ac04ed5 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/pipenet/predicate/FilterPredicate.java @@ -0,0 +1,77 @@ +package com.gregtechceu.gtceu.api.graphnet.pipenet.predicate; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.cover.filter.Filter; +import com.gregtechceu.gtceu.api.graphnet.predicate.EdgePredicate; +import com.gregtechceu.gtceu.api.graphnet.predicate.NetPredicateType; +import com.gregtechceu.gtceu.api.graphnet.predicate.test.IPredicateTestObject; + +import net.minecraft.nbt.CompoundTag; + +import lombok.Setter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; + +public final class FilterPredicate extends EdgePredicate { + + public static final NetPredicateType TYPE = new NetPredicateType<>(GTCEu.MOD_ID, "Filter", + FilterPredicate::new, new FilterPredicate()); + + @Setter + private @Nullable Filter sourceFilter; + @Setter + private @Nullable Filter targetFilter; + + @Override + public @NotNull NetPredicateType getType() { + return TYPE; + } + + @Override + public CompoundTag serializeNBT() { + CompoundTag tag = new CompoundTag(); + if (sourceFilter != null) tag.put("Source", sourceFilter.saveFilter()); + if (targetFilter != null) tag.put("Target", targetFilter.saveFilter()); + return tag; + } + + @Override + public void deserializeNBT(CompoundTag nbt) { + if (nbt.contains("Source")) { + sourceFilter = Filter.FilterType.makeNew(nbt.getCompound("Source").getString("type")); + sourceFilter.loadFilter(nbt.getCompound("Source")); + } else sourceFilter = null; + if (nbt.contains("Target")) { + targetFilter = Filter.FilterType.makeNew(nbt.getCompound("Target").getString("type")); + targetFilter.loadFilter(nbt.getCompound("Target")); + } else targetFilter = null; + } + + @Override + public boolean andy() { + return true; + } + + @Override + public boolean test(IPredicateTestObject object) { + Object test = object.recombine(); + if (sourceFilter != null && !sourceFilter.testGeneric(test)) return false; + return targetFilter == null || targetFilter.testGeneric(test); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + FilterPredicate predicate = (FilterPredicate) o; + return Objects.equals(sourceFilter, predicate.sourceFilter) && + Objects.equals(targetFilter, predicate.targetFilter); + } + + @Override + public int hashCode() { + return Objects.hash(sourceFilter, targetFilter); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/predicate/EdgePredicate.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/predicate/EdgePredicate.java new file mode 100644 index 0000000000..04038b2a3d --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/predicate/EdgePredicate.java @@ -0,0 +1,48 @@ +package com.gregtechceu.gtceu.api.graphnet.predicate; + +import com.gregtechceu.gtceu.api.graphnet.predicate.test.IPredicateTestObject; + +import net.minecraft.nbt.Tag; +import net.minecraftforge.common.util.INBTSerializable; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Note - all extenders of this class are suggested to be final, in order to avoid unexpected + * {@link #union(EdgePredicate)} behavior. + */ +public abstract class EdgePredicate, N extends Tag> + implements INBTSerializable { + + public abstract @NotNull NetPredicateType getType(); + + public void deserializeNBTNaive(Tag nbt) { + deserializeNBT((N) nbt); + } + + /** + * Whether this predicate should behave in "and" fashion with other predicates.
+ *
+ * For example, if a predicate handler has 2 and-y predicates and 3 or-y predicates, + * the effective result of evaluation will be:
+ * (andy1) && (andy2) && (ory1 || ory2 || ory3) + */ + public abstract boolean andy(); + + public abstract boolean test(IPredicateTestObject object); + + /** + * Returns null if the operation is not supported. + */ + @Nullable + public T union(EdgePredicate other) { + return null; + } + + /** + * {@link Object#equals(Object)} should always have a good implementation for the sake of sameness checks. + */ + @Override + public abstract boolean equals(Object obj); +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/predicate/EdgePredicateHandler.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/predicate/EdgePredicateHandler.java new file mode 100644 index 0000000000..c4b92dd10d --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/predicate/EdgePredicateHandler.java @@ -0,0 +1,111 @@ +package com.gregtechceu.gtceu.api.graphnet.predicate; + +import com.gregtechceu.gtceu.api.graphnet.predicate.test.IPredicateTestObject; + +import com.lowdragmc.lowdraglib.syncdata.ITagSerializable; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; +import java.util.function.Predicate; + +public final class EdgePredicateHandler implements ITagSerializable, Predicate { + + private final Map, EdgePredicate> predicateSet; + + public EdgePredicateHandler() { + predicateSet = new Object2ObjectOpenHashMap<>(); + } + + /** + * If the {@link EdgePredicate#union(EdgePredicate)} operation is not supported for this predicate, + * nothing happens if a predicate is already present. + */ + public EdgePredicateHandler mergePredicate(@NotNull EdgePredicate predicate) { + EdgePredicate current = predicateSet.get(predicate.getType()); + if (current == null) return setPredicate(predicate); + + if (predicate.getClass().isInstance(current)) { + predicate = current.union(predicate); + if (predicate == null) return this; + } + return setPredicate(predicate); + } + + /** + * Do not modify the returned value + */ + public Map, EdgePredicate> getPredicateSet() { + return predicateSet; + } + + public EdgePredicateHandler setPredicate(@NotNull EdgePredicate predicate) { + predicateSet.put(predicate.getType(), predicate); + return this; + } + + public EdgePredicateHandler removePredicate(@NotNull EdgePredicate predicate) { + return removePredicate(predicate.getType()); + } + + public EdgePredicateHandler removePredicate(@NotNull NetPredicateType type) { + predicateSet.remove(type); + return this; + } + + public boolean hasPredicate(@NotNull EdgePredicate predicate) { + return hasPredicate(predicate.getType()); + } + + public boolean hasPredicate(@NotNull NetPredicateType type) { + return predicateSet.containsKey(type); + } + + public void clearPredicates() { + this.predicateSet.clear(); + } + + public boolean shouldIgnore() { + return predicateSet.isEmpty(); + } + + @Override + public boolean test(IPredicateTestObject iPredicateTestObject) { + if (shouldIgnore()) return true; + boolean result = false; + for (EdgePredicate predicate : predicateSet.values()) { + // TODO predicate 'categories' or 'affinities' that determine order of operations with and-y and or-y + // behavior? + boolean test = predicate.test(iPredicateTestObject); + if (predicate.andy() && !test) return false; + else result |= test; + } + return result; + } + + @Override + public ListTag serializeNBT() { + ListTag list = new ListTag(); + for (EdgePredicate entry : predicateSet.values()) { + CompoundTag tag = new CompoundTag(); + tag.put("Tag", entry.serializeNBT()); + tag.putString("Type", entry.getType().getSerializedName()); + list.add(tag); + } + return list; + } + + @Override + public void deserializeNBT(ListTag nbt) { + for (int i = 0; i < nbt.size(); i++) { + CompoundTag tag = nbt.getCompound(i); + NetPredicateType type = NetPredicateRegistry.getType(tag.getString("Type")); + EdgePredicate entry = this.predicateSet.computeIfAbsent(type, NetPredicateType::getNew); + entry.deserializeNBTNaive(tag.get("Tag")); + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/predicate/NetPredicateRegistrationEvent.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/predicate/NetPredicateRegistrationEvent.java new file mode 100644 index 0000000000..3cda89a254 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/predicate/NetPredicateRegistrationEvent.java @@ -0,0 +1,23 @@ +package com.gregtechceu.gtceu.api.graphnet.predicate; + +import net.minecraftforge.eventbus.api.Event; +import net.minecraftforge.fml.event.IModBusEvent; + +import it.unimi.dsi.fastutil.objects.ObjectRBTreeSet; + +import java.util.Comparator; + +public final class NetPredicateRegistrationEvent extends Event implements IModBusEvent { + + private final ObjectRBTreeSet> gather = new ObjectRBTreeSet<>( + Comparator.comparing(NetPredicateType::getSerializedName)); + + public void accept(NetPredicateType type) { + if (!gather.add(type)) + throw new IllegalStateException("Detected a name collision during Net Predicate registration!"); + } + + ObjectRBTreeSet> getGather() { + return gather; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/predicate/NetPredicateRegistry.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/predicate/NetPredicateRegistry.java new file mode 100644 index 0000000000..ce4b3e50db --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/predicate/NetPredicateRegistry.java @@ -0,0 +1,90 @@ +package com.gregtechceu.gtceu.api.graphnet.predicate; + +import com.lowdragmc.lowdraglib.LDLib; + +import net.minecraft.client.Minecraft; +import net.minecraft.network.chat.Component; +import net.minecraftforge.common.MinecraftForge; + +import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Set; + +public final class NetPredicateRegistry { + + private static final Int2ObjectArrayMap> REGISTRY; + + private static final Object2IntOpenHashMap NAMES_TO_NETWORK_IDS; + + static { + NetPredicateRegistrationEvent event = new NetPredicateRegistrationEvent(); + MinecraftForge.EVENT_BUS.post(event); + Set> gather = event.getGather(); + NAMES_TO_NETWORK_IDS = new Object2IntOpenHashMap<>(gather.size()); + REGISTRY = new Int2ObjectArrayMap<>(gather.size()); + int id = 1; + for (NetPredicateType type : gather) { + NAMES_TO_NETWORK_IDS.put(type.getSerializedName(), id); + REGISTRY.put(id, type); + id++; + } + } + + public static String getName(int networkID) { + return REGISTRY.get(networkID).getSerializedName(); + } + + public static int getNetworkID(@NotNull String name) { + return NAMES_TO_NETWORK_IDS.getInt(name); + } + + public static int getNetworkID(@NotNull NetPredicateType type) { + return getNetworkID(type.getSerializedName()); + } + + public static int getNetworkID(@NotNull EdgePredicate entry) { + return getNetworkID(entry.getType()); + } + + public static @Nullable NetPredicateType getTypeNullable(int networkID) { + return REGISTRY.get(networkID); + } + + public static @Nullable NetPredicateType getTypeNullable(@NotNull String name) { + return getTypeNullable(getNetworkID(name)); + } + + public static @NotNull NetPredicateType getType(int networkID) { + NetPredicateType type = REGISTRY.get(networkID); + if (type == null) throwNonexistenceError(); + assert type != null; + return type; + } + + public static @NotNull NetPredicateType getType(@NotNull String name) { + return getType(getNetworkID(name)); + } + + public static void throwNonexistenceError() { + if (LDLib.isRemote()) disconnect(); + throw new RuntimeException("Could not find the type of an encoded EdgePredicate. " + + "This suggests that the server and client have different GT versions or modifications."); + } + + public static void throwDecodingError() { + if (LDLib.isRemote()) disconnect(); + throw new RuntimeException("Failed to decode an encoded EdgePredicate. " + + "This suggests that the server and client have different GT versions or modifications."); + } + + private static void disconnect() { + if (Minecraft.getInstance().getConnection() != null) + Minecraft.getInstance().getConnection() + .onDisconnect(Component.translatable("gtceu.universal.net_predicate_disconnect")); + } + + private NetPredicateRegistry() {} +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/predicate/NetPredicateType.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/predicate/NetPredicateType.java new file mode 100644 index 0000000000..ad13c66786 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/predicate/NetPredicateType.java @@ -0,0 +1,47 @@ +package com.gregtechceu.gtceu.api.graphnet.predicate; + +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.StringRepresentable; + +import org.jetbrains.annotations.NotNull; + +import java.util.function.Supplier; + +public class NetPredicateType> implements StringRepresentable { + + private final String name; + private final @NotNull Supplier<@NotNull T> supplier; + private final @NotNull T defaultable; + + public NetPredicateType(@NotNull ResourceLocation name, @NotNull Supplier<@NotNull T> supplier, + @NotNull T defaultable) { + this.name = name.toString(); + this.supplier = supplier; + this.defaultable = defaultable; + } + + public NetPredicateType(@NotNull String namespace, @NotNull String name, @NotNull Supplier<@NotNull T> supplier, + @NotNull T defaultable) { + this.name = namespace + ":" + name; + this.supplier = supplier; + this.defaultable = defaultable; + } + + @SuppressWarnings("unchecked") + public T cast(EdgePredicate predicate) { + return (T) predicate; + } + + public final @NotNull T getNew() { + return supplier.get(); + } + + public final @NotNull T getDefault() { + return defaultable; + } + + @Override + public @NotNull String getSerializedName() { + return name; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/predicate/test/FluidTestObject.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/predicate/test/FluidTestObject.java new file mode 100644 index 0000000000..b39e25033e --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/predicate/test/FluidTestObject.java @@ -0,0 +1,55 @@ +package com.gregtechceu.gtceu.api.graphnet.predicate.test; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.level.material.Fluid; +import net.minecraftforge.fluids.FluidStack; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; +import java.util.function.Predicate; + +public final class FluidTestObject implements IPredicateTestObject, Predicate { + + public final Fluid fluid; + public final @Nullable CompoundTag tag; + + private final int hash; + + public FluidTestObject(@NotNull FluidStack stack) { + this.fluid = stack.getFluid(); + this.tag = stack.getTag(); + this.hash = Objects.hash(fluid, tag); + } + + @Override + @Contract(" -> new") + public @NotNull FluidStack recombine() { + return new FluidStack(fluid, 1, tag); + } + + @Contract("_ -> new") + public @NotNull FluidStack recombine(int amount) { + return new FluidStack(fluid, amount, tag); + } + + @Override + public boolean test(@Nullable FluidStack stack) { + return stack != null && stack.getFluid() == fluid && Objects.equals(tag, stack.getTag()); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + FluidTestObject that = (FluidTestObject) o; + return Objects.equals(fluid, that.fluid) && Objects.equals(tag, that.tag); + } + + @Override + public int hashCode() { + return hash; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/predicate/test/IPredicateTestObject.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/predicate/test/IPredicateTestObject.java new file mode 100644 index 0000000000..ad1ae98835 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/predicate/test/IPredicateTestObject.java @@ -0,0 +1,13 @@ +package com.gregtechceu.gtceu.api.graphnet.predicate.test; + +import org.jetbrains.annotations.UnknownNullability; + +public interface IPredicateTestObject { + + IPredicateTestObject INSTANCE = new IPredicateTestObject() {}; + + @UnknownNullability + default Object recombine() { + return null; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/predicate/test/ItemTestObject.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/predicate/test/ItemTestObject.java new file mode 100644 index 0000000000..de5c9f0d45 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/predicate/test/ItemTestObject.java @@ -0,0 +1,76 @@ +package com.gregtechceu.gtceu.api.graphnet.predicate.test; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; +import java.util.function.Predicate; + +public class ItemTestObject implements IPredicateTestObject, Predicate { + + public final Item item; + public final @Nullable CompoundTag tag; + + public final int stackLimit; + + private final int hash; + + public ItemTestObject(@NotNull ItemStack stack) { + item = stack.getItem(); + tag = stack.getTag(); + stackLimit = stack.getMaxStackSize(); + this.hash = Objects.hash(item, tag); + } + + @Override + @Contract(" -> new") + public ItemStack recombine() { + ItemStack stack = new ItemStack(item, 1); + stack.setTag(tag == null ? null : tag.copy()); + return stack; + } + + @Contract("_ -> new") + public ItemStack recombineSafe(int amount) { + return recombine(Math.min(getStackLimit(), Math.max(0, amount))); + } + + @Contract("_ -> new") + public ItemStack recombine(int amount) { + assert amount <= getStackLimit() && amount > 0; + ItemStack stack = new ItemStack(item, amount); + stack.setTag(tag == null ? null : tag.copy()); + return stack; + } + + @Override + public boolean test(@NotNull ItemStack stack) { + if (this.stackLimit == stack.getMaxStackSize() && this.item == stack.getItem()) { + CompoundTag other = stack.getTag(); + return Objects.equals(this.tag, other); + } + return false; + } + + public int getStackLimit() { + return stackLimit; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ItemTestObject that = (ItemTestObject) o; + return Objects.equals(item, that.item) && Objects.equals(tag, that.tag); + } + + @Override + public int hashCode() { + return hash; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/traverse/EdgeDirection.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/traverse/EdgeDirection.java new file mode 100644 index 0000000000..846021e384 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/traverse/EdgeDirection.java @@ -0,0 +1,30 @@ +package com.gregtechceu.gtceu.api.graphnet.traverse; + +import org.jgrapht.Graph; + +import java.util.Set; + +public enum EdgeDirection implements EdgeSelector { + + OUTGOING, + INCOMING, + ALL; + + @Override + public Set selectEdges(Graph graph, V vertex) { + return switch (this) { + case ALL -> graph.edgesOf(vertex); + case INCOMING -> graph.incomingEdgesOf(vertex); + case OUTGOING -> graph.outgoingEdgesOf(vertex); + }; + } + + @Override + public Set selectReversedEdges(Graph graph, V vertex) { + return switch (this) { + case ALL -> graph.edgesOf(vertex); + case OUTGOING -> graph.incomingEdgesOf(vertex); + case INCOMING -> graph.outgoingEdgesOf(vertex); + }; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/traverse/EdgeSelector.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/traverse/EdgeSelector.java new file mode 100644 index 0000000000..643c2a1b63 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/traverse/EdgeSelector.java @@ -0,0 +1,33 @@ +package com.gregtechceu.gtceu.api.graphnet.traverse; + +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import org.jgrapht.Graph; + +import java.util.Set; +import java.util.function.Predicate; + +public interface EdgeSelector { + + Set selectEdges(Graph graph, V vertex); + + Set selectReversedEdges(Graph graph, V vertex); + + static EdgeSelector filtered(EdgeSelector basis, Predicate blacklist) { + return new EdgeSelector() { + + @Override + public Set selectEdges(Graph graph, V vertex) { + Set set = new ObjectOpenHashSet<>(basis.selectEdges(graph, vertex)); + set.removeIf(blacklist); + return set; + } + + @Override + public Set selectReversedEdges(Graph graph, V vertex) { + Set set = new ObjectOpenHashSet<>(basis.selectReversedEdges(graph, vertex)); + set.removeIf(blacklist); + return set; + } + }; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/traverse/NetBreadthIterator.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/traverse/NetBreadthIterator.java new file mode 100644 index 0000000000..f3aa19ec3c --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/traverse/NetBreadthIterator.java @@ -0,0 +1,87 @@ +package com.gregtechceu.gtceu.api.graphnet.traverse; + +import com.gregtechceu.gtceu.api.graphnet.graph.GraphEdge; +import com.gregtechceu.gtceu.api.graphnet.graph.GraphVertex; +import com.gregtechceu.gtceu.api.graphnet.net.IGraphNet; +import com.gregtechceu.gtceu.api.graphnet.net.NetEdge; +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jgrapht.Graph; +import org.jgrapht.traverse.BreadthFirstIterator; + +import java.util.Set; + +public class NetBreadthIterator implements NetIterator { + + protected Iter backer; + + /** + * Creates a breadth-first iterator that traverses a connected component, starting at the given node. + * + * @param origin the node to start at + */ + public NetBreadthIterator(@NotNull NetNode origin, @NotNull EdgeSelector selector) { + this.backer = new Iter(origin.getNet().getGraph(), origin.wrapper, selector); + } + + /** + * Creates a breadth-first iterator that traverses the entire graph, starting at an arbitrary point. + * + * @param graphNet the graph to traverse. + */ + public NetBreadthIterator(@NotNull IGraphNet graphNet) { + this.backer = new Iter(graphNet.getGraph(), (GraphVertex) null, EdgeDirection.ALL); + } + + public BreadthFirstIterator getBacker() { + return backer; + } + + @Override + public boolean hasNext() { + return backer.hasNext(); + } + + @Override + public NetNode next() { + return backer.next().getWrapped(); + } + + public @Nullable NetNode getParent(@NotNull NetNode node) { + return backer.getParent(node.wrapper).getWrapped(); + } + + public boolean hasSeen(@NotNull NetNode node) { + return backer.hasSeen(node.wrapper); + } + + public @Nullable NetEdge getSpanningTreeEdge(@NotNull NetNode node) { + if (!backer.hasSeen(node.wrapper)) return null; + return NetEdge.unwrap(backer.getSpanningTreeEdge(node.wrapper)); + } + + public int getDepth(@NotNull NetNode node) { + return backer.getDepth(node.wrapper); + } + + protected static final class Iter extends BreadthFirstIterator { + + private final EdgeSelector selector; + + public Iter(Graph g, GraphVertex startVertex, EdgeSelector selector) { + super(g, startVertex); + this.selector = selector; + } + + @Override + protected Set selectOutgoingEdges(GraphVertex vertex) { + return selector.selectEdges(graph, vertex); + } + + public boolean hasSeen(GraphVertex vertex) { + return getSeenData(vertex) != null; + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/traverse/NetClosestIterator.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/traverse/NetClosestIterator.java new file mode 100644 index 0000000000..43c385ebb9 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/traverse/NetClosestIterator.java @@ -0,0 +1,73 @@ +package com.gregtechceu.gtceu.api.graphnet.traverse; + +import com.gregtechceu.gtceu.api.graphnet.graph.GraphEdge; +import com.gregtechceu.gtceu.api.graphnet.graph.GraphVertex; +import com.gregtechceu.gtceu.api.graphnet.net.NetEdge; +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jgrapht.Graph; +import org.jgrapht.traverse.ClosestFirstIterator; + +import java.util.Set; + +public class NetClosestIterator implements NetIterator { + + protected final Iter backer; + + /** + * Creates a closest-first iterator that traverses a connected component, starting at the given node. + * + * @param origin the node to start at + */ + public NetClosestIterator(@NotNull NetNode origin, EdgeSelector selector) { + this.backer = new Iter(origin.getNet().getGraph(), origin.wrapper, selector); + } + + public ClosestFirstIterator getBacker() { + return backer; + } + + @Override + public boolean hasNext() { + return backer.hasNext(); + } + + @Override + public NetNode next() { + return backer.next().getWrapped(); + } + + public double getShortestPathLength(@NotNull NetNode node) { + return backer.getShortestPathLength(node.wrapper); + } + + public boolean hasSeen(@NotNull NetNode node) { + return backer.hasSeen(node.wrapper); + } + + public @Nullable NetEdge getSpanningTreeEdge(@NotNull NetNode node) { + if (!backer.hasSeen(node.wrapper)) return null; + return NetEdge.unwrap(backer.getSpanningTreeEdge(node.wrapper)); + } + + protected static final class Iter extends ClosestFirstIterator { + + private final EdgeSelector selector; + + public Iter(Graph g, GraphVertex startVertex, EdgeSelector selector) { + super(g, startVertex); + this.selector = selector; + } + + @Override + protected Set selectOutgoingEdges(GraphVertex vertex) { + return selector.selectEdges(graph, vertex); + } + + public boolean hasSeen(GraphVertex vertex) { + return getSeenData(vertex) != null; + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/traverse/NetIterator.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/traverse/NetIterator.java new file mode 100644 index 0000000000..a0a36189f3 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/traverse/NetIterator.java @@ -0,0 +1,25 @@ +package com.gregtechceu.gtceu.api.graphnet.traverse; + +import com.gregtechceu.gtceu.api.graphnet.net.NetEdge; +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Iterator; + +public interface NetIterator extends Iterator { + + /** + * @param node the node in question. + * @return Whether this iterator has encountered the node in question. + */ + boolean hasSeen(@NotNull NetNode node); + + /** + * @param node the node in question. + * @return the next edge along the lowest weight path to the origin node for the node in question. + */ + @Nullable + NetEdge getSpanningTreeEdge(@NotNull NetNode node); +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/traverse/NetIteratorSupplier.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/traverse/NetIteratorSupplier.java new file mode 100644 index 0000000000..a3bc3985dc --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/traverse/NetIteratorSupplier.java @@ -0,0 +1,12 @@ +package com.gregtechceu.gtceu.api.graphnet.traverse; + +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; + +import org.jetbrains.annotations.NotNull; + +@FunctionalInterface +public interface NetIteratorSupplier { + + @NotNull + NetIterator create(@NotNull NetNode origin, @NotNull EdgeSelector direction); +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/graphnet/traverse/ResilientNetClosestIterator.java b/src/main/java/com/gregtechceu/gtceu/api/graphnet/traverse/ResilientNetClosestIterator.java new file mode 100644 index 0000000000..842966f40a --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/graphnet/traverse/ResilientNetClosestIterator.java @@ -0,0 +1,252 @@ +package com.gregtechceu.gtceu.api.graphnet.traverse; + +import com.gregtechceu.gtceu.api.graphnet.graph.GraphEdge; +import com.gregtechceu.gtceu.api.graphnet.graph.GraphVertex; +import com.gregtechceu.gtceu.api.graphnet.net.NetEdge; +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jgrapht.Graph; +import org.jgrapht.traverse.ClosestFirstIterator; +import org.jgrapht.traverse.CrossComponentIterator; +import org.jheaps.AddressableHeap; +import org.jheaps.tree.PairingHeap; + +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.function.Consumer; + +/** + * A specialized net closest iterator that allows nodes to be marked invalid for iteration retroactively, + * without requiring a new iterator to be declared, leading to its resilience. + */ +public class ResilientNetClosestIterator implements NetIterator { + + protected final @NotNull InternalIterator internal; + + public ResilientNetClosestIterator(@NotNull NetNode origin, @NotNull EdgeSelector selector) { + internal = new InternalIterator(origin.getNet().getGraph(), origin.wrapper, selector, + origin.getGroupSafe().getNodes().size()); + } + + public ResilientNetClosestIterator(@NotNull NetNode origin, @NotNull EdgeSelector selector, double radius) { + internal = new InternalIterator(origin.getNet().getGraph(), origin.wrapper, selector, + origin.getGroupSafe().getNodes().size(), radius); + } + + /** + * Marks a node as no longer valid for traversal. When applied to a node with children, the entire "branch" of the + * seen "tree" will be cut off, and previous inferior connections to the branch will be considered again, allowing + * further iteration to potentially revisit nodes within the removed "branch". + * + * @param node the node to mark invalid + * @return this object, for convenience. + */ + @Contract("_->this") + public ResilientNetClosestIterator markInvalid(@NotNull NetNode node) { + if (node.wrapper != null) internal.invalidate(node.wrapper); + return this; + } + + @Override + public boolean hasSeen(@NotNull NetNode node) { + return internal.seen.containsKey(node.wrapper); + } + + @Override + public @Nullable NetEdge getSpanningTreeEdge(@NotNull NetNode node) { + return NetEdge.unwrap(internal.getSpanningTreeEdge(node.wrapper)); + } + + @Override + public boolean hasNext() { + return internal.hasNext(); + } + + @Override + public NetNode next() { + return internal.next().getWrapped(); + } + + /** + * See {@link ClosestFirstIterator} and {@link CrossComponentIterator} + */ + protected static final class InternalIterator implements Iterator { + + public final Graph graph; + + public final EdgeSelector selector; + + public final AddressableHeap heap; + + public final Map> seen; + + public final double radius; + + public final Set invalidated = new ObjectOpenHashSet<>(); + + public InternalIterator(Graph graph, GraphVertex startVertex, EdgeSelector selector, + int expectedSize) { + this(graph, startVertex, selector, expectedSize, Double.POSITIVE_INFINITY); + } + + public InternalIterator(Graph graph, GraphVertex startVertex, EdgeSelector selector, + int expectedSize, double radius) { + this.graph = graph; + this.selector = selector; + this.radius = radius; + this.heap = new PairingHeap<>(); + this.seen = new Object2ObjectOpenHashMap<>(expectedSize); + encounterVertexFirst(startVertex, null); + } + + @Override + public boolean hasNext() { + return !isConnectedComponentExhausted(); + } + + @Override + public GraphVertex next() { + if (hasNext()) { + AddressableHeap.Handle node; + do { + node = heap.deleteMin(); + node.getValue().foundMinimum = true; + } while (node.getValue().outdated && hasNext()); + + addUnseenChildrenOf(node.getValue().vertex); + return node.getValue().vertex; + } else { + throw new NoSuchElementException(); + } + } + + private void addUnseenChildrenOf(GraphVertex vertex) { + for (GraphEdge edge : selector.selectEdges(graph, vertex)) { + + GraphVertex oppositeV = edge.getOppositeVertex(vertex); + encounterVertex(oppositeV, edge); + } + } + + public void invalidate(GraphVertex vertex) { + if (!invalidated.add(vertex)) return; + AddressableHeap.Handle handle = seen.get(vertex); + if (handle != null) { + Set regenerationCandidates = new ObjectOpenHashSet<>(); + handle.getValue().applySelfAndChildren(c -> { + seen.remove(c.vertex); + c.outdated = true; + regenerationCandidates.addAll(selector.selectReversedEdges(graph, c.vertex)); + }); + for (GraphEdge candidate : regenerationCandidates) { + if (seen.containsKey(candidate.getSource())) { + encounterVertex(candidate.getTarget(), candidate); + } else if (seen.containsKey(candidate.getTarget())) { + encounterVertex(candidate.getSource(), candidate); + } + } + } + } + + public GraphEdge getSpanningTreeEdge(GraphVertex vertex) { + AddressableHeap.Handle node = seen.get(vertex); + return node == null ? null : node.getValue().spanningTreeEdge; + } + + private boolean isConnectedComponentExhausted() { + if (heap.size() == 0) { + return true; + } else { + if (heap.findMin().getKey() > radius) { + heap.clear(); + return true; + } else { + return false; + } + } + } + + private void encounterVertex(GraphVertex vertex, GraphEdge edge) { + if (invalidated.contains(vertex)) return; + if (seen.containsKey(vertex)) { + encounterVertexAgain(vertex, edge); + } else { + encounterVertexFirst(vertex, edge); + } + } + + private void encounterVertexFirst(GraphVertex vertex, GraphEdge edge) { + double shortestPathLength; + SeenData data; + if (edge == null) { + shortestPathLength = 0; + data = new SeenData(vertex, null, null); + } else { + GraphVertex otherVertex = edge.getOppositeVertex(vertex); + AddressableHeap.Handle otherEntry = seen.get(otherVertex); + if (otherEntry == null) return; + shortestPathLength = otherEntry.getKey() + graph.getEdgeWeight(edge); + + data = new SeenData(vertex, edge, otherEntry.getValue()); + otherEntry.getValue().spanningChildren.add(data); + } + AddressableHeap.Handle handle = heap.insert(shortestPathLength, data); + seen.put(vertex, handle); + } + + private void encounterVertexAgain(GraphVertex vertex, GraphEdge edge) { + AddressableHeap.Handle node = seen.get(vertex); + + if (node.getValue().foundMinimum) { + // no improvement for this vertex possible + return; + } + GraphVertex otherVertex = edge.getOppositeVertex(vertex); + AddressableHeap.Handle otherEntry = seen.get(otherVertex); + + double candidatePathLength = otherEntry.getKey() + graph.getEdgeWeight(edge); + if (candidatePathLength < node.getKey()) { + node.getValue().parent = otherEntry.getValue(); + node.getValue().spanningTreeEdge = edge; + node.decreaseKey(candidatePathLength); + } + } + } + + protected static final class SeenData { + + public final GraphVertex vertex; + + // the next edge along the lowest weight path to the origin node + public GraphEdge spanningTreeEdge; + + public SeenData parent; + + public boolean foundMinimum = false; + + public boolean outdated = false; + + // all nodes whose spanning tree edges point at this node + public final Set spanningChildren = new ObjectOpenHashSet<>(6); + + public SeenData(GraphVertex vertex, GraphEdge spanningTreeEdge, SeenData parent) { + this.vertex = vertex; + this.spanningTreeEdge = spanningTreeEdge; + this.parent = parent; + } + + public void applySelfAndChildren(Consumer c) { + c.accept(this); + for (SeenData data : spanningChildren) { + data.applySelfAndChildren(c); + } + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/gui/fancy/FancyMachineUIWidget.java b/src/main/java/com/gregtechceu/gtceu/api/gui/fancy/FancyMachineUIWidget.java index fd68a5135b..b12e9da868 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/gui/fancy/FancyMachineUIWidget.java +++ b/src/main/java/com/gregtechceu/gtceu/api/gui/fancy/FancyMachineUIWidget.java @@ -42,11 +42,13 @@ public class FancyMachineUIWidget extends WidgetGroup { protected final IFancyUIProvider mainPage; - /* - * Current Page: The page visible in the UI - * Current Home Page: The currently selected multiblock part's home page. + /** + * The page visible in the UI */ protected IFancyUIProvider currentPage; + /** + * The currently selected multiblock part's home page. + */ protected IFancyUIProvider currentHomePage; protected List allPages; diff --git a/src/main/java/com/gregtechceu/gtceu/api/item/DrumMachineItem.java b/src/main/java/com/gregtechceu/gtceu/api/item/DrumMachineItem.java index 9bdade2ccd..59f5d838bb 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/item/DrumMachineItem.java +++ b/src/main/java/com/gregtechceu/gtceu/api/item/DrumMachineItem.java @@ -2,10 +2,10 @@ import com.gregtechceu.gtceu.api.block.IMachineBlock; import com.gregtechceu.gtceu.api.data.chemical.material.Material; -import com.gregtechceu.gtceu.api.data.chemical.material.properties.FluidPipeProperties; import com.gregtechceu.gtceu.api.data.chemical.material.properties.PropertyKey; import com.gregtechceu.gtceu.api.misc.forge.ThermalFluidHandlerItemStack; import com.gregtechceu.gtceu.common.data.machines.GTMachineUtils; +import com.gregtechceu.gtceu.common.pipelike.handlers.properties.MaterialFluidProperties; import net.minecraft.world.item.ItemStack; import net.minecraftforge.common.capabilities.Capability; @@ -33,9 +33,9 @@ public static DrumMachineItem create(IMachineBlock block, Properties properties, } public @NotNull LazyOptional getCapability(ItemStack itemStack, @NotNull Capability cap) { - FluidPipeProperties property; - if (mat.hasProperty(PropertyKey.FLUID_PIPE)) - property = mat.getProperty(PropertyKey.FLUID_PIPE); + MaterialFluidProperties property; + if (mat.hasProperty(PropertyKey.PIPENET_PROPERTIES)) + property = mat.getProperty(PropertyKey.PIPENET_PROPERTIES).getProperty(MaterialFluidProperties.KEY); else property = null; if (cap == ForgeCapabilities.FLUID_HANDLER_ITEM && property != null) { @@ -43,8 +43,8 @@ public static DrumMachineItem create(IMachineBlock block, Properties properties, () -> new ThermalFluidHandlerItemStack( itemStack, Math.toIntExact(GTMachineUtils.DRUM_CAPACITY.get(getDefinition())), - property.getMaxFluidTemperature(), property.isGasProof(), property.isAcidProof(), - property.isCryoProof(), property.isPlasmaProof()))); + property.getMaxFluidTemperature(), property.getMinFluidTemperature(), + property.isGasProof(), property.isPlasmaProof()))); } return LazyOptional.empty(); } diff --git a/src/main/java/com/gregtechceu/gtceu/api/item/DuctPipeBlockItem.java b/src/main/java/com/gregtechceu/gtceu/api/item/DuctPipeBlockItem.java deleted file mode 100644 index 5563d6a409..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/api/item/DuctPipeBlockItem.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.gregtechceu.gtceu.api.item; - -import com.gregtechceu.gtceu.common.block.DuctPipeBlock; - -import com.lowdragmc.lowdraglib.client.renderer.IItemRendererProvider; -import com.lowdragmc.lowdraglib.client.renderer.IRenderer; - -import net.minecraft.world.item.ItemStack; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class DuctPipeBlockItem extends PipeBlockItem implements IItemRendererProvider { - - public DuctPipeBlockItem(DuctPipeBlock block, Properties properties) { - super(block, properties); - } - - @Override - @NotNull - public DuctPipeBlock getBlock() { - return (DuctPipeBlock) super.getBlock(); - } - - @Nullable - @Override - public IRenderer getRenderer(ItemStack stack) { - return getBlock().getRenderer(getBlock().defaultBlockState()); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/api/item/LaserPipeBlockItem.java b/src/main/java/com/gregtechceu/gtceu/api/item/LaserPipeBlockItem.java deleted file mode 100644 index dfaef338c1..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/api/item/LaserPipeBlockItem.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.gregtechceu.gtceu.api.item; - -import com.gregtechceu.gtceu.api.block.PipeBlock; -import com.gregtechceu.gtceu.common.block.LaserPipeBlock; - -import com.lowdragmc.lowdraglib.client.renderer.IItemRendererProvider; -import com.lowdragmc.lowdraglib.client.renderer.IRenderer; - -import net.minecraft.client.color.item.ItemColor; -import net.minecraft.world.item.ItemStack; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; - -import org.jetbrains.annotations.Nullable; - -import javax.annotation.ParametersAreNonnullByDefault; - -@ParametersAreNonnullByDefault -public class LaserPipeBlockItem extends PipeBlockItem implements IItemRendererProvider { - - public LaserPipeBlockItem(PipeBlock block, Properties properties) { - super(block, properties); - } - - @Override - public LaserPipeBlock getBlock() { - return (LaserPipeBlock) super.getBlock(); - } - - @OnlyIn(Dist.CLIENT) - public static ItemColor tintColor() { - return (itemStack, index) -> { - if (itemStack.getItem() instanceof LaserPipeBlockItem materialBlockItem) { - return LaserPipeBlock.tintedColor().getColor(materialBlockItem.getBlock().defaultBlockState(), null, - null, index); - } - return -1; - }; - } - - @Nullable - @Override - @OnlyIn(Dist.CLIENT) - public IRenderer getRenderer(ItemStack stack) { - return getBlock().getRenderer(getBlock().defaultBlockState()); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/api/item/MaterialPipeBlockItem.java b/src/main/java/com/gregtechceu/gtceu/api/item/MaterialPipeBlockItem.java deleted file mode 100644 index 52ca3f5ecb..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/api/item/MaterialPipeBlockItem.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.gregtechceu.gtceu.api.item; - -import com.gregtechceu.gtceu.api.block.MaterialPipeBlock; - -import com.lowdragmc.lowdraglib.client.renderer.IItemRendererProvider; -import com.lowdragmc.lowdraglib.client.renderer.IRenderer; - -import net.minecraft.MethodsReturnNonnullByDefault; -import net.minecraft.client.color.item.ItemColor; -import net.minecraft.network.chat.Component; -import net.minecraft.world.item.ItemStack; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.annotation.ParametersAreNonnullByDefault; - -/** - * @author KilaBash - * @implNote MaterialBlockItem - */ -@MethodsReturnNonnullByDefault -@ParametersAreNonnullByDefault -public class MaterialPipeBlockItem extends PipeBlockItem implements IItemRendererProvider { - - public MaterialPipeBlockItem(MaterialPipeBlock block, Properties properties) { - super(block, properties); - } - - @Override - @NotNull - public MaterialPipeBlock getBlock() { - return (MaterialPipeBlock) super.getBlock(); - } - - @OnlyIn(Dist.CLIENT) - public static ItemColor tintColor() { - return (itemStack, index) -> { - if (itemStack.getItem() instanceof MaterialPipeBlockItem materialBlockItem) { - return materialBlockItem.getBlock().tinted(materialBlockItem.getBlock().defaultBlockState(), null, null, - index); - } - return -1; - }; - } - - @Nullable - @Override - @OnlyIn(Dist.CLIENT) - public IRenderer getRenderer(ItemStack stack) { - return getBlock().getRenderer(getBlock().defaultBlockState()); - } - - @Override - public Component getDescription() { - return this.getBlock().getName(); - } - - @Override - public Component getName(ItemStack stack) { - return getDescription(); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/api/item/MetaMachineItem.java b/src/main/java/com/gregtechceu/gtceu/api/item/MetaMachineItem.java index 6e83030b51..5459b65d29 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/item/MetaMachineItem.java +++ b/src/main/java/com/gregtechceu/gtceu/api/item/MetaMachineItem.java @@ -1,9 +1,9 @@ package com.gregtechceu.gtceu.api.item; import com.gregtechceu.gtceu.api.block.IMachineBlock; -import com.gregtechceu.gtceu.api.block.PipeBlock; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.block.PipeBlock; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; import com.gregtechceu.gtceu.api.machine.MachineDefinition; -import com.gregtechceu.gtceu.api.pipenet.IPipeNode; import com.lowdragmc.lowdraglib.client.renderer.IItemRendererProvider; import com.lowdragmc.lowdraglib.client.renderer.IRenderer; @@ -46,7 +46,6 @@ public IRenderer getRenderer(ItemStack stack) { } @Override - @SuppressWarnings({ "rawtypes", "unchecked" }) protected boolean placeBlock(BlockPlaceContext context, BlockState state) { Level level = context.getLevel(); BlockPos pos = context.getClickedPos(); @@ -55,13 +54,12 @@ protected boolean placeBlock(BlockPlaceContext context, BlockState state) { boolean superVal = super.placeBlock(context, state); if (!level.isClientSide) { - BlockPos possiblePipe = pos.offset(side.getOpposite().getNormal()); + BlockPos possiblePipe = pos.relative(side.getOpposite()); Block block = level.getBlockState(possiblePipe).getBlock(); - if (block instanceof PipeBlock) { - IPipeNode pipeTile = ((PipeBlock) block).getPipeTile(level, possiblePipe); - if (pipeTile != null && ((PipeBlock) block).canPipeConnectToBlock(pipeTile, side.getOpposite(), - level.getBlockEntity(pos))) { - pipeTile.setConnection(side, true, false); + if (block instanceof PipeBlock pipeBlock) { + PipeBlockEntity pipeTile = pipeBlock.getBlockEntity(level, possiblePipe); + if (pipeTile != null && pipeTile.canConnectTo(side.getOpposite())) { + pipeTile.updateActiveStatus(side.getOpposite(), true); } } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/item/OpticalPipeBlockItem.java b/src/main/java/com/gregtechceu/gtceu/api/item/OpticalPipeBlockItem.java deleted file mode 100644 index ed59e05ded..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/api/item/OpticalPipeBlockItem.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.gregtechceu.gtceu.api.item; - -import com.gregtechceu.gtceu.common.block.OpticalPipeBlock; - -import com.lowdragmc.lowdraglib.client.renderer.IItemRendererProvider; -import com.lowdragmc.lowdraglib.client.renderer.IRenderer; - -import net.minecraft.world.item.ItemStack; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; - -import org.jetbrains.annotations.Nullable; - -import javax.annotation.ParametersAreNonnullByDefault; - -@ParametersAreNonnullByDefault -public class OpticalPipeBlockItem extends PipeBlockItem implements IItemRendererProvider { - - public OpticalPipeBlockItem(OpticalPipeBlock block, Properties properties) { - super(block, properties); - } - - @Nullable - @Override - @OnlyIn(Dist.CLIENT) - public IRenderer getRenderer(ItemStack stack) { - return getBlock().getRenderer(getBlock().defaultBlockState()); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/api/item/PipeBlockItem.java b/src/main/java/com/gregtechceu/gtceu/api/item/PipeBlockItem.java deleted file mode 100644 index c03cfb6a6a..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/api/item/PipeBlockItem.java +++ /dev/null @@ -1,102 +0,0 @@ -package com.gregtechceu.gtceu.api.item; - -import com.gregtechceu.gtceu.api.block.PipeBlock; -import com.gregtechceu.gtceu.api.capability.ICoverable; -import com.gregtechceu.gtceu.api.pipenet.IPipeNode; -import com.gregtechceu.gtceu.config.ConfigHolder; -import com.gregtechceu.gtceu.utils.GTUtil; - -import net.minecraft.MethodsReturnNonnullByDefault; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.network.chat.Component; -import net.minecraft.world.item.BlockItem; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.TooltipFlag; -import net.minecraft.world.item.context.BlockPlaceContext; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.phys.BlockHitResult; - -import org.jetbrains.annotations.Nullable; - -import java.util.List; - -import javax.annotation.ParametersAreNonnullByDefault; - -/** - * @author KilaBash - * @date 2023/6/23 - * @implNote PipeBlockItem - */ -@MethodsReturnNonnullByDefault -@ParametersAreNonnullByDefault -public class PipeBlockItem extends BlockItem { - - public PipeBlockItem(PipeBlock block, Properties properties) { - super(block, properties); - } - - @Override - public PipeBlock getBlock() { - return (PipeBlock) super.getBlock(); - } - - @Override - public void appendHoverText(ItemStack stack, @Nullable Level level, List tooltip, - TooltipFlag isAdvanced) { - super.appendHoverText(stack, level, tooltip, isAdvanced); - if (GTUtil.isShiftDown()) { - tooltip.add(Component.translatable("gtceu.tool_action.wire_cutter.connect")); - } else { - tooltip.add(Component.translatable("gtceu.tool_action.show_tooltips")); - } - } - - @Override - @SuppressWarnings({ "rawtypes", "unchecked" }) - public boolean placeBlock(BlockPlaceContext context, BlockState state) { - Level level = context.getLevel(); - BlockPos pos = context.getClickedPos(); - Direction side = context.getClickedFace(); - - var realPos = pos.relative(side.getOpposite()); - var baseNode = getBlock().getPipeTile(level, realPos); - if (baseNode != null) { - var sideAttach = ICoverable - .traceCoverSide(new BlockHitResult(context.getClickLocation(), side, realPos, false)); - if (sideAttach != null && context.getLevel().isEmptyBlock(realPos.relative(sideAttach))) { - pos = realPos.relative(sideAttach); - side = sideAttach; - context = new BlockPlaceContext(level, context.getPlayer(), context.getHand(), context.getItemInHand(), - new BlockHitResult(context.getClickLocation(), sideAttach, realPos, false)); - } - } - - boolean superVal = super.placeBlock(context, state); - if (superVal && !level.isClientSide) { - IPipeNode selfTile = getBlock().getPipeTile(level, pos); - if (selfTile == null) return true; - if (selfTile.getPipeBlock().canConnect(selfTile, side.getOpposite())) { - selfTile.setConnection(side.getOpposite(), true, false); - } - for (Direction facing : GTUtil.DIRECTIONS) { - BlockEntity te = selfTile.getNeighbor(facing); - if (te instanceof IPipeNode otherPipe) { - if (otherPipe.isConnected(facing.getOpposite())) { - if (otherPipe.getPipeBlock().canPipesConnect(otherPipe, facing.getOpposite(), selfTile)) { - selfTile.setConnection(facing, true, true); - } else { - otherPipe.setConnection(facing.getOpposite(), false, true); - } - } - } else if (!ConfigHolder.INSTANCE.machines.gt6StylePipesCables && - selfTile.getPipeBlock().canPipeConnectToBlock(selfTile, facing, te)) { - selfTile.setConnection(facing, true, false); - } - } - } - return superVal; - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/api/item/component/ThermalFluidStats.java b/src/main/java/com/gregtechceu/gtceu/api/item/component/ThermalFluidStats.java index 963ebd8d54..9645e3b0ff 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/item/component/ThermalFluidStats.java +++ b/src/main/java/com/gregtechceu/gtceu/api/item/component/ThermalFluidStats.java @@ -1,5 +1,7 @@ package com.gregtechceu.gtceu.api.item.component; +import com.gregtechceu.gtceu.api.fluids.attribute.FluidAttribute; +import com.gregtechceu.gtceu.api.fluids.attribute.FluidAttributes; import com.gregtechceu.gtceu.api.item.component.forge.IComponentCapability; import com.gregtechceu.gtceu.api.misc.forge.SimpleThermalFluidHandlerItemStack; import com.gregtechceu.gtceu.api.misc.forge.ThermalFluidHandlerItemStack; @@ -16,6 +18,8 @@ import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.fluids.FluidUtil; +import it.unimi.dsi.fastutil.objects.Object2BooleanMap; +import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -30,39 +34,50 @@ public class ThermalFluidStats implements IItemComponent, IComponentCapability, public final int capacity; public final int maxFluidTemperature; + public final int minFluidTemperature; public final boolean gasProof; - public final boolean acidProof; - public final boolean cryoProof; public final boolean plasmaProof; public final boolean allowPartialFill; - protected ThermalFluidStats(int capacity, int maxFluidTemperature, boolean gasProof, boolean acidProof, - boolean cryoProof, boolean plasmaProof, boolean allowPartialFill) { + private final Object2BooleanMap containmentPredicate = new Object2BooleanOpenHashMap<>(); + + protected ThermalFluidStats(int capacity, int maxFluidTemperature, int minFluidTemperature, + boolean gasProof, boolean plasmaProof, boolean acidProof, + boolean allowPartialFill) { this.capacity = capacity; this.maxFluidTemperature = maxFluidTemperature; + this.minFluidTemperature = minFluidTemperature; this.gasProof = gasProof; - this.acidProof = acidProof; - this.cryoProof = cryoProof; + if (acidProof) setCanContain(FluidAttributes.ACID, true); this.plasmaProof = plasmaProof; this.allowPartialFill = allowPartialFill; } - public static ThermalFluidStats create(int capacity, int maxFluidTemperature, boolean gasProof, boolean acidProof, - boolean cryoProof, boolean plasmaProof, boolean allowPartialFill) { - return new ThermalFluidStats(capacity, maxFluidTemperature, gasProof, acidProof, cryoProof, plasmaProof, + public static ThermalFluidStats create(int capacity, int maxFluidTemperature, int minFluidTemperature, + boolean gasProof, boolean acidProof, boolean plasmaProof, + boolean allowPartialFill) { + return new ThermalFluidStats(capacity, maxFluidTemperature, minFluidTemperature, + gasProof, acidProof, plasmaProof, allowPartialFill); } + public ThermalFluidStats setCanContain(@NotNull FluidAttribute attribute, boolean canContain) { + containmentPredicate.put(attribute, canContain); + return this; + } + @Override public @NotNull LazyOptional getCapability(ItemStack itemStack, @NotNull Capability cap) { if (cap == ForgeCapabilities.FLUID_HANDLER_ITEM) { return ForgeCapabilities.FLUID_HANDLER_ITEM.orEmpty(cap, LazyOptional.of(() -> { if (allowPartialFill) { - return new ThermalFluidHandlerItemStack(itemStack, capacity, maxFluidTemperature, gasProof, - acidProof, cryoProof, plasmaProof); + return new ThermalFluidHandlerItemStack(itemStack, capacity, + maxFluidTemperature, minFluidTemperature, + gasProof, plasmaProof); } - return new SimpleThermalFluidHandlerItemStack(itemStack, capacity, maxFluidTemperature, gasProof, - acidProof, cryoProof, plasmaProof); + return new SimpleThermalFluidHandlerItemStack(itemStack, capacity, + maxFluidTemperature, minFluidTemperature, + gasProof, plasmaProof); })); } return LazyOptional.empty(); @@ -83,14 +98,13 @@ public void appendHoverText(ItemStack stack, @Nullable Level level, List a.appendContainerTooltips(tooltipComponents::add)); + } else if (gasProof || plasmaProof || !containmentPredicate.isEmpty()) { tooltipComponents.add(Component.translatable("gtceu.tooltip.fluid_pipe_hold_shift")); } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/IMachineBlockEntity.java b/src/main/java/com/gregtechceu/gtceu/api/machine/IMachineBlockEntity.java index 72c583f910..8449fe9bdb 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/IMachineBlockEntity.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/IMachineBlockEntity.java @@ -2,6 +2,7 @@ import com.gregtechceu.gtceu.api.GTValues; import com.gregtechceu.gtceu.api.block.IMachineBlock; +import com.gregtechceu.gtceu.api.blockentity.INeighborCache; import com.gregtechceu.gtceu.api.item.tool.IToolGridHighlight; import com.gregtechceu.gtceu.common.machine.owner.IMachineOwner; @@ -21,7 +22,7 @@ * Also delivers most of the Information about TileEntities. */ public interface IMachineBlockEntity extends IToolGridHighlight, IAsyncAutoSyncBlockEntity, IRPCBlockEntity, - IAutoPersistBlockEntity { + IAutoPersistBlockEntity, INeighborCache { default BlockEntity self() { return (BlockEntity) this; @@ -35,24 +36,6 @@ default BlockPos pos() { return self().getBlockPos(); } - default void notifyBlockUpdate() { - if (level() != null) { - level().updateNeighborsAt(pos(), level().getBlockState(pos()).getBlock()); - } - } - - default void scheduleRenderUpdate() { - var pos = pos(); - if (level() != null) { - var state = level().getBlockState(pos); - if (level().isClientSide) { - level().sendBlockUpdated(pos, state, state, 1 << 3); - } else { - level().blockEvent(pos, state.getBlock(), 1, 0); - } - } - } - default long getOffsetTimer() { if (level() == null) return getOffset(); else if (level().isClientSide()) return GTValues.CLIENT_TIME + getOffset(); diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/MachineCoverContainer.java b/src/main/java/com/gregtechceu/gtceu/api/machine/MachineCoverContainer.java index 12d216c0ef..7695cc5846 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/MachineCoverContainer.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/MachineCoverContainer.java @@ -5,7 +5,6 @@ import com.gregtechceu.gtceu.api.cover.CoverBehavior; import com.gregtechceu.gtceu.api.cover.CoverDefinition; import com.gregtechceu.gtceu.api.registry.GTRegistries; -import com.gregtechceu.gtceu.api.transfer.fluid.IFluidHandlerModifiable; import com.gregtechceu.gtceu.utils.GTUtil; import com.lowdragmc.lowdraglib.syncdata.IEnhancedManaged; @@ -21,11 +20,18 @@ import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.TickTask; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.phys.shapes.VoxelShape; -import net.minecraftforge.items.IItemHandlerModifiable; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.util.LazyOptional; +import net.minecraftforge.fluids.capability.IFluidHandler; +import net.minecraftforge.items.IItemHandler; import lombok.Getter; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; @@ -49,6 +55,8 @@ public class MachineCoverContainer implements ICoverable, IEnhancedManaged { deserializeMethod = "deserializeCoverUid") private CoverBehavior up, down, north, south, west, east; + private final int[] sidedRedstoneInput = new int[6]; + public MachineCoverContainer(MetaMachine machine) { this.machine = machine; } @@ -73,6 +81,19 @@ public void onChanged() { } } + @Override + public void onLoad() { + ICoverable.super.onLoad(); + if (getLevel() instanceof ServerLevel serverLevel) { + serverLevel.getServer().tell(new TickTask(0, () -> { + for (Direction side : GTUtil.DIRECTIONS) { + this.sidedRedstoneInput[side.get3DDataValue()] = GTUtil.getRedstonePower(getLevel(), getPos(), + side); + } + })); + } + } + @Override public Level getLevel() { return machine.getLevel(); @@ -126,6 +147,11 @@ public boolean canPlaceCoverOnSide(CoverDefinition definition, Direction side) { return true; } + @Override + public boolean acceptsCovers() { + return up == null || down == null || north == null || south == null || west == null || east == null; + } + @Override public double getCoverPlateThickness() { return 0; @@ -164,6 +190,33 @@ public CoverBehavior getCoverAtSide(Direction side) { }; } + @Override + public @Nullable BlockEntity getNeighbor(@NotNull Direction side) { + return machine.getNeighbor(side); + } + + @Override + public int getInputRedstoneSignal(@NotNull Direction side, boolean ignoreCover) { + if (!ignoreCover && getCoverAtSide(side) != null) { + return 0; // covers block input redstone signal for machine + } + return sidedRedstoneInput[side.get3DDataValue()]; + } + + public void updateInputRedstoneSignals() { + for (Direction side : GTUtil.DIRECTIONS) { + int redstoneValue = GTUtil.getRedstonePower(getLevel(), getPos(), side); + int currentValue = sidedRedstoneInput[side.get3DDataValue()]; + if (redstoneValue != currentValue) { + this.sidedRedstoneInput[side.get3DDataValue()] = redstoneValue; + CoverBehavior cover = getCoverAtSide(side); + if (cover != null) { + cover.onRedstoneInputSignalChange(redstoneValue); + } + } + } + } + @Override public void setCoverAtSide(@Nullable CoverBehavior coverBehavior, Direction side) { switch (side) { @@ -180,12 +233,12 @@ public void setCoverAtSide(@Nullable CoverBehavior coverBehavior, Direction side } @Override - public IItemHandlerModifiable getItemHandlerCap(@Nullable Direction side, boolean useCoverCapability) { + public IItemHandler getItemHandlerCap(@Nullable Direction side, boolean useCoverCapability) { return machine.getItemHandlerCap(side, useCoverCapability); } @Override - public IFluidHandlerModifiable getFluidHandlerCap(@Nullable Direction side, boolean useCoverCapability) { + public IFluidHandler getFluidHandlerCap(@Nullable Direction side, boolean useCoverCapability) { return machine.getFluidHandlerCap(side, useCoverCapability); } @@ -218,6 +271,12 @@ private CoverBehavior deserializeCoverUid(CompoundTag uid) { return definition.createCoverBehavior(this, side); } GTCEu.LOGGER.error("couldn't find cover definition {}", definitionId); - throw new RuntimeException(); + throw new RuntimeException("couldn't find cover definition " + definitionId); + } + + @Override + public @NotNull LazyOptional getCapability(@NotNull Capability capability, @Nullable Direction arg) { + // FIXME + return LazyOptional.empty(); } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/MetaMachine.java b/src/main/java/com/gregtechceu/gtceu/api/machine/MetaMachine.java index 9b8a88ab0b..110a860d45 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/MetaMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/MetaMachine.java @@ -12,6 +12,8 @@ import com.gregtechceu.gtceu.api.capability.IToolable; import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.cover.CoverBehavior; +import com.gregtechceu.gtceu.api.cover.filter.CoverWithFluidFilter; +import com.gregtechceu.gtceu.api.cover.filter.CoverWithItemFilter; import com.gregtechceu.gtceu.api.data.RotationState; import com.gregtechceu.gtceu.api.gui.GuiTextures; import com.gregtechceu.gtceu.api.gui.fancy.IFancyTooltip; @@ -22,9 +24,6 @@ import com.gregtechceu.gtceu.api.machine.trait.MachineTrait; import com.gregtechceu.gtceu.api.misc.IOFilteredInvWrapper; import com.gregtechceu.gtceu.api.misc.IOFluidHandlerList; -import com.gregtechceu.gtceu.api.transfer.fluid.IFluidHandlerModifiable; -import com.gregtechceu.gtceu.common.cover.FluidFilterCover; -import com.gregtechceu.gtceu.common.cover.ItemFilterCover; import com.gregtechceu.gtceu.common.item.tool.behavior.ToolModeSwitchBehavior; import com.lowdragmc.lowdraglib.gui.texture.IGuiTexture; @@ -55,6 +54,7 @@ import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.shapes.Shapes; @@ -63,6 +63,7 @@ import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.capability.IFluidHandler; +import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.IItemHandlerModifiable; import com.mojang.datafixers.util.Pair; @@ -180,6 +181,10 @@ public long getOffsetTimer() { return holder.getOffsetTimer(); } + public @Nullable BlockEntity getNeighbor(@NotNull Direction side) { + return holder.getNeighbor(side); + } + public void markDirty() { holder.getSelf().setChanged(); } @@ -660,8 +665,8 @@ public boolean canConnectRedstone(Direction side) { public Predicate getItemCapFilter(@Nullable Direction side, IO io) { if (side != null) { var cover = getCoverContainer().getCoverAtSide(side); - if (cover instanceof ItemFilterCover filterCover && filterCover.getFilterMode().filters(io)) { - return filterCover.getItemFilter(); + if (cover instanceof CoverWithItemFilter filterCover && filterCover.getFilterMode().filters(io)) { + return filterCover.getFilterHandler().getFilter(); } } return item -> true; @@ -670,15 +675,15 @@ public Predicate getItemCapFilter(@Nullable Direction side, IO io) { public Predicate getFluidCapFilter(@Nullable Direction side, IO io) { if (side != null) { var cover = getCoverContainer().getCoverAtSide(side); - if (cover instanceof FluidFilterCover filterCover && filterCover.getFilterMode().filters(io)) { - return filterCover.getFluidFilter(); + if (cover instanceof CoverWithFluidFilter filterCover && filterCover.getFilterMode().filters(io)) { + return filterCover.getFilterHandler().getFilter(); } } return fluid -> true; } @Nullable - public IItemHandlerModifiable getItemHandlerCap(@Nullable Direction side, boolean useCoverCapability) { + public IItemHandler getItemHandlerCap(@Nullable Direction side, boolean useCoverCapability) { var list = getTraits().stream() .filter(IItemHandlerModifiable.class::isInstance) .filter(t -> t.hasCapability(side)) @@ -702,7 +707,7 @@ public IItemHandlerModifiable getItemHandlerCap(@Nullable Direction side, boolea } @Nullable - public IFluidHandlerModifiable getFluidHandlerCap(@Nullable Direction side, boolean useCoverCapability) { + public IFluidHandler getFluidHandlerCap(@Nullable Direction side, boolean useCoverCapability) { var list = getTraits().stream() .filter(IFluidHandler.class::isInstance) .filter(t -> t.hasCapability(side)) diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/feature/IEnvironmentalHazardEmitter.java b/src/main/java/com/gregtechceu/gtceu/api/machine/feature/IEnvironmentalHazardEmitter.java index 6bcf6a76c5..29a4dd816a 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/feature/IEnvironmentalHazardEmitter.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/feature/IEnvironmentalHazardEmitter.java @@ -25,7 +25,7 @@ default MedicalCondition getConditionToEmit() { } /** - * @return the starting strength of the hazard zone. + * @return the starting strength of the hazard zone. recommended values are in the range [1,5). */ float getHazardStrengthPerOperation(); @@ -38,8 +38,9 @@ default void spreadEnvironmentalHazard() { IHazardParticleContainer container = GTCapabilityHelper.getHazardContainer(serverLevel, self().getPos().relative(self().getFrontFacing()), self().getFrontFacing().getOpposite()); if (container != null && - container.getHazardCanBeInserted(getConditionToEmit()) > getHazardStrengthPerOperation()) { - container.addHazard(getConditionToEmit(), getHazardStrengthPerOperation()); + container.addHazard(getConditionToEmit(), getHazardStrengthPerOperation(), true) > + getHazardStrengthPerOperation()) { + container.addHazard(getConditionToEmit(), getHazardStrengthPerOperation(), false); return; } diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/feature/ILocalizedHazardEmitter.java b/src/main/java/com/gregtechceu/gtceu/api/machine/feature/ILocalizedHazardEmitter.java index 72ab936914..d56d034dc8 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/feature/ILocalizedHazardEmitter.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/feature/ILocalizedHazardEmitter.java @@ -25,9 +25,9 @@ default MedicalCondition getConditionToEmit() { } /** - * @return the starting strength of the hazard zone. recommended values are in the range [1,5) + * @return the starting strength of the hazard zone. recommended values are in the range [1,5). */ - int getHazardSizePerOperation(); + int getHazardStrengthPerOperation(); default void spreadLocalizedHazard() { if (!ConfigHolder.INSTANCE.gameplay.environmentalHazards) { @@ -38,13 +38,14 @@ default void spreadLocalizedHazard() { IHazardParticleContainer container = GTCapabilityHelper.getHazardContainer(serverLevel, self().getPos().relative(self().getFrontFacing()), self().getFrontFacing().getOpposite()); if (container != null && - container.getHazardCanBeInserted(getConditionToEmit()) > getHazardSizePerOperation()) { - container.addHazard(getConditionToEmit(), getHazardSizePerOperation()); + container.addHazard(getConditionToEmit(), getHazardStrengthPerOperation(), true) > + getHazardStrengthPerOperation()) { + container.addHazard(getConditionToEmit(), getHazardStrengthPerOperation(), false); return; } var savedData = LocalizedHazardSavedData.getOrCreate(serverLevel); - savedData.addSphericalZone(self().getPos(), getHazardSizePerOperation(), false, + savedData.addSphericalZone(self().getPos(), getHazardStrengthPerOperation(), false, HazardProperty.HazardTrigger.INHALATION, getConditionToEmit()); } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/feature/multiblock/IMufflerMachine.java b/src/main/java/com/gregtechceu/gtceu/api/machine/feature/multiblock/IMufflerMachine.java index 6d7e53fb03..d5316a2289 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/feature/multiblock/IMufflerMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/feature/multiblock/IMufflerMachine.java @@ -3,6 +3,7 @@ import com.gregtechceu.gtceu.api.GTValues; import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; import com.gregtechceu.gtceu.api.capability.IHazardParticleContainer; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; import com.gregtechceu.gtceu.api.gui.GuiTextures; import com.gregtechceu.gtceu.api.gui.fancy.IFancyTooltip; import com.gregtechceu.gtceu.api.gui.fancy.TooltipsPanel; @@ -10,6 +11,7 @@ import com.gregtechceu.gtceu.api.machine.multiblock.part.TieredPartMachine; import com.gregtechceu.gtceu.api.recipe.GTRecipe; +import com.gregtechceu.gtceu.common.pipelike.block.duct.DuctStructure; import net.minecraft.ChatFormatting; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.network.chat.Component; @@ -27,11 +29,17 @@ public interface IMufflerMachine extends IMultiPart, IEnvironmentalHazardEmitter /** * @return true if front face is free and contains only air blocks in 1x1 area OR has a duct block on it. */ + @SuppressWarnings("RedundantIfStatement") default boolean isFrontFaceFree() { var frontPos = self().getPos().relative(self().getFrontFacing()); - return self().getLevel().getBlockState(frontPos).isAir() || - GTCapabilityHelper.getHazardContainer(self().getLevel(), - frontPos, self().getFrontFacing().getOpposite()) != null; + if (self().getLevel().getBlockState(frontPos).isAir()) { + return true; + } else if (self().getLevel().getBlockEntity(frontPos) instanceof PipeBlockEntity pipe && + pipe.isConnected(self().getFrontFacing().getOpposite()) && + pipe.getStructure() instanceof DuctStructure) { + return true; + } + return false; } default void emitPollutionParticles() { @@ -76,6 +84,15 @@ default GTRecipe modifyRecipe(GTRecipe recipe) { return IMultiPart.super.modifyRecipe(recipe); } + @Override + default boolean onWorking(IWorkableMultiController controller) { + if (!isFrontFaceFree()) { + controller.getRecipeLogic() + .setWaiting(Component.translatable("gtceu.multiblock.universal.muffler_obstructed")); + } + return IMultiPart.super.onWorking(controller); + } + @Override default float getHazardStrengthPerOperation() { float outputAmount = 2.5f; diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/feature/multiblock/IMultiPart.java b/src/main/java/com/gregtechceu/gtceu/api/machine/feature/multiblock/IMultiPart.java index fd65c979f5..b6526ffbf0 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/feature/multiblock/IMultiPart.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/feature/multiblock/IMultiPart.java @@ -122,6 +122,7 @@ default boolean beforeWorking(IWorkableMultiController controller) { * @return modified recipe. * null -- this recipe is unavailable */ + @Nullable default GTRecipe modifyRecipe(GTRecipe recipe) { return recipe; } diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/multiblock/MultiblockDisplayText.java b/src/main/java/com/gregtechceu/gtceu/api/machine/multiblock/MultiblockDisplayText.java index 6ed5ba3d22..2f47f6f835 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/multiblock/MultiblockDisplayText.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/multiblock/MultiblockDisplayText.java @@ -210,7 +210,7 @@ public Builder addEnergyProductionAmpsLine(long maxVoltage, int amperage) { *
* Added if the structure is formed and if the max CWU/t is greater than zero. */ - public Builder addComputationUsageLine(int maxCWUt) { + public Builder addComputationUsageLine(long maxCWUt) { if (!isStructureFormed) return this; if (maxCWUt > 0) { diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/multiblock/PartAbility.java b/src/main/java/com/gregtechceu/gtceu/api/machine/multiblock/PartAbility.java index c785fbf93c..4925d7a391 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/multiblock/PartAbility.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/multiblock/PartAbility.java @@ -46,8 +46,8 @@ public class PartAbility { public static final PartAbility TANK_VALVE = new PartAbility("tank_valve"); public static final PartAbility PASSTHROUGH_HATCH = new PartAbility("passthrough_hatch"); public static final PartAbility PARALLEL_HATCH = new PartAbility("parallel_hatch"); - public static final PartAbility INPUT_LASER = new PartAbility("input_laser"); - public static final PartAbility OUTPUT_LASER = new PartAbility("output_laser"); + public static final PartAbility LASER_RECEPTION = new PartAbility("laser_reception"); + public static final PartAbility LASER_TRANSMISSION = new PartAbility("laser_transmission"); public static final PartAbility COMPUTATION_DATA_RECEPTION = new PartAbility("computation_data_reception"); public static final PartAbility COMPUTATION_DATA_TRANSMISSION = new PartAbility("computation_data_transmission"); diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableComputationContainer.java b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableComputationContainer.java index adc57c3006..6e4f642ef7 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableComputationContainer.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableComputationContainer.java @@ -1,35 +1,40 @@ package com.gregtechceu.gtceu.api.machine.trait; import com.gregtechceu.gtceu.GTCEu; -import com.gregtechceu.gtceu.api.capability.IOpticalComputationHatch; -import com.gregtechceu.gtceu.api.capability.IOpticalComputationProvider; -import com.gregtechceu.gtceu.api.capability.IOpticalComputationReceiver; +import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; +import com.gregtechceu.gtceu.api.capability.IWorkable; +import com.gregtechceu.gtceu.api.capability.data.IComputationDataAccess; +import com.gregtechceu.gtceu.api.capability.data.IComputationProvider; +import com.gregtechceu.gtceu.api.capability.data.IComputationUser; +import com.gregtechceu.gtceu.api.capability.data.IDataAccess; +import com.gregtechceu.gtceu.api.capability.data.query.DataQueryObject; +import com.gregtechceu.gtceu.api.capability.data.query.IBridgeable; +import com.gregtechceu.gtceu.api.capability.data.query.IComputationQuery; import com.gregtechceu.gtceu.api.capability.forge.GTCapability; import com.gregtechceu.gtceu.api.capability.recipe.CWURecipeCapability; import com.gregtechceu.gtceu.api.capability.recipe.IO; -import com.gregtechceu.gtceu.api.capability.recipe.IRecipeCapabilityHolder; import com.gregtechceu.gtceu.api.capability.recipe.RecipeCapability; import com.gregtechceu.gtceu.api.machine.MetaMachine; import com.gregtechceu.gtceu.api.machine.feature.IRecipeLogicMachine; import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMultiController; import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMultiPart; import com.gregtechceu.gtceu.api.recipe.GTRecipe; -import com.gregtechceu.gtceu.common.blockentity.OpticalPipeBlockEntity; import com.gregtechceu.gtceu.utils.GTUtil; import net.minecraft.core.Direction; import net.minecraft.world.level.block.entity.BlockEntity; +import com.google.common.primitives.Ints; import lombok.Getter; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Collection; +import java.util.ArrayList; import java.util.Collections; import java.util.List; -public class NotifiableComputationContainer extends NotifiableRecipeHandlerTrait - implements IOpticalComputationHatch, IOpticalComputationReceiver { +public class NotifiableComputationContainer extends NotifiableRecipeHandlerTrait + implements IComputationProvider, IComputationUser, IComputationDataAccess { @Getter protected IO handlerIO; @@ -37,7 +42,7 @@ public class NotifiableComputationContainer extends NotifiableRecipeHandlerTrait protected boolean transmitter; protected long lastTimeStamp; - private int currentOutputCwu = 0, lastOutputCwu = 0; + private long currentOutputCwu = 0, lastOutputCwu = 0; public NotifiableComputationContainer(MetaMachine machine, IO handlerIO, boolean transmitter) { super(machine); @@ -48,7 +53,50 @@ public NotifiableComputationContainer(MetaMachine machine, IO handlerIO, boolean } @Override - public int requestCWUt(int cwut, boolean simulate, @NotNull Collection seen) { + public boolean accessData(@NotNull DataQueryObject queryObject) { + if (isTransmitter()) { + MetaMachine machine = getMachine(); + if (machine instanceof IWorkable workable && !workable.isActive()) return false; + + List accesses = new ArrayList<>(); + if (machine instanceof IComputationProvider provider && + queryObject instanceof IComputationQuery cq) { + cq.registerProvider(provider); + } + if (machine instanceof IComputationDataAccess dataAccess) { + accesses.add(dataAccess); + } + if (machine instanceof IMultiPart part) { + for (IMultiController controller : part.getControllers()) { + if (controller instanceof IComputationProvider provider && + queryObject instanceof IComputationQuery cq) { + cq.registerProvider(provider); + } + if (controller instanceof IComputationDataAccess dataAccess) { + accesses.add(dataAccess); + } + for (MachineTrait trait : controller.self().getTraits()) { + if (trait instanceof IComputationDataAccess dataAccess) { + accesses.add(dataAccess); + } + } + } + } + if (queryObject instanceof IBridgeable bridgeable && accesses.size() > 1) { + bridgeable.setBridged(); + } + return IDataAccess.accessData(accesses, queryObject); + } else { + Direction front = machine.getFrontFacing(); + IDataAccess access = GTCapabilityHelper.getDataAccess(machine.getLevel(), machine.getPos().relative(front), + front.getOpposite()); + if (queryObject.traverseTo(access)) return access.accessData(queryObject); + } + return false; + } + + @Override + public long supplyCWU(long requested, boolean simulate) { var latestTimeStamp = getMachine().getOffsetTimer(); if (lastTimeStamp < latestTimeStamp) { lastOutputCwu = currentOutputCwu; @@ -56,23 +104,22 @@ public int requestCWUt(int cwut, boolean simulate, @NotNull Collection seen) { - seen.add(this); + public long requestCWU(long requested, boolean simulate) { + var latestTimeStamp = getMachine().getOffsetTimer(); + if (lastTimeStamp < latestTimeStamp) { + lastOutputCwu = currentOutputCwu; + currentOutputCwu = 0; + lastTimeStamp = latestTimeStamp; + } + if (handlerIO == IO.IN) { if (isTransmitter()) { // Ask the Multiblock controller, which *should* be an IOpticalComputationProvider - if (machine instanceof IOpticalComputationProvider provider) { - return provider.getMaxCWUt(seen); + if (machine instanceof IComputationUser user) { + return user.requestCWU(requested, simulate); } else if (machine instanceof IMultiPart part) { if (part.getControllers().isEmpty()) { return 0; } for (IMultiController controller : part.getControllers()) { - if (!controller.isFormed()) { - continue; - } - if (controller instanceof IOpticalComputationProvider provider) { - return provider.getMaxCWUt(seen); + if (controller instanceof IComputationUser provider) { + return provider.requestCWU(requested, simulate); } for (MachineTrait trait : controller.self().getTraits()) { - if (trait instanceof IOpticalComputationProvider provider) { - return provider.getMaxCWUt(seen); + if (trait instanceof IComputationUser provider) { + return provider.requestCWU(requested, simulate); } } } - GTCEu.LOGGER.error( - "NotifiableComputationContainer could not get maximum CWU/t from its machine's controller!"); + GTCEu.LOGGER + .error("NotifiableComputationContainer could request CWU/t from its machine's controller!"); return 0; } else { - GTCEu.LOGGER.error("NotifiableComputationContainer could not get maximum CWU/t from its machine!"); + GTCEu.LOGGER.error("NotifiableComputationContainer could request CWU/t from its machine!"); return 0; } } else { // Ask the attached Transmitter hatch, if it exists - IOpticalComputationProvider provider = getOpticalNetProvider(); + IComputationUser provider = getOpticalNetUser(); if (provider == null) return 0; - return provider.getMaxCWUt(seen); + return provider.requestCWU(requested, simulate); } } else { - return lastOutputCwu; + lastOutputCwu = lastOutputCwu - requested; + return Math.min(lastOutputCwu, requested); } } @Override - public boolean canBridge(@NotNull Collection seen) { - seen.add(this); + public long maxCWUt() { if (handlerIO == IO.IN) { if (isTransmitter()) { // Ask the Multiblock controller, which *should* be an IOpticalComputationProvider - if (machine instanceof IOpticalComputationProvider provider) { - return provider.canBridge(seen); + if (machine instanceof IComputationProvider provider) { + return provider.maxCWUt(); } else if (machine instanceof IMultiPart part) { if (part.getControllers().isEmpty()) { - return false; + return 0; } for (IMultiController controller : part.getControllers()) { if (!controller.isFormed()) { continue; } - if (controller instanceof IOpticalComputationProvider provider) { - return provider.canBridge(seen); + if (controller instanceof IComputationProvider provider) { + return provider.maxCWUt(); } for (MachineTrait trait : controller.self().getTraits()) { - if (trait instanceof IOpticalComputationProvider provider) { - return provider.canBridge(seen); + if (trait instanceof IComputationProvider provider) { + return provider.maxCWUt(); } } } GTCEu.LOGGER.error( - "NotifiableComputationContainer could not test bridge status of its machine's controller!"); - return false; + "NotifiableComputationContainer could not get maximum CWU/t from its machine's controller!"); + return 0; } else { - GTCEu.LOGGER.error("NotifiableComputationContainer could not test bridge status of its machine!"); - return false; + GTCEu.LOGGER.error("NotifiableComputationContainer could not get maximum CWU/t from its machine!"); + return 0; } } else { // Ask the attached Transmitter hatch, if it exists - IOpticalComputationProvider provider = getOpticalNetProvider(); - if (provider == null) return true; // nothing found, so don't report a problem, just pass quietly - return provider.canBridge(seen); + IComputationProvider provider = getOpticalNetProvider(); + if (provider == null) return 0; + return provider.maxCWUt(); } } else { - return false; + return lastOutputCwu; } } @Override - public List handleRecipeInner(IO io, GTRecipe recipe, List left, @Nullable String slotName, - boolean simulate) { - IOpticalComputationProvider provider = getOpticalNetProvider(); + public boolean supportsBridging() { + if (isTransmitter()) { + // Ask the Multiblock controller, which *should* be an IOpticalComputationProvider + if (machine instanceof IComputationProvider provider) { + return provider.supportsBridging(); + } else if (machine instanceof IMultiPart part) { + if (part.getControllers().isEmpty()) { + return false; + } + for (IMultiController controller : part.getControllers()) { + if (!controller.isFormed()) { + continue; + } + if (controller instanceof IComputationProvider provider) { + return provider.supportsBridging(); + } + for (MachineTrait trait : controller.self().getTraits()) { + if (trait instanceof IComputationProvider provider) { + return provider.supportsBridging(); + } + } + } + GTCEu.LOGGER.error( + "NotifiableComputationContainer could not test bridge status of its machine's controller!"); + return false; + } else { + GTCEu.LOGGER.error("NotifiableComputationContainer could not test bridge status of its machine!"); + return false; + } + } else { + // Ask the attached Transmitter hatch, if it exists + IComputationProvider provider = getOpticalNetProvider(); + if (provider == null) return true; // nothing found, so don't report a problem, just pass quietly + return provider.supportsBridging(); + } + } + + @Override + public List handleRecipeInner(IO io, GTRecipe recipe, List left, @Nullable String slotName, + boolean simulate) { + IComputationProvider provider = getOpticalNetProvider(); if (provider == null) return left; - int sum = left.stream().reduce(0, Integer::sum); + long sum = left.stream().reduce(0L, Long::sum); if (io == IO.IN) { - int availableCWUt = requestCWUt(Integer.MAX_VALUE, true); + long availableCWUt = requestCWU(Integer.MAX_VALUE, true); if (availableCWUt >= sum) { if (recipe.data.getBoolean("duration_is_total_cwu")) { - int drawn = provider.requestCWUt(availableCWUt, simulate); + int drawn = Ints.saturatedCast(provider.supplyCWU(availableCWUt, simulate)); if (!simulate) { if (machine instanceof IRecipeLogicMachine rlm) { // first, remove the progress the recipe logic adds. @@ -209,11 +297,11 @@ public List handleRecipeInner(IO io, GTRecipe recipe, List lef } sum -= drawn; } else { - sum -= provider.requestCWUt(sum, simulate); + sum -= provider.supplyCWU(sum, simulate); } } } else if (io == IO.OUT) { - int canInput = this.getMaxCWUt() - this.lastOutputCwu; + long canInput = this.maxCWUt() - this.lastOutputCwu; if (!simulate) { this.currentOutputCwu = Math.min(canInput, sum); } @@ -233,53 +321,26 @@ public double getTotalContentAmount() { } @Override - public RecipeCapability getCapability() { + public RecipeCapability getCapability() { return CWURecipeCapability.CAP; } @Nullable - @Override - public IOpticalComputationProvider getComputationProvider() { - if (this.handlerIO.support(IO.OUT)) { - return this; - } - if (machine instanceof IOpticalComputationReceiver receiver) { - return receiver.getComputationProvider(); - } else if (machine instanceof IOpticalComputationProvider provider) { - return provider; - } else if (machine instanceof IRecipeCapabilityHolder recipeCapabilityHolder) { - if (recipeCapabilityHolder.getCapabilitiesProxy().contains(IO.IN, CWURecipeCapability.CAP) && - !recipeCapabilityHolder.getCapabilitiesProxy().get(IO.IN, CWURecipeCapability.CAP).isEmpty()) { - var provider = (IOpticalComputationProvider) recipeCapabilityHolder.getCapabilitiesProxy() - .get(IO.IN, CWURecipeCapability.CAP).get(0); - if (provider != this) { - return provider; - } - } - } + private IComputationProvider getOpticalNetProvider() { for (Direction direction : GTUtil.DIRECTIONS) { BlockEntity blockEntity = machine.getLevel().getBlockEntity(machine.getPos().relative(direction)); - if (blockEntity == null) continue; - - // noinspection DataFlowIssue can be null just fine. - IOpticalComputationProvider provider = blockEntity - .getCapability(GTCapability.CAPABILITY_COMPUTATION_PROVIDER, direction.getOpposite()).orElse(null); - // noinspection ConstantValue can be null because above. - if (provider != null && provider != this) { - return provider; - } + return blockEntity.getCapability(GTCapability.CAPABILITY_COMPUTATION_PROVIDER, direction.getOpposite()) + .resolve().orElse(null); } return null; } @Nullable - private IOpticalComputationProvider getOpticalNetProvider() { + private IComputationUser getOpticalNetUser() { for (Direction direction : GTUtil.DIRECTIONS) { BlockEntity blockEntity = machine.getLevel().getBlockEntity(machine.getPos().relative(direction)); - if (blockEntity instanceof OpticalPipeBlockEntity) { - return blockEntity.getCapability(GTCapability.CAPABILITY_COMPUTATION_PROVIDER, direction.getOpposite()) - .orElse(null); - } + return blockEntity.getCapability(GTCapability.CAPABILITY_COMPUTATION_USER, direction.getOpposite()) + .resolve().orElse(null); } return null; } diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableEnergyContainer.java b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableEnergyContainer.java index 3ccf38e7c8..4c17e52fd7 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableEnergyContainer.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableEnergyContainer.java @@ -172,7 +172,7 @@ public void serverTick() { machine.getPos().relative(side), oppositeSide); if (energyContainer != null && energyContainer.inputsEnergy(oppositeSide)) { amperesUsed += energyContainer.acceptEnergyFromNetwork(oppositeSide, outputVoltage, - outputAmperes - amperesUsed); + outputAmperes - amperesUsed, false); if (amperesUsed == outputAmperes) break; } } @@ -254,7 +254,7 @@ private boolean handleForgeEnergyItem(IEnergyStorage energyStorage, boolean simu } @Override - public long acceptEnergyFromNetwork(Direction side, long voltage, long amperage) { + public long acceptEnergyFromNetwork(Direction side, long voltage, long amperage, boolean simulate) { var latestTimeStamp = getMachine().getOffsetTimer(); if (lastTimeStamp < latestTimeStamp) { amps = 0; diff --git a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableLaserContainer.java b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableLaserContainer.java index b1e65e8fb1..21767ad2c6 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableLaserContainer.java +++ b/src/main/java/com/gregtechceu/gtceu/api/machine/trait/NotifiableLaserContainer.java @@ -2,7 +2,9 @@ import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; import com.gregtechceu.gtceu.api.capability.ILaserContainer; +import com.gregtechceu.gtceu.api.capability.ILaserRelay; import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.api.machine.feature.IExplosionMachine; import com.gregtechceu.gtceu.utils.GTUtil; import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; @@ -45,11 +47,10 @@ public void serverTick() { if (!outputsEnergy(side)) continue; BlockEntity tileEntity = getMachine().getLevel().getBlockEntity(getMachine().getPos().relative(side)); Direction oppositeSide = side.getOpposite(); - ILaserContainer laserContainer = GTCapabilityHelper.getLaser(getMachine().getLevel(), + ILaserRelay laserContainer = GTCapabilityHelper.getLaser(getMachine().getLevel(), getMachine().getPos().relative(side), oppositeSide); if (tileEntity != null && laserContainer != null) { - if (laserContainer == null || !laserContainer.inputsEnergy(oppositeSide)) continue; - amperesUsed += laserContainer.acceptEnergyFromNetwork(oppositeSide, outputVoltage, + amperesUsed += laserContainer.receiveLaser(outputVoltage, outputAmperes - amperesUsed); if (amperesUsed == outputAmperes) break; } @@ -58,4 +59,15 @@ public void serverTick() { setEnergyStored(getEnergyStored() - amperesUsed * outputVoltage); } } + + @Override + public long receiveLaser(long laserVoltage, long laserAmperage) { + if (getInputVoltage() == 0) return 0; + long allowedAmps = getEnergyCanBeInserted() / laserVoltage; + addEnergy(laserVoltage * allowedAmps); + // over voltage explosion + if (laserVoltage > getInputVoltage() && getMachine() instanceof IExplosionMachine explosionMachine) + explosionMachine.doExplosion(GTUtil.getExplosionPower(laserVoltage)); + return allowedAmps; + } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/misc/EnergyContainerList.java b/src/main/java/com/gregtechceu/gtceu/api/misc/EnergyContainerList.java index f4b0c6c0aa..d2462854c3 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/misc/EnergyContainerList.java +++ b/src/main/java/com/gregtechceu/gtceu/api/misc/EnergyContainerList.java @@ -131,11 +131,11 @@ private static boolean isPowerOfFour(long l) { } @Override - public long acceptEnergyFromNetwork(Direction side, long voltage, long amperage) { + public long acceptEnergyFromNetwork(Direction side, long voltage, long amperage, boolean simulate) { long amperesUsed = 0L; List energyContainerList = this.energyContainerList; for (IEnergyContainer iEnergyContainer : energyContainerList) { - amperesUsed += iEnergyContainer.acceptEnergyFromNetwork(null, voltage, amperage); + amperesUsed += iEnergyContainer.acceptEnergyFromNetwork(null, voltage, amperage, simulate); if (amperage == amperesUsed) { return amperesUsed; } diff --git a/src/main/java/com/gregtechceu/gtceu/api/misc/LaserContainerList.java b/src/main/java/com/gregtechceu/gtceu/api/misc/LaserContainerList.java index 210a52d848..368aef62f9 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/misc/LaserContainerList.java +++ b/src/main/java/com/gregtechceu/gtceu/api/misc/LaserContainerList.java @@ -15,11 +15,11 @@ public LaserContainerList(List energyContainerList) { } @Override - public long acceptEnergyFromNetwork(Direction side, long voltage, long amperage) { + public long acceptEnergyFromNetwork(Direction side, long voltage, long amperage, boolean simulate) { long amperesUsed = 0L; List energyContainerList = this.energyContainerList; for (ILaserContainer iEnergyContainer : energyContainerList) { - amperesUsed += iEnergyContainer.acceptEnergyFromNetwork(null, voltage, amperage); + amperesUsed += iEnergyContainer.acceptEnergyFromNetwork(null, voltage, amperage, false); if (amperage == amperesUsed) { return amperesUsed; } @@ -95,4 +95,20 @@ public boolean inputsEnergy(Direction side) { public boolean outputsEnergy(Direction side) { return true; } + + @Override + public long receiveLaser(long laserVoltage, long laserAmperage) { + long available = laserAmperage; + List energyContainerList = this.energyContainerList; + for (ILaserContainer container : energyContainerList) { + long transmitted = container.receiveLaser(laserAmperage, laserAmperage); + if (transmitted > 0) { + available -= transmitted; + if (available <= 0) { + return laserAmperage; + } + } + } + return laserAmperage - available; + } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/misc/forge/SimpleThermalFluidHandlerItemStack.java b/src/main/java/com/gregtechceu/gtceu/api/misc/forge/SimpleThermalFluidHandlerItemStack.java index a256aede09..6937c7441e 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/misc/forge/SimpleThermalFluidHandlerItemStack.java +++ b/src/main/java/com/gregtechceu/gtceu/api/misc/forge/SimpleThermalFluidHandlerItemStack.java @@ -6,6 +6,7 @@ import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.capability.templates.FluidHandlerItemStackSimple; +import lombok.Getter; import org.jetbrains.annotations.NotNull; /** @@ -16,20 +17,22 @@ public class SimpleThermalFluidHandlerItemStack extends FluidHandlerItemStackSimple implements IThermalFluidHandlerItemStack { + @Getter public final int maxFluidTemperature; + @Getter + public final int minFluidTemperature; + @Getter private final boolean gasProof; - private final boolean acidProof; - private final boolean cryoProof; + @Getter private final boolean plasmaProof; - public SimpleThermalFluidHandlerItemStack(@NotNull ItemStack container, int capacity, int maxFluidTemperature, - boolean gasProof, boolean acidProof, boolean cryoProof, - boolean plasmaProof) { + public SimpleThermalFluidHandlerItemStack(@NotNull ItemStack container, int capacity, + int maxFluidTemperature, int minFluidTemperature, + boolean gasProof, boolean plasmaProof) { super(container, capacity); this.maxFluidTemperature = maxFluidTemperature; + this.minFluidTemperature = minFluidTemperature; this.gasProof = gasProof; - this.acidProof = acidProof; - this.cryoProof = cryoProof; this.plasmaProof = plasmaProof; } @@ -52,29 +55,4 @@ private void removeTagWhenEmpty(FluidAction action) { this.container.setTag(null); } } - - @Override - public int getMaxFluidTemperature() { - return maxFluidTemperature; - } - - @Override - public boolean isGasProof() { - return gasProof; - } - - @Override - public boolean isAcidProof() { - return acidProof; - } - - @Override - public boolean isCryoProof() { - return cryoProof; - } - - @Override - public boolean isPlasmaProof() { - return plasmaProof; - } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/misc/forge/ThermalFluidHandlerItemStack.java b/src/main/java/com/gregtechceu/gtceu/api/misc/forge/ThermalFluidHandlerItemStack.java index 987090ffe8..82fe78e05b 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/misc/forge/ThermalFluidHandlerItemStack.java +++ b/src/main/java/com/gregtechceu/gtceu/api/misc/forge/ThermalFluidHandlerItemStack.java @@ -6,27 +6,31 @@ import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.capability.templates.FluidHandlerItemStack; +import lombok.Getter; import org.jetbrains.annotations.NotNull; public class ThermalFluidHandlerItemStack extends FluidHandlerItemStack implements IThermalFluidHandlerItemStack { + @Getter private final int maxFluidTemperature; + @Getter + private final int minFluidTemperature; + @Getter private final boolean gasProof; - private final boolean acidProof; - private final boolean cryoProof; + @Getter private final boolean plasmaProof; /** * @param container The container itemStack, data is stored on it directly as NBT. * @param capacity The maximum capacity of this fluid tank. */ - public ThermalFluidHandlerItemStack(@NotNull ItemStack container, int capacity, int maxFluidTemperature, - boolean gasProof, boolean acidProof, boolean cryoProof, boolean plasmaProof) { + public ThermalFluidHandlerItemStack(@NotNull ItemStack container, int capacity, + int maxFluidTemperature, int minFluidTemperature, + boolean gasProof, boolean plasmaProof) { super(container, capacity); this.maxFluidTemperature = maxFluidTemperature; + this.minFluidTemperature = minFluidTemperature; this.gasProof = gasProof; - this.acidProof = acidProof; - this.cryoProof = cryoProof; this.plasmaProof = plasmaProof; } @@ -54,29 +58,4 @@ private void removeTagWhenEmpty(FluidAction action) { public boolean canFillFluidType(FluidStack fluid) { return IThermalFluidHandlerItemStack.super.canFillFluidType(fluid); } - - @Override - public int getMaxFluidTemperature() { - return maxFluidTemperature; - } - - @Override - public boolean isGasProof() { - return gasProof; - } - - @Override - public boolean isAcidProof() { - return acidProof; - } - - @Override - public boolean isCryoProof() { - return cryoProof; - } - - @Override - public boolean isPlasmaProof() { - return plasmaProof; - } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/network/PacketDataList.java b/src/main/java/com/gregtechceu/gtceu/api/network/PacketDataList.java new file mode 100644 index 0000000000..17dca65cf3 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/network/PacketDataList.java @@ -0,0 +1,111 @@ +package com.gregtechceu.gtceu.api.network; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; + +import org.jetbrains.annotations.NotNull; + +/** + * An optimised data structure backed by two arrays. + * This is essentially equivalent to List>, but more efficient. + * {@link it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap} can not be used since it doesn't allow duplicate + * discriminators. + */ +public class PacketDataList { + + private int[] discriminators; + private byte[][] data; + private int size = 0; + + public PacketDataList() { + this.discriminators = new int[4]; + this.data = new byte[4][]; + } + + /** + * Resizes the arrays to fit the required elements. + * + * @param s minimum size + */ + private void ensureSize(int s) { + if (this.discriminators.length < s) { + int n = this.discriminators.length; + int newCapacity = Math.max(n + 2, s); // there are rarely more than 2 elements in the list + int[] temp = new int[newCapacity]; + byte[][] temp2 = new byte[newCapacity][]; + System.arraycopy(this.discriminators, 0, temp, 0, n); + System.arraycopy(this.data, 0, temp2, 0, n); + this.discriminators = temp; + this.data = temp2; + } + } + + /** + * Adds a discriminator - data pair to the list + * + * @param discriminator data id + * @param data data + */ + public void add(int discriminator, byte[] data) { + ensureSize(this.size + 1); + this.discriminators[this.size] = discriminator; + this.data[this.size] = data; + this.size++; + } + + /** + * Adds all discriminator - data pairs from another list. + * This does not check if the other list is empty or the same list. + * + * @param dataList other data list + */ + public void addAll(PacketDataList dataList) { + ensureSize(this.size + dataList.size); + System.arraycopy(dataList.discriminators, 0, this.discriminators, this.size, dataList.size); + System.arraycopy(dataList.data, 0, this.data, this.size, dataList.size); + this.size += dataList.size; + } + + /** + * @return amount of data packets + */ + public int size() { + return size; + } + + /** + * @return true if there are no data packets + */ + public boolean isEmpty() { + return size == 0; + } + + /** + * remove all data packets + */ + public void clear() { + for (int i = 0; i < this.size; i++) { + this.data[i] = null; + } + this.size = 0; + } + + /** + * Writes all discriminator - data pairs to a nbt list. + * Also removes all data packets from this list. + * + * @return nbt list with discriminators and data + */ + @NotNull + public ListTag dumpToNbt() { + ListTag listTag = new ListTag(); + for (int i = 0; i < this.size; i++) { + CompoundTag entryTag = new CompoundTag(); + entryTag.putByteArray(Integer.toString(this.discriminators[i]), this.data[i]); + listTag.add(entryTag); + this.data[i] = null; + } + this.size = 0; + return listTag; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/api/pattern/BlockPattern.java b/src/main/java/com/gregtechceu/gtceu/api/pattern/BlockPattern.java index 6053834f31..725472939b 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/pattern/BlockPattern.java +++ b/src/main/java/com/gregtechceu/gtceu/api/pattern/BlockPattern.java @@ -141,7 +141,7 @@ public boolean checkPatternAt(MultiblockState worldState, BlockPos centerPos, Di } } boolean canPartShared = true; - if (worldState.getTileEntity() instanceof IMachineBlockEntity machineBlockEntity && + if (worldState.getBlockEntity() instanceof IMachineBlockEntity machineBlockEntity && machineBlockEntity.getMetaMachine() instanceof IMultiPart part) { // add detected parts if (!predicate.isAny()) { if (part.isFormed() && !part.canShared() && diff --git a/src/main/java/com/gregtechceu/gtceu/api/pattern/MultiblockState.java b/src/main/java/com/gregtechceu/gtceu/api/pattern/MultiblockState.java index 4549c44422..5ad9fff81c 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/pattern/MultiblockState.java +++ b/src/main/java/com/gregtechceu/gtceu/api/pattern/MultiblockState.java @@ -119,7 +119,7 @@ public BlockState getBlockState() { } @Nullable - public BlockEntity getTileEntity() { + public BlockEntity getBlockEntity() { if (!getBlockState().hasBlockEntity()) { return null; } diff --git a/src/main/java/com/gregtechceu/gtceu/api/pattern/Predicates.java b/src/main/java/com/gregtechceu/gtceu/api/pattern/Predicates.java index e9936719d1..f96156d907 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/pattern/Predicates.java +++ b/src/main/java/com/gregtechceu/gtceu/api/pattern/Predicates.java @@ -9,11 +9,11 @@ import com.gregtechceu.gtceu.api.capability.recipe.ItemRecipeCapability; import com.gregtechceu.gtceu.api.data.chemical.material.Material; import com.gregtechceu.gtceu.api.data.tag.TagPrefix; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; import com.gregtechceu.gtceu.api.machine.multiblock.IBatteryData; import com.gregtechceu.gtceu.api.machine.multiblock.PartAbility; import com.gregtechceu.gtceu.api.pattern.error.PatternStringError; import com.gregtechceu.gtceu.api.pattern.predicates.*; -import com.gregtechceu.gtceu.api.pipenet.IPipeNode; import com.gregtechceu.gtceu.api.recipe.GTRecipeType; import com.gregtechceu.gtceu.common.block.BatteryBlock; import com.gregtechceu.gtceu.common.block.CoilBlock; @@ -268,8 +268,8 @@ public static TraceabilityPredicate frames(Material... frameMaterials) { .filter(Objects::nonNull).filter(RegistryEntry::isPresent).map(RegistryEntry::get) .toArray(Block[]::new)) .or(new TraceabilityPredicate(blockWorldState -> { - BlockEntity tileEntity = blockWorldState.getTileEntity(); - if (!(tileEntity instanceof IPipeNode pipeNode)) { + BlockEntity tileEntity = blockWorldState.getBlockEntity(); + if (!(tileEntity instanceof PipeBlockEntity pipeNode)) { return false; } return ArrayUtils.contains(frameMaterials, pipeNode.getFrameMaterial()); diff --git a/src/main/java/com/gregtechceu/gtceu/api/pattern/predicates/SimplePredicate.java b/src/main/java/com/gregtechceu/gtceu/api/pattern/predicates/SimplePredicate.java index d4082a60db..09bf719ff0 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/pattern/predicates/SimplePredicate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/pattern/predicates/SimplePredicate.java @@ -131,7 +131,7 @@ private boolean checkInnerConditions(MultiblockState blockWorldState) { } } if (nbtParser != null && !blockWorldState.world.isClientSide) { - BlockEntity te = blockWorldState.getTileEntity(); + BlockEntity te = blockWorldState.getBlockEntity(); if (te != null) { CompoundTag nbt = te.saveWithFullMetadata(); if (Pattern.compile(nbtParser).matcher(nbt.toString()).find()) { diff --git a/src/main/java/com/gregtechceu/gtceu/api/pipenet/IAttachData.java b/src/main/java/com/gregtechceu/gtceu/api/pipenet/IAttachData.java deleted file mode 100644 index 59ca6377ae..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/api/pipenet/IAttachData.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.gregtechceu.gtceu.api.pipenet; - -import net.minecraft.core.Direction; - -/** - * @author KilaBash - * @date 2023/3/1 - * @implNote IAttachData - */ -public interface IAttachData { - - /** - * is the node can attach to the side. - */ - boolean canAttachTo(Direction side); - - /** - * set it attach to a side. - * - * @return whether the status is changed. - */ - boolean setAttached(Direction side, boolean attach); -} diff --git a/src/main/java/com/gregtechceu/gtceu/api/pipenet/IMaterialPipeType.java b/src/main/java/com/gregtechceu/gtceu/api/pipenet/IMaterialPipeType.java deleted file mode 100644 index ee96ff3331..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/api/pipenet/IMaterialPipeType.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.gregtechceu.gtceu.api.pipenet; - -import com.gregtechceu.gtceu.api.data.tag.TagPrefix; - -public interface IMaterialPipeType extends IPipeType { - - /** - * Determines tag prefix used for this pipe type, which gives pipe tag key - * when combined with pipe's material - * - * @return tag prefix used for this pipe type - */ - TagPrefix getTagPrefix(); -} diff --git a/src/main/java/com/gregtechceu/gtceu/api/pipenet/IPipeNode.java b/src/main/java/com/gregtechceu/gtceu/api/pipenet/IPipeNode.java deleted file mode 100644 index 84a0ce52ef..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/api/pipenet/IPipeNode.java +++ /dev/null @@ -1,178 +0,0 @@ -package com.gregtechceu.gtceu.api.pipenet; - -import com.gregtechceu.gtceu.GTCEu; -import com.gregtechceu.gtceu.api.block.PipeBlock; -import com.gregtechceu.gtceu.api.blockentity.IPaintable; -import com.gregtechceu.gtceu.api.blockentity.ITickSubscription; -import com.gregtechceu.gtceu.api.blockentity.PipeBlockEntity; -import com.gregtechceu.gtceu.api.capability.ICoverable; -import com.gregtechceu.gtceu.api.data.chemical.material.Material; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.entity.BlockEntity; - -import org.jetbrains.annotations.Nullable; - -public interface IPipeNode & IPipeType, NodeDataType> - extends ITickSubscription, IPaintable { - - long getOffsetTimer(); - - /** - * Get Cover Container. - */ - ICoverable getCoverContainer(); - - /** - * If tube is set to block connection from the specific side - * - * @param side face - */ - default boolean isBlocked(Direction side) { - return PipeBlockEntity.isFaceBlocked(getBlockedConnections(), side); - } - - /** - * Unsafe!!! to set internal connections. - * In general, you shouldn't call it yourself. - */ - void setConnections(int connections); - - int getConnections(); - - int getNumConnections(); - - /** - * set to block connection from the specific side - * - * @param side face - * @param isBlocked is blocked - */ - void setBlocked(Direction side, boolean isBlocked); - - /** - * Whether pipe can attach to specific side. - * e.g. check if there is an energyContainer nearby. - */ - boolean canAttachTo(Direction side); - - /** - * get connections for rendering and collision. - */ - int getVisualConnections(); - - /** - * If node is connected to the specific side - * - * @param side face - */ - default boolean isConnected(Direction side) { - return PipeBlockEntity.isConnected(getConnections(), side); - } - - void setConnection(Direction side, boolean connected, boolean fromNeighbor); - - // if a face is blocked it will still render as connected, but it won't be able to receive stuff from that direction - default boolean canHaveBlockedFaces() { - return true; - } - - int getBlockedConnections(); - - default BlockEntity self() { - return (BlockEntity) this; - } - - default Level getPipeLevel() { - return self().getLevel(); - } - - default BlockPos getPipePos() { - return self().getBlockPos(); - } - - default void markAsDirty() { - self().setChanged(); - } - - default boolean isInValid() { - return self().isRemoved(); - } - - default boolean isRemote() { - var level = getPipeLevel(); - if (level == null) { - return GTCEu.isClientThread(); - } - return level.isClientSide; - } - - @SuppressWarnings("unchecked") - default PipeBlock getPipeBlock() { - return (PipeBlock) self().getBlockState().getBlock(); - } - - @Nullable - default PipeNet getPipeNet() { - if (getPipeLevel() instanceof ServerLevel serverLevel) { - return getPipeBlock().getWorldPipeNet(serverLevel).getNetFromPos(getPipePos()); - } - return null; - } - - default PipeType getPipeType() { - return getPipeBlock().pipeType; - } - - @Nullable - default NodeDataType getNodeData() { - var net = getPipeNet(); - if (net != null) { - return net.getNodeAt(getPipePos()).data; - } - return null; - } - - void notifyBlockUpdate(); - - default void scheduleRenderUpdate() { - var pos = getPipePos(); - var level = getPipeLevel(); - if (level != null) { - var state = level.getBlockState(pos); - if (level.isClientSide) { - level.sendBlockUpdated(pos, state, state, Block.UPDATE_IMMEDIATE); - } else { - level.blockEvent(pos, state.getBlock(), 1, 0); - } - } - } - - default void serverTick() {} - - default void scheduleNeighborShapeUpdate() { - Level level = getPipeLevel(); - BlockPos pos = getPipePos(); - - if (level == null || pos == null) - return; - - level.getBlockState(pos).updateNeighbourShapes(level, pos, Block.UPDATE_ALL); - } - - default BlockEntity getNeighbor(Direction direction) { - return getPipeLevel().getBlockEntity(getPipePos().relative(direction)); - } - - @Override - default int getDefaultPaintingColor() { - return 0xFFFFFF; - } - - @Nullable - Material getFrameMaterial(); -} diff --git a/src/main/java/com/gregtechceu/gtceu/api/pipenet/IPipeType.java b/src/main/java/com/gregtechceu/gtceu/api/pipenet/IPipeType.java deleted file mode 100644 index 1ae39649c1..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/api/pipenet/IPipeType.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.gregtechceu.gtceu.api.pipenet; - -import net.minecraft.resources.ResourceLocation; - -public interface IPipeType { - - /** - * the thickness of the pipe. - */ - float getThickness(); - - /** - * modify the node data by the pipe type. - */ - NodeDataType modifyProperties(NodeDataType baseProperties); - - /** - * can the pipe be painted as other color. - */ - boolean isPaintable(); - - /** - * indicate a unique type id. - */ - ResourceLocation type(); -} diff --git a/src/main/java/com/gregtechceu/gtceu/api/pipenet/IRoutePath.java b/src/main/java/com/gregtechceu/gtceu/api/pipenet/IRoutePath.java deleted file mode 100644 index 030c5c456c..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/api/pipenet/IRoutePath.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.gregtechceu.gtceu.api.pipenet; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraftforge.common.capabilities.Capability; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public interface IRoutePath { - - @NotNull - BlockPos getTargetPipePos(); - - @NotNull - Direction getTargetFacing(); - - int getDistance(); - - @Nullable - T getHandler(Level world); - - @Nullable - default BlockEntity getTargetBlockEntity(Level level) { - return level.getBlockEntity(getTargetPipePos().relative(getTargetFacing())); - } - - @Nullable - default I getTargetCapability(Capability capability, Level level) { - BlockEntity blockEntity = getTargetBlockEntity(level); - return blockEntity == null ? null : - blockEntity.getCapability(capability, getTargetFacing().getOpposite()).resolve().orElse(null); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/api/pipenet/ITickablePipeNet.java b/src/main/java/com/gregtechceu/gtceu/api/pipenet/ITickablePipeNet.java deleted file mode 100644 index c84dedb39e..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/api/pipenet/ITickablePipeNet.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.gregtechceu.gtceu.api.pipenet; - -/** - * @author KilaBash - * @date 2023/2/28 - * @implNote ITickablePipeNet - */ -public interface ITickablePipeNet { - - void update(); -} diff --git a/src/main/java/com/gregtechceu/gtceu/api/pipenet/LevelPipeNet.java b/src/main/java/com/gregtechceu/gtceu/api/pipenet/LevelPipeNet.java deleted file mode 100644 index 32e78bf31a..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/api/pipenet/LevelPipeNet.java +++ /dev/null @@ -1,150 +0,0 @@ -package com.gregtechceu.gtceu.api.pipenet; - -import com.gregtechceu.gtceu.utils.GTUtil; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.Tag; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.saveddata.SavedData; - -import java.util.*; - -public abstract class LevelPipeNet> extends SavedData { - - private final ServerLevel serverLevel; - protected List pipeNets = new ArrayList<>(); - protected final Map> pipeNetsByChunk = new HashMap<>(); - - public LevelPipeNet(ServerLevel serverLevel) { - this.serverLevel = serverLevel; - } - - public LevelPipeNet(ServerLevel serverLevel, CompoundTag tag) { - this(serverLevel); - this.pipeNets = new ArrayList<>(); - ListTag allEnergyNets = tag.getList("PipeNets", Tag.TAG_COMPOUND); - for (int i = 0; i < allEnergyNets.size(); i++) { - CompoundTag pNetTag = allEnergyNets.getCompound(i); - T pipeNet = createNetInstance(); - pipeNet.deserializeNBT(pNetTag); - addPipeNetSilently(pipeNet); - } - init(); - } - - public ServerLevel getWorld() { - return serverLevel; - } - - protected void init() { - this.pipeNets.forEach(PipeNet::onNodeConnectionsUpdate); - } - - public void addNode(BlockPos nodePos, NodeDataType nodeData, int mark, int openConnections, boolean isActive) { - T myPipeNet = null; - Node node = new Node<>(nodeData, openConnections, mark, isActive); - for (Direction facing : GTUtil.DIRECTIONS) { - BlockPos offsetPos = nodePos.relative(facing); - T pipeNet = getNetFromPos(offsetPos); - Node secondNode = pipeNet == null ? null : pipeNet.getAllNodes().get(offsetPos); - if (pipeNet != null && pipeNet.canAttachNode(nodeData) && - pipeNet.canNodesConnect(secondNode, facing.getOpposite(), node, null)) { - if (myPipeNet == null) { - myPipeNet = pipeNet; - myPipeNet.addNode(nodePos, node); - } else if (myPipeNet != pipeNet) { - myPipeNet.uniteNetworks(pipeNet); - } - } - - } - if (myPipeNet == null) { - myPipeNet = createNetInstance(); - myPipeNet.addNode(nodePos, node); - addPipeNet(myPipeNet); - setDirty(); - } - } - - protected void addPipeNetToChunk(ChunkPos chunkPos, T pipeNet) { - this.pipeNetsByChunk.computeIfAbsent(chunkPos, any -> new ArrayList<>()).add(pipeNet); - } - - protected void removePipeNetFromChunk(ChunkPos chunkPos, T pipeNet) { - List list = this.pipeNetsByChunk.get(chunkPos); - if (list != null) list.remove(pipeNet); - if (list.isEmpty()) this.pipeNetsByChunk.remove(chunkPos); - } - - public void removeNode(BlockPos nodePos) { - T pipeNet = getNetFromPos(nodePos); - if (pipeNet != null) { - pipeNet.removeNode(nodePos); - } - } - - public void updateBlockedConnections(BlockPos nodePos, Direction side, boolean isBlocked) { - T pipeNet = getNetFromPos(nodePos); - if (pipeNet != null) { - pipeNet.updateBlockedConnections(nodePos, side, isBlocked); - pipeNet.onPipeConnectionsUpdate(); - } - } - - public void updateData(BlockPos nodePos, NodeDataType data) { - T pipeNet = getNetFromPos(nodePos); - if (pipeNet != null) { - pipeNet.updateNodeData(nodePos, data); - } - } - - public void updateMark(BlockPos nodePos, int newMark) { - T pipeNet = getNetFromPos(nodePos); - if (pipeNet != null) { - pipeNet.updateMark(nodePos, newMark); - } - } - - public T getNetFromPos(BlockPos blockPos) { - List pipeNetsInChunk = pipeNetsByChunk.getOrDefault(new ChunkPos(blockPos), Collections.emptyList()); - for (T pipeNet : pipeNetsInChunk) { - if (pipeNet.containsNode(blockPos)) - return pipeNet; - } - return null; - } - - protected void addPipeNet(T pipeNet) { - addPipeNetSilently(pipeNet); - } - - protected void addPipeNetSilently(T pipeNet) { - this.pipeNets.add(pipeNet); - pipeNet.getContainedChunks().forEach(chunkPos -> addPipeNetToChunk(chunkPos, pipeNet)); - pipeNet.isValid = true; - } - - protected void removePipeNet(T pipeNet) { - this.pipeNets.remove(pipeNet); - pipeNet.getContainedChunks().forEach(chunkPos -> removePipeNetFromChunk(chunkPos, pipeNet)); - pipeNet.isValid = false; - setDirty(); - } - - protected abstract T createNetInstance(); - - @Override - public CompoundTag save(CompoundTag compound) { - ListTag allPipeNets = new ListTag(); - for (T pipeNet : pipeNets) { - CompoundTag pNetTag = pipeNet.serializeNBT(); - allPipeNets.add(pNetTag); - } - compound.put("PipeNets", allPipeNets); - return compound; - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/api/pipenet/Node.java b/src/main/java/com/gregtechceu/gtceu/api/pipenet/Node.java deleted file mode 100644 index 23e251d85c..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/api/pipenet/Node.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.gregtechceu.gtceu.api.pipenet; - -import net.minecraft.core.Direction; - -/** - * Represents a single node in network of pipes - * It can have blocked connections and be active or not - */ -public final class Node { - - public static final int DEFAULT_MARK = 0; - public static final int ALL_OPENED = 0b111111; - public static final int ALL_CLOSED = 0b000000; - - public NodeDataType data; - /** - * Specifies bitmask of blocked connections - * Node will not connect in blocked direction in any case, - * even if neighbour node mark matches - */ - public int openConnections; - /** - * Specifies mark of this node - * Nodes can connect only if their marks are equal, or if - * one of marks is default one - */ - public int mark; - public boolean isActive; - - public Node(NodeDataType data, int openConnections, int mark, boolean isActive) { - this.data = data; - this.openConnections = openConnections; - this.mark = mark; - this.isActive = isActive; - } - - public boolean isBlocked(Direction facing) { - return (openConnections & 1 << facing.ordinal()) == 0; - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/api/pipenet/PipeCoverContainer.java b/src/main/java/com/gregtechceu/gtceu/api/pipenet/PipeCoverContainer.java deleted file mode 100644 index 4dfec36022..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/api/pipenet/PipeCoverContainer.java +++ /dev/null @@ -1,233 +0,0 @@ -package com.gregtechceu.gtceu.api.pipenet; - -import com.gregtechceu.gtceu.GTCEu; -import com.gregtechceu.gtceu.api.capability.ICoverable; -import com.gregtechceu.gtceu.api.cover.CoverBehavior; -import com.gregtechceu.gtceu.api.cover.CoverDefinition; -import com.gregtechceu.gtceu.api.machine.TickableSubscription; -import com.gregtechceu.gtceu.api.registry.GTRegistries; -import com.gregtechceu.gtceu.api.transfer.fluid.IFluidHandlerModifiable; -import com.gregtechceu.gtceu.common.blockentity.FluidPipeBlockEntity; -import com.gregtechceu.gtceu.common.blockentity.ItemPipeBlockEntity; -import com.gregtechceu.gtceu.utils.GTUtil; - -import com.lowdragmc.lowdraglib.syncdata.IEnhancedManaged; -import com.lowdragmc.lowdraglib.syncdata.annotation.DescSynced; -import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; -import com.lowdragmc.lowdraglib.syncdata.annotation.ReadOnlyManaged; -import com.lowdragmc.lowdraglib.syncdata.annotation.UpdateListener; -import com.lowdragmc.lowdraglib.syncdata.field.FieldManagedStorage; -import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.Level; -import net.minecraftforge.items.IItemHandlerModifiable; -import net.minecraftforge.items.wrapper.EmptyHandler; - -import lombok.Getter; -import org.jetbrains.annotations.Nullable; - -/** - * @author KilaBash - * @date 2023/2/18 - * @implNote PipeCoverContainer - */ - -public class PipeCoverContainer implements ICoverable, IEnhancedManaged { - - public static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder(PipeCoverContainer.class); - @Getter - private final FieldManagedStorage syncStorage = new FieldManagedStorage(this); - private final IPipeNode pipeTile; - - @DescSynced - @Persisted - @UpdateListener(methodName = "onCoverSet") - @ReadOnlyManaged(onDirtyMethod = "onCoverDirty", - serializeMethod = "serializeCoverUid", - deserializeMethod = "deserializeCoverUid") - private CoverBehavior up, down, north, south, west, east; - - public PipeCoverContainer(IPipeNode pipeTile) { - this.pipeTile = pipeTile; - } - - @SuppressWarnings("unused") - private void onCoverSet(CoverBehavior newValue, CoverBehavior oldValue) { - if (newValue != oldValue && (newValue == null || oldValue == null)) { - scheduleRenderUpdate(); - } - } - - @Override - public ManagedFieldHolder getFieldHolder() { - return MANAGED_FIELD_HOLDER; - } - - @Override - public void onChanged() { - var level = getLevel(); - if (level != null && !level.isClientSide && level.getServer() != null) { - level.getServer().execute(this::markDirty); - } - } - - @Override - public Level getLevel() { - return pipeTile.getPipeLevel(); - } - - @Override - public BlockPos getPos() { - return pipeTile.getPipePos(); - } - - @Override - public long getOffsetTimer() { - return pipeTile.getOffsetTimer(); - } - - @Override - public void markDirty() { - pipeTile.markAsDirty(); - } - - @Override - public void notifyBlockUpdate() { - pipeTile.notifyBlockUpdate(); - } - - @Override - public void scheduleRenderUpdate() { - pipeTile.scheduleRenderUpdate(); - } - - @Override - public void scheduleNeighborShapeUpdate() { - pipeTile.scheduleNeighborShapeUpdate(); - } - - @Override - public boolean isInValid() { - return pipeTile.isInValid(); - } - - @Override - public boolean canPlaceCoverOnSide(CoverDefinition definition, Direction side) { - return getCoverAtSide(side) == null; - } - - @Override - public double getCoverPlateThickness() { - float thickness = pipeTile.getPipeType().getThickness(); - // no cover plate for pipes >= 1 block thick - if (thickness >= 1) return 0; - - // If the available space for the cover is less than the regular cover plate thickness, use that - - // need to divide by 2 because thickness is centered on the block, so the space is half on each side of the pipe - return Math.min(1.0 / 16.0, (1.0 - thickness) / 2); - } - - @Override - public Direction getFrontFacing() { - return Direction.NORTH; - } - - @Override - public boolean shouldRenderBackSide() { - return true; - } - - @Nullable - @Override - public TickableSubscription subscribeServerTick(Runnable runnable) { - return pipeTile.subscribeServerTick(runnable); - } - - @Override - public void unsubscribe(@Nullable TickableSubscription current) { - pipeTile.unsubscribe(current); - } - - @Override - public IItemHandlerModifiable getItemHandlerCap(@Nullable Direction side, boolean useCoverCapability) { - if (pipeTile instanceof ItemPipeBlockEntity itemPipe) { - return getLevel() instanceof ServerLevel ? itemPipe.getHandler(side, useCoverCapability) : - (IItemHandlerModifiable) EmptyHandler.INSTANCE; - } else { - return null; - } - } - - @Override - public IFluidHandlerModifiable getFluidHandlerCap(@Nullable Direction side, boolean useCoverCapability) { - if (pipeTile instanceof FluidPipeBlockEntity fluidPipe) { - return fluidPipe.getTankList(side); - } else { - return null; - } - } - - @Override - public CoverBehavior getCoverAtSide(Direction side) { - return switch (side) { - case UP -> up; - case SOUTH -> south; - case WEST -> west; - case DOWN -> down; - case EAST -> east; - case NORTH -> north; - }; - } - - public void setCoverAtSide(@Nullable CoverBehavior coverBehavior, Direction side) { - var previousCover = getCoverAtSide(side); - switch (side) { - case UP -> up = coverBehavior; - case SOUTH -> south = coverBehavior; - case WEST -> west = coverBehavior; - case DOWN -> down = coverBehavior; - case EAST -> east = coverBehavior; - case NORTH -> north = coverBehavior; - } - if (coverBehavior != null) { - coverBehavior.getSyncStorage().markAllDirty(); - if (coverBehavior.canPipePassThrough()) { - pipeTile.setConnection(side, true, false); - } - } else if (previousCover != null && previousCover.canPipePassThrough()) { - pipeTile.setConnection(side, false, false); - } - } - - @SuppressWarnings("unused") - private boolean onCoverDirty(CoverBehavior coverBehavior) { - return coverBehavior != null && (coverBehavior.getSyncStorage().hasDirtySyncFields() || - coverBehavior.getSyncStorage().hasDirtyPersistedFields()); - } - - @SuppressWarnings("unused") - private CompoundTag serializeCoverUid(CoverBehavior coverBehavior) { - var uid = new CompoundTag(); - uid.putString("id", GTRegistries.COVERS.getKey(coverBehavior.coverDefinition).toString()); - uid.putInt("side", coverBehavior.attachedSide.ordinal()); - return uid; - } - - @SuppressWarnings("unused") - private CoverBehavior deserializeCoverUid(CompoundTag uid) { - var definitionId = new ResourceLocation(uid.getString("id")); - var side = GTUtil.DIRECTIONS[uid.getInt("side")]; - var definition = GTRegistries.COVERS.get(definitionId); - if (definition != null) { - return definition.createCoverBehavior(this, side); - } - GTCEu.LOGGER.error("couldn't find cover definition {}", definitionId); - throw new RuntimeException(); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/api/pipenet/PipeNet.java b/src/main/java/com/gregtechceu/gtceu/api/pipenet/PipeNet.java deleted file mode 100644 index 1f6bee43ba..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/api/pipenet/PipeNet.java +++ /dev/null @@ -1,495 +0,0 @@ -package com.gregtechceu.gtceu.api.pipenet; - -import com.gregtechceu.gtceu.utils.GTUtil; - -import com.lowdragmc.lowdraglib.syncdata.ITagSerializable; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.Tag; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.ChunkPos; - -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import it.unimi.dsi.fastutil.objects.Object2IntMap; -import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; - -import java.util.*; -import java.util.Map.Entry; - -public abstract class PipeNet implements ITagSerializable { - - protected final LevelPipeNet> worldData; - private final Map> nodeByBlockPos = new HashMap<>(); - private final Map> unmodifiableNodeByBlockPos = Collections - .unmodifiableMap(nodeByBlockPos); - private final Map ownedChunks = new HashMap<>(); - private long lastUpdate; - boolean isValid = false; - - public PipeNet(LevelPipeNet> Level) { - // noinspection unchecked - this.worldData = (LevelPipeNet>) Level; - } - - public Set getContainedChunks() { - return Collections.unmodifiableSet(ownedChunks.keySet()); - } - - public LevelPipeNet> getWorldData() { - return worldData; - } - - public ServerLevel getLevel() { - return worldData.getWorld(); - } - - public long getLastUpdate() { - return lastUpdate; - } - - public boolean isValid() { - return isValid; - } - - /** - * Is only called when connections changed of nodes. Nodes can ONLY connect to other nodes. - */ - protected void onNodeConnectionsUpdate() { - this.lastUpdate = System.currentTimeMillis(); - } - - /** - * Is only called when Data changed of nodes. - */ - protected void onNodeDataUpdate() {} - - /** - * Is called when any connection of any pipe in the net changes - */ - public void onPipeConnectionsUpdate() {} - - public void onNeighbourUpdate(BlockPos fromPos) {} - - public Map> getAllNodes() { - return unmodifiableNodeByBlockPos; - } - - public Node getNodeAt(BlockPos blockPos) { - return nodeByBlockPos.get(blockPos); - } - - public boolean containsNode(BlockPos blockPos) { - return nodeByBlockPos.containsKey(blockPos); - } - - public boolean isNodeConnectedTo(BlockPos pos, Direction side) { - var nodeFirst = getNodeAt(pos); - if (nodeFirst == null) return false; - var nodeSecond = getNodeAt(pos.relative(side)); - if (nodeSecond == null) return false; - return canNodesConnect(nodeFirst, side, nodeSecond, this); - } - - protected void addNodeSilently(BlockPos nodePos, Node node) { - this.nodeByBlockPos.put(nodePos, node); - checkAddedInChunk(nodePos); - } - - protected void addNode(BlockPos nodePos, Node node) { - addNodeSilently(nodePos, node); - onNodeConnectionsUpdate(); - worldData.setDirty(); - } - - protected Node removeNodeWithoutRebuilding(BlockPos nodePos) { - Node removedNode = this.nodeByBlockPos.remove(nodePos); - ensureRemovedFromChunk(nodePos); - worldData.setDirty(); - return removedNode; - } - - public void removeNode(BlockPos nodePos) { - if (nodeByBlockPos.containsKey(nodePos)) { - Node selfNode = removeNodeWithoutRebuilding(nodePos); - rebuildNetworkOnNodeRemoval(nodePos, selfNode); - } - } - - protected void checkAddedInChunk(BlockPos nodePos) { - ChunkPos chunkPos = new ChunkPos(nodePos); - int newValue = this.ownedChunks.compute(chunkPos, (pos, old) -> (old == null ? 0 : old) + 1); - if (newValue == 1 && isValid()) { - this.worldData.addPipeNetToChunk(chunkPos, this); - } - } - - protected void ensureRemovedFromChunk(BlockPos nodePos) { - ChunkPos chunkPos = new ChunkPos(nodePos); - int newValue = this.ownedChunks.compute(chunkPos, (pos, old) -> old == null ? 0 : old - 1); - if (newValue == 0) { - this.ownedChunks.remove(chunkPos); - if (isValid()) { - this.worldData.removePipeNetFromChunk(chunkPos, this); - } - } - } - - public void updateBlockedConnections(BlockPos nodePos, Direction facing, boolean isBlocked) { - if (!containsNode(nodePos)) { - return; - } - Node selfNode = getNodeAt(nodePos); - if (selfNode.isBlocked(facing) == isBlocked) { - return; - } - - setBlocked(selfNode, facing, isBlocked); - BlockPos offsetPos = nodePos.relative(facing); - PipeNet pipeNetAtOffset = worldData.getNetFromPos(offsetPos); - if (pipeNetAtOffset == null) { - return; - } - // if we are on that side of node too - // and it is blocked now - if (pipeNetAtOffset == this) { - // if side was unblocked, well, there is really nothing changed in this e-net - // if it is blocked now, but was able to connect with neighbour node before, try split networks - if (isBlocked) { - // need to unblock node before doing canNodesConnectCheck - setBlocked(selfNode, facing, false); - if (canNodesConnect(selfNode, facing, getNodeAt(offsetPos), this)) { - // now block again to call findAllConnectedBlocks - setBlocked(selfNode, facing, true); - HashMap> thisENet = findAllConnectedBlocks(nodePos); - if (!getAllNodes().equals(thisENet)) { - // node visibility has changed, split network into 2 - // node that code below is similar to removeNodeInternal, but only for 2 networks, and without - // node removal - PipeNet newPipeNet = worldData.createNetInstance(); - thisENet.keySet().forEach(this::removeNodeWithoutRebuilding); - newPipeNet.transferNodeData(thisENet, this); - worldData.addPipeNet(newPipeNet); - } - } - } - // there is another network on that side - // if this is an unblock, and we can connect with their node, merge them - - } else if (!isBlocked) { - Node neighbourNode = pipeNetAtOffset.getNodeAt(offsetPos); - // check connection availability from both networks - if (canNodesConnect(selfNode, facing, neighbourNode, pipeNetAtOffset) && - pipeNetAtOffset.canNodesConnect(neighbourNode, facing.getOpposite(), selfNode, this)) { - // so, side is unblocked now, and nodes can connect, merge two networks - // our network consumes other one - uniteNetworks(pipeNetAtOffset); - } - } - onNodeConnectionsUpdate(); - worldData.setDirty(); - } - - public void updateNodeData(BlockPos nodePos, NodeDataType data) { - if (containsNode(nodePos)) { - Node selfNode = getNodeAt(nodePos); - selfNode.data = data; - onNodeDataUpdate(); - worldData.setDirty(); - } - } - - public void updateMark(BlockPos nodePos, int newMark) { - if (!containsNode(nodePos)) { - return; - } - HashMap> selfConnectedBlocks = null; - Node selfNode = getNodeAt(nodePos); - int oldMark = selfNode.mark; - selfNode.mark = newMark; - for (Direction facing : GTUtil.DIRECTIONS) { - BlockPos offsetPos = nodePos.relative(facing); - PipeNet otherPipeNet = worldData.getNetFromPos(offsetPos); - Node secondNode = otherPipeNet == null ? null : otherPipeNet.getNodeAt(offsetPos); - if (secondNode == null) - continue; // there is noting here - if (!areNodeBlockedConnectionsCompatible(selfNode, facing, secondNode) || - !areNodesCustomContactable(selfNode.data, secondNode.data, otherPipeNet)) - continue; // if connections aren't compatible, skip them - if (areMarksCompatible(oldMark, secondNode.mark) == areMarksCompatible(newMark, secondNode.mark)) - continue; // if compatibility didn't change, skip it - if (areMarksCompatible(newMark, secondNode.mark)) { - // if marks are compatible now, and offset network is different network, merge them - // if it is same network, just update mask and paths - if (otherPipeNet != this) { - uniteNetworks(otherPipeNet); - } - // marks are incompatible now, and this net is connected with it - } else if (otherPipeNet == this) { - // search connected nodes from newly marked node - // populate self connected blocks lazily only once - if (selfConnectedBlocks == null) { - selfConnectedBlocks = findAllConnectedBlocks(nodePos); - } - if (getAllNodes().equals(selfConnectedBlocks)) { - continue; // if this node is still connected to this network, just continue - } - // otherwise, it is not connected - HashMap> offsetConnectedBlocks = findAllConnectedBlocks(offsetPos); - // if in the result of remarking offset node has separated from main network, - // and it is also separated from current cable too, form new network for it - if (!offsetConnectedBlocks.equals(selfConnectedBlocks)) { - offsetConnectedBlocks.keySet().forEach(this::removeNodeWithoutRebuilding); - PipeNet offsetPipeNet = worldData.createNetInstance(); - offsetPipeNet.transferNodeData(offsetConnectedBlocks, this); - worldData.addPipeNet(offsetPipeNet); - } - } - } - onNodeConnectionsUpdate(); - worldData.setDirty(); - } - - private void setBlocked(Node selfNode, Direction facing, boolean isBlocked) { - if (!isBlocked) { - selfNode.openConnections |= 1 << facing.ordinal(); - } else { - selfNode.openConnections &= ~(1 << facing.ordinal()); - } - } - - public boolean markNodeAsActive(BlockPos nodePos, boolean isActive) { - if (containsNode(nodePos) && getNodeAt(nodePos).isActive != isActive) { - getNodeAt(nodePos).isActive = isActive; - worldData.setDirty(); - onNodeConnectionsUpdate(); - return true; - } - return false; - } - - protected final void uniteNetworks(PipeNet unitedPipeNet) { - Map> allNodes = new HashMap<>(unitedPipeNet.getAllNodes()); - worldData.removePipeNet(unitedPipeNet); - allNodes.keySet().forEach(unitedPipeNet::removeNodeWithoutRebuilding); - transferNodeData(allNodes, unitedPipeNet); - } - - private boolean areNodeBlockedConnectionsCompatible(Node first, Direction firstFacing, - Node second) { - return !first.isBlocked(firstFacing) && !second.isBlocked(firstFacing.getOpposite()); - } - - private boolean areMarksCompatible(int mark1, int mark2) { - return mark1 == mark2 || mark1 == Node.DEFAULT_MARK || mark2 == Node.DEFAULT_MARK; - } - - /** - * Checks if given nodes can connect - * Note that this logic should equal with block connection logic - * for proper work of network - */ - protected final boolean canNodesConnect(Node first, Direction firstFacing, Node second, - PipeNet secondPipeNet) { - return areNodeBlockedConnectionsCompatible(first, firstFacing, second) && - areMarksCompatible(first.mark, second.mark) && - areNodesCustomContactable(first.data, second.data, secondPipeNet); - } - - // we need to search only this network - protected HashMap> findAllConnectedBlocks(BlockPos startPos) { - HashMap> observedSet = new HashMap<>(); - observedSet.put(startPos, getNodeAt(startPos)); - Node firstNode = getNodeAt(startPos); - BlockPos.MutableBlockPos currentPos = startPos.mutable(); - Deque moveStack = new ArrayDeque<>(); - main: - while (true) { - for (Direction facing : GTUtil.DIRECTIONS) { - currentPos.move(facing); - Node secondNode = getNodeAt(currentPos); - // if there is node, and it can connect with previous node, add it to list, and set previous node as - // current - if (secondNode != null && canNodesConnect(firstNode, facing, secondNode, this) && - !observedSet.containsKey(currentPos)) { - observedSet.put(currentPos.immutable(), getNodeAt(currentPos)); - firstNode = secondNode; - moveStack.push(facing.getOpposite()); - continue main; - } else currentPos.move(facing.getOpposite()); - } - if (!moveStack.isEmpty()) { - currentPos.move(moveStack.pop()); - firstNode = getNodeAt(currentPos); - } else break; - } - return observedSet; - } - - // called when node is removed to rebuild network - protected void rebuildNetworkOnNodeRemoval(BlockPos nodePos, Node selfNode) { - int amountOfConnectedSides = 0; - for (Direction facing : GTUtil.DIRECTIONS) { - BlockPos offsetPos = nodePos.relative(facing); - if (containsNode(offsetPos)) - amountOfConnectedSides++; - } - // if we are connected only on one side or not connected at all, we don't need to find connected blocks - // because they are only on on side or doesn't exist at all - // this saves a lot of performance in big networks, which are quite big to depth-first them fastly - if (amountOfConnectedSides >= 2) { - for (Direction facing : GTUtil.DIRECTIONS) { - BlockPos offsetPos = nodePos.relative(facing); - Node secondNode = getNodeAt(offsetPos); - if (secondNode == null || !canNodesConnect(selfNode, facing, secondNode, this)) { - // if there isn't any neighbour node, or it wasn't connected with us, just skip it - continue; - } - HashMap> thisENet = findAllConnectedBlocks(offsetPos); - if (getAllNodes().equals(thisENet)) { - // if cable on some direction contains all nodes of this network - // the network didn't change so keep it as is - break; - } else { - // and use them to create new network with caching active nodes set - PipeNet energyNet = worldData.createNetInstance(); - // remove blocks that aren't connected with this network - thisENet.keySet().forEach(this::removeNodeWithoutRebuilding); - energyNet.transferNodeData(thisENet, this); - worldData.addPipeNet(energyNet); - } - } - } - if (getAllNodes().isEmpty()) { - // if this energy net is empty now, remove it - worldData.removePipeNet(this); - } - onNodeConnectionsUpdate(); - worldData.setDirty(); - } - - protected boolean areNodesCustomContactable(NodeDataType first, NodeDataType second, - PipeNet secondNodePipeNet) { - return true; - } - - protected boolean canAttachNode(NodeDataType nodeData) { - return true; - } - - /** - * Called during network split when one net needs to transfer some of it's nodes to another one - * Use this for diving old net contents according to node amount of new network - * For example, for fluid pipes it would remove amount of fluid contained in old nodes - * from parent network and add it to it's own tank, keeping network contents when old network is split - * Note that it should be called when parent net doesn't have transferredNodes in allNodes already - */ - protected void transferNodeData(Map> transferredNodes, - PipeNet parentNet) { - transferredNodes.forEach(this::addNodeSilently); - onNodeConnectionsUpdate(); - worldData.setDirty(); - } - - /** - * Serializes node data into specified tag compound - * Used for writing persistent node data - */ - protected abstract void writeNodeData(NodeDataType nodeData, CompoundTag tagCompound); - - /** - * Deserializes node data from specified tag compound - * Used for reading persistent node data - */ - protected abstract NodeDataType readNodeData(CompoundTag tagCompound); - - @Override - public CompoundTag serializeNBT() { - CompoundTag compound = new CompoundTag(); - compound.put("Nodes", serializeAllNodeList(nodeByBlockPos)); - return compound; - } - - @Override - public void deserializeNBT(CompoundTag nbt) { - this.nodeByBlockPos.clear(); - this.ownedChunks.clear(); - deserializeAllNodeList(nbt.getCompound("Nodes")); - } - - protected void deserializeAllNodeList(CompoundTag compound) { - ListTag allNodesList = compound.getList("NodeIndexes", Tag.TAG_COMPOUND); - ListTag wirePropertiesList = compound.getList("WireProperties", Tag.TAG_COMPOUND); - Int2ObjectMap readProperties = new Int2ObjectOpenHashMap<>(); - - for (int i = 0; i < wirePropertiesList.size(); i++) { - CompoundTag propertiesTag = wirePropertiesList.getCompound(i); - int wirePropertiesIndex = propertiesTag.getInt("index"); - NodeDataType nodeData = readNodeData(propertiesTag); - readProperties.put(wirePropertiesIndex, nodeData); - } - - for (int i = 0; i < allNodesList.size(); i++) { - CompoundTag nodeTag = allNodesList.getCompound(i); - int x = nodeTag.getInt("x"); - int y = nodeTag.getInt("y"); - int z = nodeTag.getInt("z"); - int wirePropertiesIndex = nodeTag.getInt("index"); - BlockPos blockPos = new BlockPos(x, y, z); - NodeDataType nodeData = readProperties.get(wirePropertiesIndex); - int openConnections = nodeTag.getInt("open"); - int mark = nodeTag.getInt("mark"); - boolean isNodeActive = nodeTag.getBoolean("active"); - addNodeSilently(blockPos, new Node<>(nodeData, openConnections, mark, isNodeActive)); - } - } - - protected CompoundTag serializeAllNodeList(Map> allNodes) { - CompoundTag compound = new CompoundTag(); - ListTag allNodesList = new ListTag(); - ListTag wirePropertiesList = new ListTag(); - Object2IntMap alreadyWritten = new Object2IntOpenHashMap<>(); - int currentIndex = 0; - - for (Entry> entry : allNodes.entrySet()) { - BlockPos nodePos = entry.getKey(); - Node node = entry.getValue(); - CompoundTag nodeTag = new CompoundTag(); - nodeTag.putInt("x", nodePos.getX()); - nodeTag.putInt("y", nodePos.getY()); - nodeTag.putInt("z", nodePos.getZ()); - int wirePropertiesIndex = alreadyWritten.getOrDefault(node.data, -1); - if (wirePropertiesIndex == -1) { - wirePropertiesIndex = currentIndex; - alreadyWritten.put(node.data, wirePropertiesIndex); - currentIndex++; - } - nodeTag.putInt("index", wirePropertiesIndex); - if (node.mark != Node.DEFAULT_MARK) { - nodeTag.putInt("mark", node.mark); - } - if (node.openConnections > 0) { - nodeTag.putInt("open", node.openConnections); - } - if (node.isActive) { - nodeTag.putBoolean("active", true); - } - allNodesList.add(nodeTag); - } - - for (NodeDataType nodeData : alreadyWritten.keySet()) { - int wirePropertiesIndex = alreadyWritten.getInt(nodeData); - CompoundTag propertiesTag = new CompoundTag(); - propertiesTag.putInt("index", wirePropertiesIndex); - writeNodeData(nodeData, propertiesTag); - wirePropertiesList.add(propertiesTag); - } - - compound.put("NodeIndexes", allNodesList); - compound.put("WireProperties", wirePropertiesList); - return compound; - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/api/pipenet/PipeNetWalker.java b/src/main/java/com/gregtechceu/gtceu/api/pipenet/PipeNetWalker.java deleted file mode 100644 index 25bb61229c..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/api/pipenet/PipeNetWalker.java +++ /dev/null @@ -1,248 +0,0 @@ -package com.gregtechceu.gtceu.api.pipenet; - -import com.gregtechceu.gtceu.GTCEu; -import com.gregtechceu.gtceu.utils.GTUtil; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.block.entity.BlockEntity; - -import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; -import lombok.Getter; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.*; - -/** - * This is a helper class to get information about a pipe net - *

- * The walker is written that it will always find the shortest path to any destination - *

- * On the way it can collect information about the pipes and it's neighbours - *

- * After creating a walker simply call {@link #traversePipeNet()} to start walking, then you can just collect the data - *

- * Do not walk a walker more than once - */ -@SuppressWarnings("unused") -public abstract class PipeNetWalker, NodeDataType, Net extends PipeNet> { - - protected PipeNetWalker root; - protected final Net pipeNet; - private Set walked; - protected List> walkers; - protected final BlockPos.MutableBlockPos currentPos; - protected final List nextPipeFacings = new ArrayList<>(5); - protected final List nextPipes = new ArrayList<>(5); - protected T currentPipe; - private Direction from = null; - @Getter - protected int walkedBlocks; - @Getter - protected boolean invalid; - protected boolean running; - @Getter - private boolean failed = false; - - protected PipeNetWalker(Net pipeNet, BlockPos sourcePipe, int walkedBlocks) { - this.pipeNet = pipeNet; - this.walkedBlocks = walkedBlocks; - this.currentPos = sourcePipe.mutable(); - this.root = this; - } - - /** - * Creates a sub walker - * Will be called when a pipe has multiple valid pipes - * - * @param pipeNet pipe net - * @param nextPos next pos to check - * @param walkedBlocks distance from source in blocks - * @return new sub walker - */ - @NotNull - protected abstract PipeNetWalker createSubWalker(Net pipeNet, Direction facingToNextPos, - BlockPos nextPos, int walkedBlocks); - - /** - * Checks the neighbour of the current pos - * - * @param pipePos current pos. Note!! its a mutable pos. - * @param faceToNeighbour face to neighbour - * @param pipeNode pipeNode - * @param neighbourTile the neighboring BlockEntity. Might not be a pipe. - */ - protected void checkNeighbour(T pipeNode, BlockPos pipePos, Direction faceToNeighbour, - @Nullable BlockEntity neighbourTile) {} - - /** - * If the pipe is valid to perform a walk on - * - * @param currentPipe current pipe - * @param neighbourPipe neighbour pipe to check - * @param pipePos current pos (tile.getPipePos() != pipePos) - * @param faceToNeighbour face to pipeTile - * @return if the pipe is valid - */ - protected boolean isValidPipe(T currentPipe, T neighbourPipe, BlockPos pipePos, Direction faceToNeighbour) { - return true; - } - - protected abstract Class getBasePipeClass(); - - /** - * You can increase walking stats here. for example - * - * @param pipeTile current checking pipe - * @param pos current pipe pos - */ - protected abstract void checkPipe(T pipeTile, BlockPos pos); - - /** - * The directions that this net can traverse from this pipe - * - * @return the array of valid Directions - */ - protected Direction[] getSurroundingPipeSides() { - return GTUtil.DIRECTIONS; - } - - /** - * Called when a sub walker is done walking - * - * @param subWalker the finished sub walker - */ - protected void onRemoveSubWalker(PipeNetWalker subWalker) {} - - public void traversePipeNet() { - traversePipeNet(32768); - } - - /** - * Starts walking the pipe net and gathers information. - * - * @param maxWalks max walks to prevent possible stack overflow - * @throws IllegalStateException if the walker already walked - */ - public void traversePipeNet(int maxWalks) { - if (invalid) - throw new IllegalStateException("This walker already walked. Create a new one if you want to walk again"); - root = this; - walked = new ObjectOpenHashSet<>(); - int i = 0; - running = true; - while (running && !walk() && i++ < maxWalks); - running = false; - root.walked.clear(); - if (i >= maxWalks) - GTCEu.LOGGER.warn("The walker reached the maximum amount of walks {}", i); - invalid = true; - } - - private boolean walk() { - if (walkers == null) { - if (!checkPos()) { - this.root.failed = true; - return true; - } - - if (nextPipeFacings.isEmpty()) - return true; - if (nextPipeFacings.size() == 1) { - currentPos.set(nextPipes.get(0).getPipePos()); - currentPipe = nextPipes.get(0); - from = nextPipeFacings.get(0).getOpposite(); - walkedBlocks++; - return !isRunning(); - } - - walkers = new ArrayList<>(); - for (int i = 0; i < nextPipeFacings.size(); i++) { - Direction side = nextPipeFacings.get(i); - PipeNetWalker walker = Objects.requireNonNull( - createSubWalker(pipeNet, side, currentPos.relative(side), walkedBlocks + 1), - "Walker can't be null"); - walker.root = root; - walker.currentPipe = nextPipes.get(i); - walker.from = side.getOpposite(); - walkers.add(walker); - } - } - Iterator> iterator = walkers.iterator(); - while (iterator.hasNext()) { - PipeNetWalker walker = iterator.next(); - if (walker.walk()) { - onRemoveSubWalker(walker); - iterator.remove(); - } - } - - return !isRunning() || walkers.isEmpty(); - } - - private boolean checkPos() { - nextPipeFacings.clear(); - nextPipes.clear(); - if (currentPipe == null) { - BlockEntity thisPipe = getLevel().getBlockEntity(currentPos); - if (!(thisPipe instanceof IPipeNode)) { - GTCEu.LOGGER.error("PipeWalker expected a pipe, but found {} at {}", thisPipe, currentPos); - return false; - } - if (!getBasePipeClass().isAssignableFrom(thisPipe.getClass())) { - return false; - } - currentPipe = (T) thisPipe; - } - T pipeTile = currentPipe; - checkPipe(pipeTile, currentPos); - root.walked.add(pipeTile); - - // check for surrounding pipes and item handlers - for (Direction accessSide : getSurroundingPipeSides()) { - // skip sides reported as blocked by pipe network - if (accessSide == from || !pipeTile.isConnected(accessSide)) - continue; - - BlockEntity tile = pipeTile.getNeighbor(accessSide); - if (tile != null && getBasePipeClass().isAssignableFrom(tile.getClass())) { - T otherPipe = (T) tile; - if (!otherPipe.isConnected(accessSide.getOpposite()) || - otherPipe.isBlocked(accessSide.getOpposite()) || isWalked(otherPipe)) - continue; - if (isValidPipe(pipeTile, otherPipe, currentPos, accessSide)) { - nextPipeFacings.add(accessSide); - nextPipes.add(otherPipe); - continue; - } - } - checkNeighbour(pipeTile, currentPos, accessSide, tile); - } - return true; - } - - protected boolean isWalked(T pipe) { - return root.walked.contains(pipe); - } - - /** - * Will cause the root walker to stop after the next walk - */ - public void stop() { - root.running = false; - } - - public boolean isRunning() { - return root.running; - } - - public ServerLevel getLevel() { - return pipeNet.getLevel(); - } - - public BlockPos getCurrentPos() { - return currentPos.immutable(); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/api/pipenet/TickableLevelPipeNet.java b/src/main/java/com/gregtechceu/gtceu/api/pipenet/TickableLevelPipeNet.java deleted file mode 100644 index d06fb258b8..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/api/pipenet/TickableLevelPipeNet.java +++ /dev/null @@ -1,143 +0,0 @@ -package com.gregtechceu.gtceu.api.pipenet; - -import net.minecraft.nbt.CompoundTag; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.chunk.ChunkAccess; - -import org.apache.commons.lang3.tuple.Pair; - -import java.util.*; -import java.util.stream.Collectors; - -public abstract class TickableLevelPipeNet & ITickablePipeNet> - extends LevelPipeNet { - - private final Map> loadedChunksByPipeNet = new HashMap<>(); - private final Set tickingPipeNets = new HashSet<>(); - private final Set removeLater = new HashSet<>(); - - public TickableLevelPipeNet(ServerLevel serverLevel) { - super(serverLevel); - } - - public TickableLevelPipeNet(ServerLevel serverLevel, CompoundTag tag) { - super(serverLevel, tag); - } - - private boolean isChunkLoaded(ChunkPos chunkPos) { - var worldServer = getWorld(); - if (worldServer == null) return false; - return worldServer.getChunkSource().hasChunk(chunkPos.x, chunkPos.z); - } - - protected abstract int getUpdateRate(); - - public void update() { - if (getWorld().getGameTime() % getUpdateRate() == 0L) { - tickingPipeNets.forEach(ITickablePipeNet::update); - } - if (removeLater.size() > 0) { - removeLater.forEach(tickingPipeNets::remove); - removeLater.clear(); - } - } - - public void onChunkLoaded(ChunkAccess chunk) { - ChunkPos chunkPos = chunk.getPos(); - List pipeNetsInThisChunk = this.pipeNetsByChunk.get(chunkPos); - if (pipeNetsInThisChunk == null) return; - for (T pipeNet : pipeNetsInThisChunk) { - List loadedChunks = getOrCreateChunkListForPipeNet(pipeNet); - if (loadedChunks.isEmpty()) { - this.tickingPipeNets.add(pipeNet); - } - loadedChunks.add(chunkPos); - } - } - - public void onChunkUnloaded(ChunkAccess chunk) { - ChunkPos chunkPos = chunk.getPos(); - List pipeNetsInThisChunk = this.pipeNetsByChunk.get(chunkPos); - if (pipeNetsInThisChunk == null) return; - for (T pipeNet : pipeNetsInThisChunk) { - List loadedChunks = this.loadedChunksByPipeNet.get(pipeNet); - if (loadedChunks != null && loadedChunks.contains(chunkPos)) { - loadedChunks.remove(chunkPos); - if (loadedChunks.isEmpty()) { - removeFromTicking(pipeNet); - } - } - } - } - - @Override - protected void init() { - super.init(); - Map> pipeNetByLoadedChunks = pipeNets.stream() - .map(pipeNet -> Pair.of(pipeNet, getPipeNetLoadedChunks(pipeNet))) - .filter(pair -> !pair.getRight().isEmpty()) - .collect(Collectors.toMap(Pair::getLeft, Pair::getRight)); - if (!pipeNetByLoadedChunks.isEmpty()) { - this.tickingPipeNets.addAll(pipeNetByLoadedChunks.keySet()); - this.loadedChunksByPipeNet.putAll(pipeNetByLoadedChunks); - } - } - - @Override - protected void addPipeNet(T pipeNet) { - super.addPipeNet(pipeNet); - List loadedChunks = getPipeNetLoadedChunks(pipeNet); - if (!loadedChunks.isEmpty()) { - this.loadedChunksByPipeNet.put(pipeNet, loadedChunks); - this.tickingPipeNets.add(pipeNet); - } - } - - private List getPipeNetLoadedChunks(T pipeNet) { - return pipeNet.getContainedChunks().stream() - .filter(this::isChunkLoaded) - .collect(Collectors.toList()); - } - - @Override - protected void removePipeNet(T pipeNet) { - super.removePipeNet(pipeNet); - if (loadedChunksByPipeNet.containsKey(pipeNet)) { - removeFromTicking(pipeNet); - } - } - - private void removeFromTicking(T pipeNet) { - this.loadedChunksByPipeNet.remove(pipeNet); - this.removeLater.add(pipeNet); - } - - private List getOrCreateChunkListForPipeNet(T pipeNet) { - return this.loadedChunksByPipeNet.computeIfAbsent(pipeNet, k -> new ArrayList<>()); - } - - @Override - protected void addPipeNetToChunk(ChunkPos chunkPos, T pipeNet) { - super.addPipeNetToChunk(chunkPos, pipeNet); - if (isChunkLoaded(chunkPos)) { - List loadedChunks = getOrCreateChunkListForPipeNet(pipeNet); - if (loadedChunks.isEmpty()) { - this.tickingPipeNets.add(pipeNet); - } - loadedChunks.add(chunkPos); - } - } - - @Override - protected void removePipeNetFromChunk(ChunkPos chunkPos, T pipeNet) { - super.removePipeNetFromChunk(chunkPos, pipeNet); - List loadedChunks = this.loadedChunksByPipeNet.get(pipeNet); - if (loadedChunks != null && loadedChunks.contains(chunkPos)) { - loadedChunks.remove(chunkPos); - if (loadedChunks.isEmpty()) { - removeFromTicking(pipeNet); - } - } - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/api/pipenet/longdistance/LongDistancePipeType.java b/src/main/java/com/gregtechceu/gtceu/api/pipenet/longdistance/LongDistancePipeType.java index 2983ff3bcb..2410556162 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/pipenet/longdistance/LongDistancePipeType.java +++ b/src/main/java/com/gregtechceu/gtceu/api/pipenet/longdistance/LongDistancePipeType.java @@ -1,7 +1,7 @@ package com.gregtechceu.gtceu.api.pipenet.longdistance; -import com.gregtechceu.gtceu.common.pipelike.fluidpipe.longdistance.LDFluidPipeType; -import com.gregtechceu.gtceu.common.pipelike.item.longdistance.LDItemPipeType; +import com.gregtechceu.gtceu.common.pipelike.longdistance.fluid.LDFluidPipeType; +import com.gregtechceu.gtceu.common.pipelike.longdistance.item.LDItemPipeType; import net.minecraft.core.BlockPos; import net.minecraft.world.level.Level; diff --git a/src/main/java/com/gregtechceu/gtceu/api/transfer/fluid/FluidHandlerList.java b/src/main/java/com/gregtechceu/gtceu/api/transfer/fluid/FluidHandlerList.java index 7c26dd9688..c243fd6eac 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/transfer/fluid/FluidHandlerList.java +++ b/src/main/java/com/gregtechceu/gtceu/api/transfer/fluid/FluidHandlerList.java @@ -10,9 +10,10 @@ import lombok.Setter; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.UnmodifiableView; import java.util.Arrays; -import java.util.List; +import java.util.Collection; import java.util.function.Predicate; public class FluidHandlerList implements IFluidHandlerModifiable, INBTSerializable { @@ -26,7 +27,7 @@ public FluidHandlerList(IFluidHandler... handlers) { this.handlers = handlers; } - public FluidHandlerList(List handlers) { + public FluidHandlerList(Collection handlers) { this.handlers = handlers.toArray(IFluidHandler[]::new); } @@ -192,4 +193,10 @@ public boolean supportsDrain(int tank) { return true; } + + @NotNull + @UnmodifiableView + public Collection getBackingHandlers() { + return Arrays.asList(handlers); + } } diff --git a/src/main/java/com/gregtechceu/gtceu/api/transfer/item/ItemHandlerDelegate.java b/src/main/java/com/gregtechceu/gtceu/api/transfer/item/ItemHandlerDelegate.java index b0d5805987..80edfca167 100644 --- a/src/main/java/com/gregtechceu/gtceu/api/transfer/item/ItemHandlerDelegate.java +++ b/src/main/java/com/gregtechceu/gtceu/api/transfer/item/ItemHandlerDelegate.java @@ -2,23 +2,21 @@ import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.world.item.ItemStack; -import net.minecraftforge.items.IItemHandlerModifiable; +import net.minecraftforge.items.IItemHandler; +import lombok.Setter; import org.jetbrains.annotations.NotNull; import javax.annotation.ParametersAreNonnullByDefault; @MethodsReturnNonnullByDefault @ParametersAreNonnullByDefault -public abstract class ItemHandlerDelegate implements IItemHandlerModifiable { +public abstract class ItemHandlerDelegate implements IItemHandler { - public IItemHandlerModifiable delegate; + @Setter + public IItemHandler delegate; - public ItemHandlerDelegate(IItemHandlerModifiable delegate) { - this.delegate = delegate; - } - - protected void setDelegate(IItemHandlerModifiable delegate) { + public ItemHandlerDelegate(IItemHandler delegate) { this.delegate = delegate; } @@ -37,11 +35,6 @@ public ItemStack getStackInSlot(int slot) { return delegate.getStackInSlot(slot); } - @Override - public void setStackInSlot(int slot, @NotNull ItemStack stack) { - delegate.setStackInSlot(slot, stack); - } - @Override @NotNull public ItemStack insertItem(int slot, @NotNull ItemStack stack, boolean simulate) { diff --git a/src/main/java/com/gregtechceu/gtceu/api/transfer/item/ItemHandlerList.java b/src/main/java/com/gregtechceu/gtceu/api/transfer/item/ItemHandlerList.java new file mode 100644 index 0000000000..34511a7bcd --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/api/transfer/item/ItemHandlerList.java @@ -0,0 +1,132 @@ +package com.gregtechceu.gtceu.api.transfer.item; + +import com.gregtechceu.gtceu.GTCEu; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.common.util.INBTSerializable; +import net.minecraftforge.items.IItemHandler; +import net.minecraftforge.items.IItemHandlerModifiable; + +import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; +import lombok.Setter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.UnmodifiableView; + +import java.util.Collection; +import java.util.function.Predicate; + +public class ItemHandlerList implements IItemHandlerModifiable, INBTSerializable { + + private final Int2ObjectMap handlerBySlotIndex = new Int2ObjectLinkedOpenHashMap<>(); + private final Reference2IntOpenHashMap baseIndexOffset = new Reference2IntOpenHashMap<>(); + + @Setter + protected Predicate filter = fluid -> true; + + public ItemHandlerList(Collection handlers) { + int currentSlotIndex = 0; + for (IItemHandler itemHandler : handlers) { + if (baseIndexOffset.containsKey(itemHandler)) { + throw new IllegalArgumentException("Attempted to add item handler " + itemHandler + " twice"); + } + baseIndexOffset.put(itemHandler, currentSlotIndex); + int slotsCount = itemHandler.getSlots(); + for (int slotIndex = 0; slotIndex < slotsCount; slotIndex++) { + handlerBySlotIndex.put(currentSlotIndex + slotIndex, itemHandler); + } + currentSlotIndex += slotsCount; + } + } + + @Override + public int getSlots() { + return handlerBySlotIndex.size(); + } + + @Override + public void setStackInSlot(int slot, @NotNull ItemStack stack) { + IItemHandler itemHandler = getHandlerBySlot(slot); + if (!(itemHandler instanceof IItemHandlerModifiable)) + throw new UnsupportedOperationException("Handler " + itemHandler + " does not support this method"); + ((IItemHandlerModifiable) itemHandler).setStackInSlot(slot - getOffsetByHandler(itemHandler), stack); + } + + @NotNull + @Override + public ItemStack getStackInSlot(int slot) { + IItemHandler itemHandler = getHandlerBySlot(slot); + return itemHandler.getStackInSlot(slot - getOffsetByHandler(itemHandler)); + } + + @Override + public int getSlotLimit(int slot) { + IItemHandler itemHandler = getHandlerBySlot(slot); + return itemHandler.getSlotLimit(slot - getOffsetByHandler(itemHandler)); + } + + @Override + public boolean isItemValid(int slot, @NotNull ItemStack stack) { + IItemHandler itemHandler = getHandlerBySlot(slot); + return itemHandler.isItemValid(slot - getOffsetByHandler(itemHandler), stack); + } + + @NotNull + @Override + public ItemStack insertItem(int slot, @NotNull ItemStack stack, boolean simulate) { + IItemHandler itemHandler = getHandlerBySlot(slot); + return itemHandler.insertItem(slot - getOffsetByHandler(itemHandler), stack, simulate); + } + + @NotNull + @Override + public ItemStack extractItem(int slot, int amount, boolean simulate) { + IItemHandler itemHandler = getHandlerBySlot(slot); + return itemHandler.extractItem(slot - getOffsetByHandler(itemHandler), amount, simulate); + } + + @Override + public CompoundTag serializeNBT() { + var tag = new CompoundTag(); + var list = new ListTag(); + for (IItemHandler handler : handlerBySlotIndex.values()) { + if (handler instanceof INBTSerializable serializable) { + list.add(serializable.serializeNBT()); + } else { + GTCEu.LOGGER.warn("[ItemHandlerList] internal tank doesn't support serialization"); + } + } + tag.put("slots", list); + tag.putByte("type", list.getElementType()); + return tag; + } + + @Override + public void deserializeNBT(CompoundTag nbt) { + var list = nbt.getList("slots", nbt.getByte("type")); + for (int i = 0; i < list.size(); i++) { + if (getHandlerBySlot(i) instanceof INBTSerializable serializable) { + serializable.deserializeNBT(list.get(i)); + } else { + GTCEu.LOGGER.warn("[ItemHandlerList] internal tank doesn't support serialization"); + } + } + } + + @NotNull + @UnmodifiableView + public Collection getBackingHandlers() { + return handlerBySlotIndex.values(); + } + + public IItemHandler getHandlerBySlot(int slot) { + return handlerBySlotIndex.get(slot); + } + + public int getOffsetByHandler(IItemHandler handler) { + return baseIndexOffset.getInt(handler); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/ClientProxy.java b/src/main/java/com/gregtechceu/gtceu/client/ClientProxy.java index c4219eda04..cffd8c7d06 100644 --- a/src/main/java/com/gregtechceu/gtceu/client/ClientProxy.java +++ b/src/main/java/com/gregtechceu/gtceu/client/ClientProxy.java @@ -8,6 +8,7 @@ import com.gregtechceu.gtceu.client.particle.HazardParticle; import com.gregtechceu.gtceu.client.renderer.entity.GTBoatRenderer; import com.gregtechceu.gtceu.client.renderer.entity.GTExplosiveRenderer; +import com.gregtechceu.gtceu.client.renderer.pipe.*; import com.gregtechceu.gtceu.common.CommonProxy; import com.gregtechceu.gtceu.common.data.GTBlockEntities; import com.gregtechceu.gtceu.common.data.GTEntityTypes; @@ -28,16 +29,17 @@ import net.minecraft.client.renderer.blockentity.SignRenderer; import net.minecraft.client.renderer.entity.ThrownItemRenderer; import net.minecraft.resources.ResourceLocation; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.client.ForgeHooksClient; -import net.minecraftforge.client.event.EntityRenderersEvent; -import net.minecraftforge.client.event.RegisterGuiOverlaysEvent; -import net.minecraftforge.client.event.RegisterKeyMappingsEvent; -import net.minecraftforge.client.event.RegisterParticleProvidersEvent; +import net.minecraftforge.client.event.*; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; +import lombok.Getter; +import lombok.Setter; /** * @author KilaBash @@ -50,11 +52,16 @@ public class ClientProxy extends CommonProxy { public static final BiMap CLIENT_FLUID_VEINS = HashBiMap.create(); public static final BiMap CLIENT_BEDROCK_ORE_VEINS = HashBiMap.create(); + @Getter + @Setter + private static long serverTickCount = -1L; + public ClientProxy() { super(); init(); } + @OnlyIn(Dist.CLIENT) public static void init() { if (!GTCEu.isDataGen()) { ClientCacheManager.registerClientCache(GTClientCache.instance, "gtceu"); @@ -63,6 +70,7 @@ public static void init() { } } + @OnlyIn(Dist.CLIENT) @SubscribeEvent public void onRegisterEntityRenderers(EntityRenderersEvent.RegisterRenderers event) { event.registerEntityRenderer(GTEntityTypes.DYNAMITE.get(), ThrownItemRenderer::new); @@ -82,21 +90,43 @@ public void onRegisterEntityRenderers(EntityRenderersEvent.RegisterRenderers eve } } + @OnlyIn(Dist.CLIENT) @SubscribeEvent public void registerKeyBindings(RegisterKeyMappingsEvent event) { KeyBind.onRegisterKeyBinds(event); } + @OnlyIn(Dist.CLIENT) @SubscribeEvent public void onRegisterGuiOverlays(RegisterGuiOverlaysEvent event) { event.registerAboveAll("hud", new HudGuiOverlay()); } + @OnlyIn(Dist.CLIENT) @SubscribeEvent public void onRegisterParticleProviders(RegisterParticleProvidersEvent event) { event.registerSpriteSet(GTParticleTypes.HAZARD_PARTICLE.get(), HazardParticle.Provider::new); } + @OnlyIn(Dist.CLIENT) + @SubscribeEvent + public void modifyModels(ModelEvent.ModifyBakingResult event) { + AbstractPipeModel.invalidateCaches(); + PipeModelRegistry.registerModels(event.getModels()::put); + } + + @OnlyIn(Dist.CLIENT) + @SubscribeEvent + public void registerAdditionalModels(ModelEvent.RegisterAdditional event) { + PipeModelRegistry.registerModels((id, $) -> event.register(id)); + } + + @OnlyIn(Dist.CLIENT) + @SubscribeEvent + public void modelRegistry(final ModelEvent.RegisterGeometryLoaders e) { + e.register("pipe", UnbakedPipeModel.Loader.INSTANCE); + } + @SubscribeEvent public void onClientSetup(FMLClientSetupEvent event) { if (ConfigHolder.INSTANCE.compat.minimap.toggle.ftbChunksIntegration && diff --git a/src/main/java/com/gregtechceu/gtceu/client/forge/ForgeClientEventListener.java b/src/main/java/com/gregtechceu/gtceu/client/forge/ForgeClientEventListener.java index ca0cc1da45..1889635f8f 100644 --- a/src/main/java/com/gregtechceu/gtceu/client/forge/ForgeClientEventListener.java +++ b/src/main/java/com/gregtechceu/gtceu/client/forge/ForgeClientEventListener.java @@ -2,6 +2,7 @@ import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.GTValues; +import com.gregtechceu.gtceu.client.ClientProxy; import com.gregtechceu.gtceu.client.EnvironmentalHazardClientHandler; import com.gregtechceu.gtceu.client.TooltipsHandler; import com.gregtechceu.gtceu.client.renderer.BlockHighlightRenderer; @@ -56,6 +57,8 @@ public static void onClientTickEvent(TickEvent.ClientTickEvent event) { MultiblockInWorldPreviewRenderer.onClientTick(); EnvironmentalHazardClientHandler.INSTANCE.onClientTick(); GTValues.CLIENT_TIME++; + } else { + ClientProxy.setServerTickCount(ClientProxy.getServerTickCount() + 1); } } diff --git a/src/main/java/com/gregtechceu/gtceu/client/model/PipeModel.java b/src/main/java/com/gregtechceu/gtceu/client/model/PipeModel.java deleted file mode 100644 index 51b44a9cea..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/client/model/PipeModel.java +++ /dev/null @@ -1,356 +0,0 @@ -package com.gregtechceu.gtceu.client.model; - -import com.gregtechceu.gtceu.GTCEu; -import com.gregtechceu.gtceu.api.blockentity.PipeBlockEntity; -import com.gregtechceu.gtceu.utils.GTUtil; -import com.gregtechceu.gtceu.utils.SupplierMemoizer; - -import com.lowdragmc.lowdraglib.client.bakedpipeline.FaceQuad; -import com.lowdragmc.lowdraglib.client.model.ModelFactory; -import com.lowdragmc.lowdraglib.client.renderer.IItemRendererProvider; - -import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.MultiBufferSource; -import net.minecraft.client.renderer.block.model.BakedQuad; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.resources.model.BakedModel; -import net.minecraft.core.Direction; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.ItemDisplayContext; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.phys.AABB; -import net.minecraft.world.phys.shapes.Shapes; -import net.minecraft.world.phys.shapes.VoxelShape; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; - -import com.mojang.blaze3d.vertex.PoseStack; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import lombok.Setter; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; - -/** - * @author KilaBash - * @date 2023/3/1 - * @implNote PipeModel - */ -public class PipeModel { - - public static final ResourceLocation PIPE_BLOCKED_OVERLAY = GTCEu.id("block/pipe/blocked/pipe_blocked"); - public static final ResourceLocation PIPE_BLOCKED_OVERLAY_UP = GTCEu.id("block/pipe/blocked/pipe_blocked_up"); - public static final ResourceLocation PIPE_BLOCKED_OVERLAY_DOWN = GTCEu.id("block/pipe/blocked/pipe_blocked_down"); - public static final ResourceLocation PIPE_BLOCKED_OVERLAY_LEFT = GTCEu.id("block/pipe/blocked/pipe_blocked_left"); - public static final ResourceLocation PIPE_BLOCKED_OVERLAY_RIGHT = GTCEu.id("block/pipe/blocked/pipe_blocked_right"); - public static final ResourceLocation PIPE_BLOCKED_OVERLAY_NU = GTCEu.id("block/pipe/blocked/pipe_blocked_nu"); - public static final ResourceLocation PIPE_BLOCKED_OVERLAY_ND = GTCEu.id("block/pipe/blocked/pipe_blocked_nd"); - public static final ResourceLocation PIPE_BLOCKED_OVERLAY_NL = GTCEu.id("block/pipe/blocked/pipe_blocked_nl"); - public static final ResourceLocation PIPE_BLOCKED_OVERLAY_NR = GTCEu.id("block/pipe/blocked/pipe_blocked_nr"); - public static final ResourceLocation PIPE_BLOCKED_OVERLAY_UD = GTCEu.id("block/pipe/blocked/pipe_blocked_ud"); - public static final ResourceLocation PIPE_BLOCKED_OVERLAY_UL = GTCEu.id("block/pipe/blocked/pipe_blocked_ul"); - public static final ResourceLocation PIPE_BLOCKED_OVERLAY_UR = GTCEu.id("block/pipe/blocked/pipe_blocked_ur"); - public static final ResourceLocation PIPE_BLOCKED_OVERLAY_DL = GTCEu.id("block/pipe/blocked/pipe_blocked_dl"); - public static final ResourceLocation PIPE_BLOCKED_OVERLAY_DR = GTCEu.id("block/pipe/blocked/pipe_blocked_dr"); - public static final ResourceLocation PIPE_BLOCKED_OVERLAY_LR = GTCEu.id("block/pipe/blocked/pipe_blocked_lr"); - private static final EnumMap> FACE_BORDER_MAP = new EnumMap<>( - Direction.class); - private static final Int2ObjectMap RESTRICTOR_MAP = new Int2ObjectOpenHashMap<>(); - private static boolean isRestrictorInitialized; - - public static void initializeRestrictor(Function atlas) { - addRestrictor(atlas.apply(PIPE_BLOCKED_OVERLAY_UP), Border.TOP); - addRestrictor(atlas.apply(PIPE_BLOCKED_OVERLAY_DOWN), Border.BOTTOM); - addRestrictor(atlas.apply(PIPE_BLOCKED_OVERLAY_UD), Border.TOP, Border.BOTTOM); - addRestrictor(atlas.apply(PIPE_BLOCKED_OVERLAY_LEFT), Border.LEFT); - addRestrictor(atlas.apply(PIPE_BLOCKED_OVERLAY_UL), Border.TOP, Border.LEFT); - addRestrictor(atlas.apply(PIPE_BLOCKED_OVERLAY_DL), Border.BOTTOM, Border.LEFT); - addRestrictor(atlas.apply(PIPE_BLOCKED_OVERLAY_NR), Border.TOP, Border.BOTTOM, Border.LEFT); - addRestrictor(atlas.apply(PIPE_BLOCKED_OVERLAY_RIGHT), Border.RIGHT); - addRestrictor(atlas.apply(PIPE_BLOCKED_OVERLAY_UR), Border.TOP, Border.RIGHT); - addRestrictor(atlas.apply(PIPE_BLOCKED_OVERLAY_DR), Border.BOTTOM, Border.RIGHT); - addRestrictor(atlas.apply(PIPE_BLOCKED_OVERLAY_NL), Border.TOP, Border.BOTTOM, Border.RIGHT); - addRestrictor(atlas.apply(PIPE_BLOCKED_OVERLAY_LR), Border.LEFT, Border.RIGHT); - addRestrictor(atlas.apply(PIPE_BLOCKED_OVERLAY_ND), Border.TOP, Border.LEFT, Border.RIGHT); - addRestrictor(atlas.apply(PIPE_BLOCKED_OVERLAY_NU), Border.BOTTOM, Border.LEFT, Border.RIGHT); - addRestrictor(atlas.apply(PIPE_BLOCKED_OVERLAY), Border.TOP, Border.BOTTOM, Border.LEFT, Border.RIGHT); - } - - static { - FACE_BORDER_MAP.put(Direction.DOWN, - borderMap(Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST)); - FACE_BORDER_MAP.put(Direction.UP, - borderMap(Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST)); - FACE_BORDER_MAP.put(Direction.NORTH, - borderMap(Direction.DOWN, Direction.UP, Direction.WEST, Direction.EAST)); - FACE_BORDER_MAP.put(Direction.SOUTH, - borderMap(Direction.DOWN, Direction.UP, Direction.WEST, Direction.EAST)); - FACE_BORDER_MAP.put(Direction.WEST, - borderMap(Direction.DOWN, Direction.UP, Direction.NORTH, Direction.SOUTH)); - FACE_BORDER_MAP.put(Direction.EAST, - borderMap(Direction.DOWN, Direction.UP, Direction.NORTH, Direction.SOUTH)); - } - - public final static int ITEM_CONNECTIONS = 0b001100; - public final float thickness; - public final AABB coreCube; - public final Map sideCubes; - - public SupplierMemoizer.MemoizedSupplier sideTexture, endTexture; - @Nullable - public SupplierMemoizer.MemoizedSupplier<@Nullable ResourceLocation> secondarySideTexture, secondaryEndTexture; - @Setter - public ResourceLocation sideOverlayTexture, endOverlayTexture; - - @OnlyIn(Dist.CLIENT) - TextureAtlasSprite sideSprite, endSprite, secondarySideSprite, secondaryEndSprite, sideOverlaySprite, - endOverlaySprite; - - public PipeModel(float thickness, Supplier sideTexture, Supplier endTexture, - @Nullable Supplier<@Nullable ResourceLocation> secondarySideTexture, - @Nullable Supplier<@Nullable ResourceLocation> secondaryEndTexture) { - this.sideTexture = SupplierMemoizer.memoize(sideTexture); - this.endTexture = SupplierMemoizer.memoize(endTexture); - this.secondarySideTexture = secondarySideTexture != null ? SupplierMemoizer.memoize(secondarySideTexture) : - null; - this.secondaryEndTexture = secondaryEndTexture != null ? SupplierMemoizer.memoize(secondaryEndTexture) : null; - this.thickness = thickness; - double min = (1d - thickness) / 2; - double max = min + thickness; - this.coreCube = new AABB(min, min, min, max, max, max); - this.sideCubes = new EnumMap<>(Direction.class); - for (Direction side : GTUtil.DIRECTIONS) { - var normal = side.getNormal(); - sideCubes.put(side, new AABB( - normal.getX() == 0 ? min : normal.getX() > 0 ? max : 0, - normal.getY() == 0 ? min : normal.getY() > 0 ? max : 0, - normal.getZ() == 0 ? min : normal.getZ() > 0 ? max : 0, - normal.getX() == 0 ? max : normal.getX() > 0 ? 1 : min, - normal.getY() == 0 ? max : normal.getY() > 0 ? 1 : min, - normal.getZ() == 0 ? max : normal.getZ() > 0 ? 1 : min)); - } - } - - public VoxelShape getShapes(int connections) { - var shapes = new ArrayList(7); - shapes.add(Shapes.create(coreCube)); - for (Direction side : GTUtil.DIRECTIONS) { - if (PipeBlockEntity.isConnected(connections, side)) { - shapes.add(Shapes.create(sideCubes.get(side))); - } - } - return shapes.stream().reduce(Shapes.empty(), Shapes::or); - } - - @OnlyIn(Dist.CLIENT) - public List bakeQuads(@Nullable Direction side, int connections, int blockedConnections) { - if (!isRestrictorInitialized) { - initializeRestrictor(ModelFactory::getBlockSprite); - isRestrictorInitialized = true; - } - if (sideSprite == null) { - sideSprite = ModelFactory.getBlockSprite(sideTexture.get()); - } - if (endSprite == null) { - endSprite = ModelFactory.getBlockSprite(endTexture.get()); - } - if (secondarySideTexture != null && secondarySideTexture.get() != null && secondarySideSprite == null) { - secondarySideSprite = ModelFactory.getBlockSprite(secondarySideTexture.get()); - } - if (secondaryEndTexture != null && secondaryEndTexture.get() != null && secondaryEndSprite == null) { - secondaryEndSprite = ModelFactory.getBlockSprite(secondaryEndTexture.get()); - } - if (sideOverlayTexture != null && sideOverlaySprite == null) { - sideOverlaySprite = ModelFactory.getBlockSprite(sideOverlayTexture); - } - if (endOverlayTexture != null && endOverlaySprite == null) { - endOverlaySprite = ModelFactory.getBlockSprite(endOverlayTexture); - } - - if (side != null) { - if (thickness == 1) { // full block - List quads = new ArrayList<>(); - quads.add(FaceQuad.builder(side, sideSprite).cube(coreCube).cubeUV().tintIndex(0).bake()); - if (secondarySideSprite != null) { - quads.add(FaceQuad.builder(side, secondarySideSprite).cube(coreCube).cubeUV().tintIndex(0).bake()); - } - return quads; - } - - if (PipeBlockEntity.isConnected(connections, side)) { // side connected - List quads = new ArrayList<>(); - quads.add(FaceQuad.builder(side, endSprite).cube(sideCubes.get(side).inflate(-0.001)).cubeUV() - .tintIndex(1).bake()); - if (secondaryEndSprite != null) { - quads.add(FaceQuad.builder(side, secondaryEndSprite).cube(sideCubes.get(side)).cubeUV().tintIndex(1) - .bake()); - } - if (endOverlaySprite != null) { - quads.add(FaceQuad.builder(side, endOverlaySprite).cube(sideCubes.get(side)).cubeUV().tintIndex(0) - .bake()); - } - if (sideOverlaySprite != null) { - for (Direction face : GTUtil.DIRECTIONS) { - if (face.getAxis() != side.getAxis()) { - quads.add(FaceQuad.builder(face, sideOverlaySprite).cube(sideCubes.get(side)).cubeUV() - .tintIndex(2).bake()); - } - } - } - int borderMask = computeBorderMask(blockedConnections, connections, side); - if (borderMask != 0) { - quads.add(FaceQuad.builder(side, RESTRICTOR_MAP.get(borderMask)).cube(sideCubes.get(side)).cubeUV() - .bake()); - } - return quads; - } - - return Collections.emptyList(); - } - - List quads = new LinkedList<>(); - if (thickness < 1) { // non full block - // render core cube - for (Direction face : GTUtil.DIRECTIONS) { - if (!PipeBlockEntity.isConnected(connections, face)) { - quads.add(FaceQuad.builder(face, sideSprite).cube(coreCube).cubeUV().tintIndex(0).bake()); - if (secondarySideSprite != null) { - quads.add(FaceQuad.builder(face, secondarySideSprite).cube(coreCube).cubeUV().tintIndex(0) - .bake()); - } - } - // render each connected side - for (Direction facing : GTUtil.DIRECTIONS) { - if (facing.getAxis() != face.getAxis()) { - if (PipeBlockEntity.isConnected(connections, facing)) { - quads.add(FaceQuad.builder(face, sideSprite).cube(sideCubes.get(facing)).cubeUV() - .tintIndex(0).bake()); - if (secondarySideSprite != null) { - quads.add(FaceQuad.builder(face, secondarySideSprite).cube(sideCubes.get(facing)) - .cubeUV().tintIndex(0).bake()); - } - if (sideOverlaySprite != null) { - quads.add(FaceQuad.builder(face, sideOverlaySprite) - .cube(sideCubes.get(facing).inflate(0.001)).cubeUV().tintIndex(2).bake()); - } - int borderMask = computeBorderMask(blockedConnections, connections, face); - if (borderMask != 0) { - quads.add(FaceQuad.builder(face, RESTRICTOR_MAP.get(borderMask)) - .cube(sideCubes.get(facing)).cubeUV().bake()); - } - } - } - } - } - } - return quads; - } - - @NotNull - @OnlyIn(Dist.CLIENT) - public TextureAtlasSprite getParticleTexture() { - if (sideSprite == null) { - sideSprite = ModelFactory.getBlockSprite(sideTexture.get()); - } - return sideSprite; - } - - private final Map, List> itemModelCache = new ConcurrentHashMap<>(); - - @OnlyIn(Dist.CLIENT) - public void renderItem(ItemStack stack, ItemDisplayContext transformType, boolean leftHand, PoseStack matrixStack, - MultiBufferSource buffer, int combinedLight, int combinedOverlay, BakedModel model) { - IItemRendererProvider.disabled.set(true); - Minecraft.getInstance().getItemRenderer().render(stack, transformType, leftHand, matrixStack, buffer, - combinedLight, combinedOverlay, - (ItemBakedModel) (state, direction, random) -> itemModelCache.computeIfAbsent( - Optional.ofNullable(direction), - direction1 -> bakeQuads(direction1.orElse(null), ITEM_CONNECTIONS, 0))); - IItemRendererProvider.disabled.set(false); - } - - @OnlyIn(Dist.CLIENT) - public void registerTextureAtlas(Consumer register) { - itemModelCache.clear(); - sideTexture.forget(); - register.accept(sideTexture.get()); - endTexture.forget(); - register.accept(endTexture.get()); - if (secondarySideTexture != null) { - secondarySideTexture.forget(); - if (secondarySideTexture.get() != null) { - register.accept(secondarySideTexture.get()); - } - } - if (secondaryEndTexture != null) { - secondaryEndTexture.forget(); - if (secondaryEndTexture.get() != null) { - register.accept(secondaryEndTexture.get()); - } - } - if (sideOverlayTexture != null) register.accept(sideOverlayTexture); - if (endOverlayTexture != null) register.accept(endOverlayTexture); - sideSprite = null; - endSprite = null; - endOverlaySprite = null; - } - - private static EnumMap borderMap(Direction topSide, Direction bottomSide, Direction leftSide, - Direction rightSide) { - EnumMap sideMap = new EnumMap<>(Border.class); - sideMap.put(Border.TOP, topSide); - sideMap.put(Border.BOTTOM, bottomSide); - sideMap.put(Border.LEFT, leftSide); - sideMap.put(Border.RIGHT, rightSide); - return sideMap; - } - - private static void addRestrictor(TextureAtlasSprite sprite, Border... borders) { - int mask = 0; - for (Border border : borders) { - mask |= border.mask; - } - RESTRICTOR_MAP.put(mask, sprite); - } - - protected static Direction getSideAtBorder(Direction side, Border border) { - return FACE_BORDER_MAP.get(side).get(border); - } - - protected static int computeBorderMask(int blockedConnections, int connections, Direction side) { - int borderMask = 0; - if (blockedConnections != 0) { - for (Border border : Border.VALUES) { - Direction borderSide = getSideAtBorder(side, border); - if (PipeBlockEntity.isFaceBlocked(blockedConnections, borderSide) && - PipeBlockEntity.isConnected(connections, borderSide)) { - // only render when the side is blocked *and* connected - borderMask |= border.mask; - } - } - } - return borderMask; - } - - public enum Border { - - TOP, - BOTTOM, - LEFT, - RIGHT; - - public static final Border[] VALUES = values(); - - public final int mask; - - Border() { - mask = 1 << this.ordinal(); - } - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/client/particle/GTOverheatParticle.java b/src/main/java/com/gregtechceu/gtceu/client/particle/GTOverheatParticle.java new file mode 100644 index 0000000000..35afda5a13 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/particle/GTOverheatParticle.java @@ -0,0 +1,297 @@ +package com.gregtechceu.gtceu.client.particle; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.GTValues; +import com.gregtechceu.gtceu.api.graphnet.pipenet.logic.TemperatureLogic; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; +import com.gregtechceu.gtceu.client.ClientProxy; +import com.gregtechceu.gtceu.client.renderer.GTRenderTypes; +import com.gregtechceu.gtceu.client.renderer.IRenderSetup; +import com.gregtechceu.gtceu.client.util.BloomUtils; +import com.gregtechceu.gtceu.client.util.DrawUtil; +import com.gregtechceu.gtceu.client.util.EffectRenderContext; +import com.gregtechceu.gtceu.client.util.RenderBufferHelper; + +import com.lowdragmc.shimmer.client.shader.RenderUtils; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.shapes.Shapes; +import net.minecraft.world.phys.shapes.VoxelShape; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.*; +import lombok.Setter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * @author brachy84 + */ +public class GTOverheatParticle extends GTParticle { + + public static final int TEMPERATURE_CUTOFF = 400; + + /** + * Source + */ + private static final int[] blackBodyColors = { + 0xFF3300, // 1000 K + 0xFF5300, // 1200 K + 0xFF6500, // 1400 K + 0xFF7300, // 1600 K + 0xFF7E00, // 1800 K + 0xFF8912, // 2000 K + 0xFF932C, // 2200 K + 0xFF9D3F, // 2400 K + 0xffa54f, // 2600 K + 0xffad5e, // 2800 K + 0xffb46b, // 3000 K + 0xffbb78, // 3200 K + 0xffc184, // 3400 K + 0xffc78f, // 3600 K + 0xffcc99, // 3800 K + 0xffd1a3, // 4000 K + 0xffd5ad, // 4200 K + 0xffd9b6, // 4400 K + 0xffddbe, // 4600 K + 0xffe1c6, // 4800 K + 0xffe4ce, // 5000 K + 0xffe8d5, // 5200 K + 0xffebdc, // 5400 K + 0xffeee3, // 5600 K + 0xfff0e9, // 5800 K + 0xfff3ef, // 6000 K + 0xfff5f5, // 6200 K + 0xfff8fb, // 6400 K + 0xfef9ff, // 6600 K + 0xf9f6ff, // 6800 K + 0xf5f3ff, // 7000 K + 0xf0f1ff, // 7200 K + 0xedefff, // 7400 K + 0xe9edff, // 7600 K + 0xe6ebff, // 7800 K + 0xe3e9ff, // 8000 K + 0xe0e7ff, // 8200 K + 0xdde6ff, // 8400 K + 0xdae4ff, // 8600 K + 0xd8e3ff, // 8800 K + 0xd6e1ff, // 9000 K + 0xd3e0ff, // 9200 K + 0xd1dfff, // 9400 K + 0xcfddff, // 9600 K + 0xcedcff, // 9800 K + 0xccdbff, // 10000 K + 0xcadaff, // 10200 K + 0xc9d9ff, // 10400 K + 0xc7d8ff, // 10600 K + 0xc6d8ff, // 10800 K + 0xc4d7ff, // 11000 K + 0xc3d6ff, // 11200 K + 0xc2d5ff, // 11400 K + 0xc1d4ff, // 11600 K + 0xc0d4ff, // 11800 K + 0xbfd3ff, // 12000 K + 0xbed2ff, // 12200 K + 0xbdd2ff, // 12400 K + 0xbcd1ff, // 12600 K + 0xbbd1ff, // 12800 K + 0xbad0ff, // 13000 K + 0xb9d0ff, // 13200 K + 0xb8cfff, // 13400 K + 0xb7cfff, // 13600 K + 0xb7ceff, // 13800 K + 0xb6ceff, // 14000 K + 0xb5cdff, // 14200 K + 0xb5cdff, // 14400 K + 0xb4ccff, // 14600 K + 0xb3ccff, // 14800 K + 0xb3ccff, // 15000 K + 0xb2cbff, // 15200 K + 0xb2cbff, // 15400 K + 0xb1caff, // 15600 K + 0xb1caff, // 15800 K + 0xb0caff, // 16000 K + 0xafc9ff, // 16200 K + 0xafc9ff, // 16400 K + 0xafc9ff, // 16600 K + 0xaec9ff, // 16800 K + 0xaec8ff, // 17000 K + 0xadc8ff, // 17200 K + 0xadc8ff, // 17400 K + 0xacc7ff, // 17600 K + 0xacc7ff, // 17800 K + 0xacc7ff, // 18000 K + 0xabc7ff, // 18200 K + 0xabc6ff, // 18400 K + 0xaac6ff, // 18600 K + 0xaac6ff, // 18800 K + 0xaac6ff, // 19000 K + 0xa9c6ff, // 19200 K + 0xa9c5ff, // 19400 K + 0xa9c5ff, // 19600 K + 0xa9c5ff, // 19800 K + 0xa8c5ff, // 20000 K + // color doesn't really change onwards + }; + + public static int getBlackBodyColor(int temperature) { + if (temperature < 1000) + return blackBodyColors[0]; + int index = (temperature - 1000) / 200; + if (index >= blackBodyColors.length - 1) + return blackBodyColors[blackBodyColors.length - 1]; + int color = blackBodyColors[index]; + return DrawUtil.interpolateColor(color, blackBodyColors[index + 1], temperature % 200 / 200f); + } + + private final PipeBlockEntity tileEntity; + @Setter + private @NotNull TemperatureLogic temperatureLogic; + + protected VoxelShape pipeBoxes; + protected boolean insulated; + + protected float alpha = 0; + protected int color = blackBodyColors[0]; + + public GTOverheatParticle(@NotNull PipeBlockEntity tileEntity, @NotNull TemperatureLogic temperatureLogic, + @NotNull VoxelShape pipeBoxes, boolean insulated) { + super(tileEntity.getBlockPos().getX(), tileEntity.getBlockPos().getY(), tileEntity.getBlockPos().getZ()); + this.tileEntity = tileEntity; + this.temperatureLogic = temperatureLogic; + this.pipeBoxes = pipeBoxes; + updatePipeBoxes(pipeBoxes); + this.insulated = insulated; + } + + private int getTemperature() { + long tick = ClientProxy.getServerTickCount(); + return temperatureLogic.getTemperature(tick); + } + + public void updatePipeBoxes(@NotNull VoxelShape pipeBoxes) { + List boxes = pipeBoxes.toAabbs(); + this.pipeBoxes = boxes.stream() + .map(aabb -> aabb.inflate(0.001)) + .map(Shapes::create) + .reduce(Shapes.empty(), Shapes::or) + .optimize(); + } + + @Override + public void onUpdate() { + if (tileEntity.isRemoved() || !tileEntity.isOverheatParticleAlive()) { + setExpired(); + tileEntity.killOverheatParticle(); + return; + } + int temperature = getTemperature(); + + if (temperature <= TEMPERATURE_CUTOFF || temperature > temperatureLogic.getTemperatureMaximum()) { + setExpired(); + return; + } + if (temperature < 500) { + alpha = 0f; + } else if (temperature < 1000) { + alpha = (temperature - 500f) / 500f; + alpha *= 0.8f; + } else { + alpha = 0.8f; + } + color = getBlackBodyColor(temperature); + + if (GTValues.RNG.nextFloat() < 0.04) { + spawnSmoke(); + } + } + + private void spawnSmoke() { + BlockPos pos = tileEntity.getBlockPos(); + float xPos = pos.getX() + 0.5F; + float yPos = pos.getY() + 0.9F; + float zPos = pos.getZ() + 0.5F; + + float ySpd = 0.3F + 0.1F * GTValues.RNG.nextFloat(); + tileEntity.getLevel().addParticle(ParticleTypes.LARGE_SMOKE, xPos, yPos, zPos, 0, ySpd, 0); + } + + @Override + public String toString() { + return "GTOverheatParticle{" + + "tileEntity=" + tileEntity + + ", temperatureLogic=" + temperatureLogic + + ", pipeBoxes=" + pipeBoxes + + ", insulated=" + insulated + + ", alpha=" + alpha + + ", color=" + color + + '}'; + } + + @Nullable + @Override + public IRenderSetup getRenderSetup() { + return SETUP; + } + + @Override + public boolean shouldRender(@NotNull EffectRenderContext context) { + if (this.insulated) return false; + for (AABB cuboid : pipeBoxes.toAabbs()) { + if (!context.frustum().isVisible(cuboid.move(posX, posY, posZ))) { + return false; + } + } + return true; + } + + @Override + public void renderParticle(@NotNull PoseStack poseStack, @NotNull BufferBuilder buffer, + @NotNull EffectRenderContext context) { + if (GTCEu.Mods.isShimmerLoaded()) { + final PoseStack finalStack = RenderUtils.copyPoseStack(poseStack); + BloomUtils.entityBloom( + source -> renderBloomEffect(finalStack, source.getBuffer(GTRenderTypes.getBloomQuad()), context)); + } else { + renderBloomEffect(poseStack, buffer, context); + } + } + + public void renderBloomEffect(@NotNull PoseStack poseStack, @NotNull VertexConsumer buffer, + @NotNull EffectRenderContext context) { + float red = ((color >> 16) & 0xFF) / 255f; + float green = ((color >> 8) & 0xFF) / 255f; + float blue = (color & 0xFF) / 255f; + + poseStack.pushPose(); + poseStack.translate(posX - context.cameraX(), posY - context.cameraY(), posZ - context.cameraZ()); + for (AABB cuboid : pipeBoxes.toAabbs()) { + RenderBufferHelper.renderCubeFace(poseStack, buffer, cuboid, red, green, blue, alpha, true); + } + poseStack.popPose(); + } + + private static final IRenderSetup SETUP = new IRenderSetup() { + + @Override + @OnlyIn(Dist.CLIENT) + public void preDraw(@NotNull BufferBuilder buffer) { + RenderSystem.setShaderColor(1, 1, 1, 1); + RenderSystem.enableBlend(); + buffer.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR); + } + + @Override + @OnlyIn(Dist.CLIENT) + public void postDraw(@NotNull BufferBuilder buffer) { + Tesselator.getInstance().end(); + RenderSystem.disableBlend(); + } + }; +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/particle/GTParticle.java b/src/main/java/com/gregtechceu/gtceu/client/particle/GTParticle.java new file mode 100644 index 0000000000..ece35c29e8 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/particle/GTParticle.java @@ -0,0 +1,118 @@ +package com.gregtechceu.gtceu.client.particle; + +import com.gregtechceu.gtceu.client.renderer.IRenderSetup; +import com.gregtechceu.gtceu.client.util.EffectRenderContext; + +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import com.mojang.blaze3d.vertex.BufferBuilder; +import com.mojang.blaze3d.vertex.PoseStack; +import lombok.Getter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A custom particle implementation with framework for more advanced rendering capabilities. + *

+ * GTParticle instances are managed by {@link GTParticleManager}. GTParticle instances with same {@link IRenderSetup}s + * will be drawn together as a batch. + */ +public abstract class GTParticle { + + public double posX; + public double posY; + public double posZ; + + /** + * render range. If the distance between particle and render view entity exceeds this value, the particle + * will not be rendered. If render range is negative value or {@code NaN}, then the check is disabled and + * the + * particle will be rendered regardless of the distance. + */ + @Getter + private double renderRange = -1; + /** + * squared render range, or negative value if render distance check is disabled. + */ + @Getter + private double squaredRenderRange = -1; + + @Getter + private boolean expired; + + protected GTParticle(double posX, double posY, double posZ) { + this.posX = posX; + this.posY = posY; + this.posZ = posZ; + } + + public boolean shouldRender(@NotNull EffectRenderContext context) { + if (squaredRenderRange < 0) return true; + return context.renderViewEntity().getEyePosition(context.partialTicks()) + .distanceToSqr(posX, posY, posZ) <= squaredRenderRange; + } + + public final boolean isAlive() { + return !expired; + } + + public final void setExpired() { + if (this.expired) return; + this.expired = true; + onExpired(); + } + + /** + * @return {@code true} to render the particle with + * {@link com.mojang.blaze3d.systems.RenderSystem#depthMask(boolean) depth mask} feature disabled; in + * other words, render the particle without modifying depth buffer. + */ + public boolean shouldDisableDepth() { + return false; + } + + /** + * Sets the render range. If the distance between particle and render view entity exceeds this value, the particle + * will not be rendered. If render range is negative value or {@code NaN}, then the check is disabled and the + * particle will be rendered regardless of the distance. + * + * @param renderRange Render range + */ + public final void setRenderRange(double renderRange) { + this.renderRange = renderRange; + if (renderRange >= 0) this.squaredRenderRange = renderRange * renderRange; + else this.squaredRenderRange = -1; + } + + /** + * Update the particle. This method is called each tick. + */ + public void onUpdate() {} + + /** + * Called once on expiration. + */ + protected void onExpired() {} + + /** + * Render the particle. If this particle has non-null {@link #getRenderSetup()} associated, this method will be + * called between a {@link IRenderSetup#preDraw(BufferBuilder)} call and a + * {@link IRenderSetup#postDraw(BufferBuilder)} call. + * + * @param poseStack + * @param buffer buffer builder + * @param context render context + */ + @OnlyIn(Dist.CLIENT) + public void renderParticle(@NotNull PoseStack poseStack, @NotNull BufferBuilder buffer, + @NotNull EffectRenderContext context) {} + + /** + * @return Render setup for this particle, if exists + */ + @Nullable + public IRenderSetup getRenderSetup() { + return null; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/particle/GTParticleManager.java b/src/main/java/com/gregtechceu/gtceu/client/particle/GTParticleManager.java new file mode 100644 index 0000000000..82b49170da --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/particle/GTParticleManager.java @@ -0,0 +1,222 @@ +package com.gregtechceu.gtceu.client.particle; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.client.renderer.IRenderSetup; +import com.gregtechceu.gtceu.client.util.EffectRenderContext; + +import net.minecraft.ChatFormatting; +import net.minecraft.client.Camera; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.renderer.culling.Frustum; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.Level; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.client.event.CustomizeGuiOverlayEvent; +import net.minecraftforge.client.event.RenderLevelStageEvent; +import net.minecraftforge.event.TickEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; + +import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.BufferBuilder; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.Tesselator; +import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +/** + * Singleton class responsible for managing, updating and rendering {@link GTParticle} instances. + */ +@Mod.EventBusSubscriber(value = Dist.CLIENT, modid = GTCEu.MOD_ID) +public class GTParticleManager { + + public static final GTParticleManager INSTANCE = new GTParticleManager(); + + @Nullable + private static Level currentWorld = null; + + private final Map<@Nullable IRenderSetup, ArrayDeque> depthEnabledParticles = new Object2ObjectLinkedOpenHashMap<>(); + private final Map<@Nullable IRenderSetup, ArrayDeque> depthDisabledParticles = new Object2ObjectLinkedOpenHashMap<>(); + + private final List newParticleQueue = new ArrayList<>(); + + public void addEffect(@NotNull GTParticle particles) { + newParticleQueue.add(particles); + } + + public void updateEffects() { + if (!depthEnabledParticles.isEmpty()) { + updateQueue(depthEnabledParticles); + } + if (!depthDisabledParticles.isEmpty()) { + updateQueue(depthDisabledParticles); + } + if (!newParticleQueue.isEmpty()) { + for (GTParticle particle : newParticleQueue) { + var queue = particle.shouldDisableDepth() ? depthDisabledParticles : depthEnabledParticles; + + ArrayDeque particles = queue.computeIfAbsent(particle.getRenderSetup(), + setup -> new ArrayDeque<>()); + + if (particles.size() > 6000) { + particles.removeFirst().setExpired(); + } + particles.add(particle); + } + newParticleQueue.clear(); + } + } + + private void updateQueue(Map> renderQueue) { + Iterator> it = renderQueue.values().iterator(); + while (it.hasNext()) { + ArrayDeque particles = it.next(); + + Iterator it2 = particles.iterator(); + while (it2.hasNext()) { + GTParticle particle = it2.next(); + if (particle.isAlive()) { + try { + particle.onUpdate(); + } catch (RuntimeException exception) { + GTCEu.LOGGER.error("particle update error: {}", particle, exception); + particle.setExpired(); + } + if (particle.isAlive()) continue; + } + it2.remove(); + } + + if (particles.isEmpty()) { + it.remove(); + } + } + } + + public void clearAllEffects(boolean cleanNewQueue) { + if (cleanNewQueue) { + for (GTParticle particle : newParticleQueue) { + particle.setExpired(); + } + newParticleQueue.clear(); + } + for (ArrayDeque particles : depthEnabledParticles.values()) { + for (GTParticle particle : particles) { + particle.setExpired(); + } + } + for (ArrayDeque particles : depthDisabledParticles.values()) { + for (GTParticle particle : particles) { + particle.setExpired(); + } + } + depthEnabledParticles.clear(); + depthDisabledParticles.clear(); + } + + public void renderParticles(@NotNull PoseStack poseStack, @NotNull Entity renderViewEntity, Camera camera, + Frustum frustum, float partialTicks) { + if (depthEnabledParticles.isEmpty() && depthDisabledParticles.isEmpty()) return; + + EffectRenderContext instance = EffectRenderContext.getInstance().update(renderViewEntity, camera, frustum, + partialTicks); + + RenderSystem.enableBlend(); + RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); + + if (!depthDisabledParticles.isEmpty()) { + RenderSystem.depthMask(false); + + renderGlParticlesInLayer(poseStack, depthDisabledParticles, instance); + + RenderSystem.depthMask(true); + } + + renderGlParticlesInLayer(poseStack, depthEnabledParticles, instance); + + RenderSystem.disableBlend(); + } + + private static void renderGlParticlesInLayer(@NotNull PoseStack poseStack, + @NotNull Map<@Nullable IRenderSetup, ArrayDeque> renderQueue, + @NotNull EffectRenderContext context) { + for (var e : renderQueue.entrySet()) { + @Nullable + IRenderSetup handler = e.getKey(); + ArrayDeque particles = e.getValue(); + if (particles.isEmpty()) continue; + + boolean initialized = false; + BufferBuilder buffer = Tesselator.getInstance().getBuilder(); + for (GTParticle particle : particles) { + if (particle.shouldRender(context)) { + try { + if (!initialized) { + initialized = true; + if (handler != null) { + handler.preDraw(buffer); + } + } + particle.renderParticle(poseStack, buffer, context); + } catch (Throwable throwable) { + GTCEu.LOGGER.error("particle render error: {}", particle, throwable); + particle.setExpired(); + } + } + } + if (initialized && handler != null) { + handler.postDraw(buffer); + } + } + } + + @SubscribeEvent + public static void clientTick(TickEvent.ClientTickEvent event) { + if (event.phase != TickEvent.Phase.END || Minecraft.getInstance().isPaused()) { + return; + } + + ClientLevel world = Minecraft.getInstance().level; + if (currentWorld != world) { + INSTANCE.clearAllEffects(currentWorld != null); + currentWorld = world; + } + + if (currentWorld != null) { + INSTANCE.updateEffects(); + } + } + + @SubscribeEvent + public static void renderWorld(RenderLevelStageEvent event) { + if (event.getStage() == RenderLevelStageEvent.Stage.AFTER_CUTOUT_BLOCKS) { + Entity entity = Minecraft.getInstance().getCameraEntity(); + INSTANCE.renderParticles(event.getPoseStack(), entity == null ? Minecraft.getInstance().player : entity, + event.getCamera(), event.getFrustum(), event.getPartialTick()); + } + } + + @SubscribeEvent + public static void debugOverlay(CustomizeGuiOverlayEvent.DebugText event) { + if (event.getLeft().size() >= 5) { + String particleTxt = event.getLeft().get(4); + particleTxt += "." + ChatFormatting.GOLD + + " PARTICLE-BACK: " + count(INSTANCE.depthEnabledParticles) + + "PARTICLE-FRONT: " + count(INSTANCE.depthDisabledParticles); + event.getLeft().set(4, particleTxt); + } + } + + private static int count(Map<@Nullable IRenderSetup, ArrayDeque> renderQueue) { + int g = 0; + for (Deque queue : renderQueue.values()) { + g += queue.size(); + } + return g; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/BlockHighlightRenderer.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/BlockHighlightRenderer.java index 6c393e4585..f91e02de18 100644 --- a/src/main/java/com/gregtechceu/gtceu/client/renderer/BlockHighlightRenderer.java +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/BlockHighlightRenderer.java @@ -1,14 +1,13 @@ package com.gregtechceu.gtceu.client.renderer; -import com.gregtechceu.gtceu.api.blockentity.PipeBlockEntity; import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; import com.gregtechceu.gtceu.api.capability.ICoverable; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.block.PipeBlockItem; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; import com.gregtechceu.gtceu.api.gui.GuiTextures; -import com.gregtechceu.gtceu.api.item.PipeBlockItem; import com.gregtechceu.gtceu.api.item.tool.GTToolType; import com.gregtechceu.gtceu.api.item.tool.IToolGridHighlight; import com.gregtechceu.gtceu.api.item.tool.ToolHelper; -import com.gregtechceu.gtceu.api.pipenet.IPipeType; import com.gregtechceu.gtceu.common.item.CoverPlaceBehavior; import com.gregtechceu.gtceu.common.item.tool.rotation.CustomBlockRotations; import com.gregtechceu.gtceu.core.mixins.GuiGraphicsAccessor; @@ -144,18 +143,19 @@ public ResourceTexture sideTips(Player player, BlockPos pos, BlockState state, } // draw pipe connection grid highlight - var pipeType = held.getItem() instanceof PipeBlockItem pipeBlockItem ? pipeBlockItem.getBlock().pipeType : - null; - if (pipeType instanceof IPipeType type && blockEntity instanceof PipeBlockEntity pipeBlockEntity && - pipeBlockEntity.getPipeType().type().equals(type.type())) { + var pipeBlock = held.getItem() instanceof PipeBlockItem pipeBlockItem ? + pipeBlockItem.getBlock() : null; + var pipeStructure = pipeBlock == null ? null : pipeBlock.getStructure(); + if (pipeStructure != null && pipeBlock.hasPipeCollisionChangingItem(level, blockPos, player) && + blockEntity instanceof PipeBlockEntity pipeBlockEntity) { Vec3 pos = camera.getPosition(); poseStack.pushPose(); poseStack.translate(-pos.x, -pos.y, -pos.z); var buffer = multiBufferSource.getBuffer(RenderType.lines()); RenderSystem.lineWidth(3); - drawGridOverlays(poseStack, buffer, target, side -> level.isEmptyBlock(blockPos.relative(side)) ? - pipeBlockEntity.getPipeTexture(true) : null); + drawGridOverlays(poseStack, buffer, target, side -> level.isEmptyBlock(blockPos.relative(side)) && + pipeBlockEntity.canConnectTo(side) ? pipeStructure.getPipeTexture(true) : null); poseStack.popPose(); } diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/GTRenderTypes.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/GTRenderTypes.java index 0338050f0c..8392cef2b8 100644 --- a/src/main/java/com/gregtechceu/gtceu/client/renderer/GTRenderTypes.java +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/GTRenderTypes.java @@ -17,6 +17,12 @@ public class GTRenderTypes extends RenderType { .setCullState(NO_CULL) .setShaderState(RenderStateShard.POSITION_COLOR_SHADER) .createCompositeState(false)); + private static final RenderType BLOOM_QUAD = RenderType.create("bloom_quad", + DefaultVertexFormat.POSITION_COLOR, VertexFormat.Mode.QUADS, 256, false, false, + RenderType.CompositeState.builder() + .setCullState(NO_CULL) + .setShaderState(RenderStateShard.POSITION_COLOR_SHADER) + .createCompositeState(false)); private GTRenderTypes(String name, VertexFormat format, VertexFormat.Mode mode, int bufferSize, boolean affectsCrumbling, boolean sortOnUpload, Runnable setupState, Runnable clearState) { @@ -26,4 +32,8 @@ private GTRenderTypes(String name, VertexFormat format, VertexFormat.Mode mode, public static RenderType getLightRing() { return LIGHT_RING; } + + public static RenderType getBloomQuad() { + return BLOOM_QUAD; + } } diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/GTRendererProvider.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/GTRendererProvider.java index c70126ff93..505b60db9b 100644 --- a/src/main/java/com/gregtechceu/gtceu/client/renderer/GTRendererProvider.java +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/GTRendererProvider.java @@ -24,10 +24,7 @@ public class GTRendererProvider extends ATESRRendererProvider { private static GTRendererProvider INSTANCE; - private GTRendererProvider(BlockEntityRendererProvider.Context context) { - // ModelBellows.INSTANCE = new ModelBellows(context); - // ModelHungryChest.INSTANCE = new ModelHungryChest(context); - } + private GTRendererProvider(BlockEntityRendererProvider.Context context) {} public static GTRendererProvider getOrCreate(BlockEntityRendererProvider.Context context) { if (INSTANCE == null) { diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/IRenderSetup.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/IRenderSetup.java new file mode 100644 index 0000000000..895010d624 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/IRenderSetup.java @@ -0,0 +1,35 @@ +package com.gregtechceu.gtceu.client.renderer; + +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import com.mojang.blaze3d.vertex.BufferBuilder; +import org.jetbrains.annotations.NotNull; + +/** + *

+ * Object representation of GL setup code. Any recurring render setup / cleanup code should probably go here. + *

+ *

+ * During render, render calls with identical render setup instance will be drawn in a batch. + * Providing proper {@link Object#equals(Object) equals()} and {@link Object#hashCode() hashCode()} implementation is + * recommended for non-singleton render setup implementations. + *

+ */ +@OnlyIn(Dist.CLIENT) +public interface IRenderSetup { + + /** + * Run any pre render gl code here. + * + * @param buffer Buffer builder + */ + void preDraw(@NotNull BufferBuilder buffer); + + /** + * Run any post render gl code here. + * + * @param buffer Buffer builder + */ + void postDraw(@NotNull BufferBuilder buffer); +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/block/PipeBlockRenderer.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/block/PipeBlockRenderer.java deleted file mode 100644 index fafbe4f1e7..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/client/renderer/block/PipeBlockRenderer.java +++ /dev/null @@ -1,134 +0,0 @@ -package com.gregtechceu.gtceu.client.renderer.block; - -import com.gregtechceu.gtceu.GTCEu; -import com.gregtechceu.gtceu.api.data.chemical.material.info.MaterialIconType; -import com.gregtechceu.gtceu.api.data.tag.TagPrefix; -import com.gregtechceu.gtceu.api.pipenet.IPipeNode; -import com.gregtechceu.gtceu.client.model.PipeModel; -import com.gregtechceu.gtceu.client.renderer.cover.ICoverableRenderer; -import com.gregtechceu.gtceu.common.data.GTMaterialBlocks; - -import com.lowdragmc.lowdraglib.client.model.ModelFactory; -import com.lowdragmc.lowdraglib.client.renderer.IRenderer; - -import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.MultiBufferSource; -import net.minecraft.client.renderer.block.model.BakedQuad; -import net.minecraft.client.renderer.texture.TextureAtlas; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.resources.model.BakedModel; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.util.RandomSource; -import net.minecraft.world.item.ItemDisplayContext; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.BlockAndTintGetter; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; - -import com.mojang.blaze3d.vertex.PoseStack; -import lombok.Getter; -import org.jetbrains.annotations.NotNull; - -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.function.Consumer; - -/** - * @author KilaBash - * @date 2023/3/1 - * @implNote PipeBlockRenderer - */ -public class PipeBlockRenderer implements IRenderer, ICoverableRenderer { - - @Getter - PipeModel pipeModel; - - public PipeBlockRenderer(PipeModel pipeModel) { - this.pipeModel = pipeModel; - if (GTCEu.isClientSide()) { - registerEvent(); - } - } - - @Override - @OnlyIn(Dist.CLIENT) - public void renderItem(ItemStack stack, - ItemDisplayContext transformType, - boolean leftHand, PoseStack matrixStack, - MultiBufferSource buffer, int combinedLight, - int combinedOverlay, BakedModel model) { - pipeModel.renderItem(stack, transformType, leftHand, matrixStack, buffer, combinedLight, combinedOverlay, - model); - } - - @Override - public boolean useAO() { - return true; - } - - @Override - @OnlyIn(Dist.CLIENT) - public boolean useBlockLight(ItemStack stack) { - return true; - } - - @Override - @OnlyIn(Dist.CLIENT) - public List renderModel(BlockAndTintGetter level, BlockPos pos, BlockState state, Direction side, - RandomSource rand) { - if (level == null) { - return pipeModel.bakeQuads(side, PipeModel.ITEM_CONNECTIONS, 0); - } else if (level.getBlockEntity(pos) instanceof IPipeNode pipeNode) { - var quads = new LinkedList<>( - pipeModel.bakeQuads(side, pipeNode.getVisualConnections(), pipeNode.getBlockedConnections())); - var modelState = ModelFactory.getRotation(pipeNode.getCoverContainer().getFrontFacing()); - var modelFacing = side == null ? null : - ModelFactory.modelFacing(side, pipeNode.getCoverContainer().getFrontFacing()); - ICoverableRenderer.super.renderCovers(quads, side, rand, pipeNode.getCoverContainer(), modelFacing, pos, - level, modelState); - if (pipeNode.getFrameMaterial() != null) { - ResourceLocation rl = MaterialIconType.frameGt - .getBlockTexturePath(pipeNode.getFrameMaterial().getMaterialIconSet(), true); - BlockState blockState = GTMaterialBlocks.MATERIAL_BLOCKS - .get(TagPrefix.frameGt, pipeNode.getFrameMaterial()) - .getDefaultState(); - var frameModel = Minecraft.getInstance().getBlockRenderer().getBlockModel(blockState); - for (Direction face : Direction.values()) { - if ((pipeNode.getConnections() & 1 << (12 + face.get3DDataValue())) == 0) { - var frameTintedFaces = frameModel.getQuads(state, face, rand) - .stream() - .map(quad -> new BakedQuad(quad.getVertices(), - quad.getTintIndex() + (quad.isTinted() ? 3 : 0), - quad.getDirection(), - quad.getSprite(), - quad.isShade(), - quad.hasAmbientOcclusion())) - .toList(); - quads.addAll(frameTintedFaces); - } - } - } - return quads; - } - return Collections.emptyList(); - } - - @NotNull - @Override - @OnlyIn(Dist.CLIENT) - public TextureAtlasSprite getParticleTexture() { - return pipeModel.getParticleTexture(); - } - - @Override - @OnlyIn(Dist.CLIENT) - public void onPrepareTextureAtlas(ResourceLocation atlasName, Consumer register) { - if (atlasName.equals(TextureAtlas.LOCATION_BLOCKS)) { - pipeModel.registerTextureAtlas(register); - } - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/ConveyorCoverRenderer.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/ConveyorCoverRenderer.java deleted file mode 100644 index 611d268076..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/ConveyorCoverRenderer.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.gregtechceu.gtceu.client.renderer.cover; - -import com.gregtechceu.gtceu.GTCEu; -import com.gregtechceu.gtceu.api.capability.recipe.IO; -import com.gregtechceu.gtceu.api.cover.CoverBehavior; -import com.gregtechceu.gtceu.client.util.StaticFaceBakery; -import com.gregtechceu.gtceu.common.cover.ConveyorCover; - -import com.lowdragmc.lowdraglib.client.model.ModelFactory; - -import net.minecraft.client.renderer.block.model.BakedQuad; -import net.minecraft.client.renderer.texture.TextureAtlas; -import net.minecraft.client.resources.model.ModelState; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.util.RandomSource; -import net.minecraft.world.level.BlockAndTintGetter; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.List; -import java.util.function.Consumer; - -/** - * @author KilaBash - * @date 2023/3/12 - * @implNote ConveyorCoverRenderer - */ -public class ConveyorCoverRenderer implements ICoverRenderer { - - public final static ConveyorCoverRenderer INSTANCE = new ConveyorCoverRenderer(); - public final static ResourceLocation CONVEYOR_OVERLAY = GTCEu.id("block/cover/overlay_conveyor"); - public final static ResourceLocation CONVEYOR_OVERLAY_OUT = GTCEu.id("block/cover/overlay_conveyor_emissive"); - public final static ResourceLocation CONVEYOR_OVERLAY_IN = GTCEu - .id("block/cover/overlay_conveyor_inverted_emissive"); - - protected ConveyorCoverRenderer() { - if (GTCEu.isClientSide()) { - registerEvent(); - } - } - - @Override - @OnlyIn(Dist.CLIENT) - public void renderCover(List quads, @Nullable Direction side, RandomSource rand, - @NotNull CoverBehavior coverBehavior, @Nullable Direction modelFacing, BlockPos pos, - BlockAndTintGetter level, ModelState modelState) { - if (side == coverBehavior.attachedSide && coverBehavior instanceof ConveyorCover conveyor && - modelFacing != null) { - quads.add( - StaticFaceBakery.bakeFace(modelFacing, ModelFactory.getBlockSprite(CONVEYOR_OVERLAY), modelState)); - quads.add(StaticFaceBakery.bakeFace(modelFacing, - ModelFactory - .getBlockSprite(conveyor.getIo() == IO.OUT ? CONVEYOR_OVERLAY_OUT : CONVEYOR_OVERLAY_IN), - modelState, -101, 15)); - } - } - - @Override - @OnlyIn(Dist.CLIENT) - public void onPrepareTextureAtlas(ResourceLocation atlasName, Consumer register) { - if (atlasName.equals(TextureAtlas.LOCATION_BLOCKS)) { - register.accept(CONVEYOR_OVERLAY); - register.accept(CONVEYOR_OVERLAY_IN); - register.accept(CONVEYOR_OVERLAY_OUT); - } - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/FacadeCoverRenderer.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/FacadeCoverRenderer.java index 71ac14e596..831513e450 100644 --- a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/FacadeCoverRenderer.java +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/FacadeCoverRenderer.java @@ -1,14 +1,15 @@ package com.gregtechceu.gtceu.client.renderer.cover; import com.gregtechceu.gtceu.GTCEu; -import com.gregtechceu.gtceu.api.cover.CoverBehavior; import com.gregtechceu.gtceu.client.model.ModelUtil; -import com.gregtechceu.gtceu.common.cover.FacadeCover; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRenderer; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRendererBuilder; import com.gregtechceu.gtceu.common.item.FacadeItemBehaviour; import com.gregtechceu.gtceu.utils.GTUtil; import com.lowdragmc.lowdraglib.client.bakedpipeline.FaceQuad; import com.lowdragmc.lowdraglib.client.model.ModelFactory; +import com.lowdragmc.lowdraglib.client.renderer.IRenderer; import com.lowdragmc.lowdraglib.utils.FacadeBlockAndTintGetter; import net.minecraft.client.Minecraft; @@ -17,27 +18,22 @@ import net.minecraft.client.renderer.block.BlockRenderDispatcher; import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.resources.model.BakedModel; -import net.minecraft.client.resources.model.ModelState; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; -import net.minecraft.util.RandomSource; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.ItemDisplayContext; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.BlockAndTintGetter; -import net.minecraft.world.level.block.RenderShape; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.AABB; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import com.mojang.blaze3d.vertex.PoseStack; -import org.jetbrains.annotations.NotNull; import org.joml.AxisAngle4d; import org.joml.Quaternionf; import java.util.LinkedList; -import java.util.List; /** * @author KilaBash @@ -46,7 +42,7 @@ * It can only be used for item. * call it in other renderer to render a facade cover. */ -public class FacadeCoverRenderer implements ICoverRenderer { +public class FacadeCoverRenderer implements IRenderer { public final static FacadeCoverRenderer INSTANCE = new FacadeCoverRenderer(); @@ -108,37 +104,22 @@ public void renderItem(ItemStack stack, ItemDisplayContext transformType, boolea } } - @Override - @OnlyIn(Dist.CLIENT) - public void renderCover(List quads, Direction side, RandomSource rand, - @NotNull CoverBehavior coverBehavior, Direction modelFacing, BlockPos pos, - BlockAndTintGetter level, ModelState modelState) { - if (coverBehavior instanceof FacadeCover facadeCover) { - var state = facadeCover.getFacadeState(); - if (state.getRenderShape() == RenderShape.MODEL) { - BlockRenderDispatcher brd = Minecraft.getInstance().getBlockRenderer(); - BakedModel model = brd.getBlockModel(state); - if (side == coverBehavior.attachedSide) { - quads.addAll(ModelUtil.getBakedModelQuads(model, level, pos, state, side, rand)); - } else if (side == null && coverBehavior.coverHolder.shouldRenderBackSide()) { - var normal = coverBehavior.attachedSide.getNormal(); - var cube = new AABB( - normal.getX() == 0 ? 0 : normal.getX() > 0 ? 1 : 0, - normal.getY() == 0 ? 0 : normal.getY() > 0 ? 1 : 0, - normal.getZ() == 0 ? 0 : normal.getZ() > 0 ? 1 : 0, - normal.getX() == 0 ? 1 : normal.getX() > 0 ? 1 : 0, - normal.getY() == 0 ? 1 : normal.getY() > 0 ? 1 : 0, - normal.getZ() == 0 ? 1 : normal.getZ() > 0 ? 1 : 0); - for (BakedQuad quad : ModelUtil.getBakedModelQuads(model, level, pos, state, - coverBehavior.attachedSide, rand)) { - quads.add(FaceQuad.builder(coverBehavior.attachedSide.getOpposite(), quad.getSprite()) - .cube(cube) - .shade(quad.isShade()) - .tintIndex(quad.getTintIndex()) - .bake()); - } - } + public static CoverRenderer createRenderer(final BlockAndTintGetter world, BlockPos pos, final BlockState state) { + BlockRenderDispatcher dispatcher = Minecraft.getInstance().getBlockRenderer(); + + BakedModel model = dispatcher.getBlockModel(state); + + return (quads, side, rand, renderPlate, renderBackside, modelData, data, renderType) -> { + if (renderType != RenderType.cutoutMipped()) return; + + AABB cube = CoverRendererBuilder.PLATE_AABBS.get(side); + for (BakedQuad quad : ModelUtil.getBakedModelQuads(model, world, pos, state, side, rand)) { + quads.add(FaceQuad.builder(side.getOpposite(), quad.getSprite()) + .cube(cube) + .shade(quad.isShade()) + .tintIndex(quad.getTintIndex()) + .bake()); } - } + }; } } diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/FluidRegulatorCoverRenderer.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/FluidRegulatorCoverRenderer.java deleted file mode 100644 index 9ca4807998..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/FluidRegulatorCoverRenderer.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.gregtechceu.gtceu.client.renderer.cover; - -import com.gregtechceu.gtceu.GTCEu; -import com.gregtechceu.gtceu.api.capability.recipe.IO; -import com.gregtechceu.gtceu.api.cover.CoverBehavior; -import com.gregtechceu.gtceu.client.util.StaticFaceBakery; -import com.gregtechceu.gtceu.common.cover.PumpCover; - -import com.lowdragmc.lowdraglib.client.model.ModelFactory; - -import net.minecraft.client.renderer.block.model.BakedQuad; -import net.minecraft.client.renderer.texture.TextureAtlas; -import net.minecraft.client.resources.model.ModelState; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.util.RandomSource; -import net.minecraft.world.level.BlockAndTintGetter; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; - -import org.jetbrains.annotations.NotNull; - -import java.util.List; -import java.util.function.Consumer; - -/** - * @author KilaBash - * @date 2023/3/12 - * @implNote ConveyorCoverRenderer - */ -public class FluidRegulatorCoverRenderer implements ICoverRenderer { - - public final static FluidRegulatorCoverRenderer INSTANCE = new FluidRegulatorCoverRenderer(); - public final static ResourceLocation PUMP_OVERLAY_OUT = GTCEu.id("block/cover/overlay_pump"); - public final static ResourceLocation PUMP_OVERLAY_IN = GTCEu.id("block/cover/overlay_pump_inverted"); - - protected FluidRegulatorCoverRenderer() { - if (GTCEu.isClientSide()) { - registerEvent(); - } - } - - @Override - @OnlyIn(Dist.CLIENT) - public void renderCover(List quads, Direction side, RandomSource rand, - @NotNull CoverBehavior coverBehavior, Direction modelFacing, BlockPos pos, - BlockAndTintGetter level, ModelState modelState) { - if (side == coverBehavior.attachedSide && coverBehavior instanceof PumpCover pump && modelFacing != null) { - quads.add(StaticFaceBakery.bakeFace(modelFacing, - ModelFactory.getBlockSprite(pump.getIo() == IO.OUT ? PUMP_OVERLAY_OUT : PUMP_OVERLAY_IN), - modelState)); - } - } - - @Override - @OnlyIn(Dist.CLIENT) - public void onPrepareTextureAtlas(ResourceLocation atlasName, Consumer register) { - if (atlasName.equals(TextureAtlas.LOCATION_BLOCKS)) { - register.accept(PUMP_OVERLAY_IN); - register.accept(PUMP_OVERLAY_OUT); - } - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/ICoverRenderer.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/ICoverRenderer.java deleted file mode 100644 index bf67108162..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/ICoverRenderer.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.gregtechceu.gtceu.client.renderer.cover; - -import com.gregtechceu.gtceu.api.cover.CoverBehavior; - -import com.lowdragmc.lowdraglib.client.renderer.IRenderer; - -import net.minecraft.client.renderer.block.model.BakedQuad; -import net.minecraft.client.resources.model.ModelState; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.util.RandomSource; -import net.minecraft.world.level.BlockAndTintGetter; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.List; - -/** - * @author KilaBash - * @date 2023/2/24 - * @implNote ICoverRenderer - * Do not use it as a block renderer alone. It should be called from {@link ICoverableRenderer} - */ -public interface ICoverRenderer extends IRenderer { - - /** - * Use - * {@link #renderCover(List, Direction, RandomSource, CoverBehavior, Direction, BlockPos, BlockAndTintGetter, ModelState)} - * instead - */ - @Override - @Deprecated - @OnlyIn(Dist.CLIENT) - default List renderModel(BlockAndTintGetter level, BlockPos pos, BlockState state, Direction side, - RandomSource rand) { - return IRenderer.super.renderModel(level, pos, state, side, rand); - } - - @OnlyIn(Dist.CLIENT) - void renderCover(List quads, @Nullable Direction side, RandomSource rand, - @NotNull CoverBehavior coverBehavior, @Nullable Direction modelFacing, BlockPos pos, - BlockAndTintGetter level, ModelState modelState); -} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/ICoverableRenderer.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/ICoverableRenderer.java index f2986b5d81..2940530f03 100644 --- a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/ICoverableRenderer.java +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/ICoverableRenderer.java @@ -3,10 +3,12 @@ import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; import com.gregtechceu.gtceu.api.capability.ICoverable; +import com.gregtechceu.gtceu.client.renderer.pipe.util.ColorData; import com.gregtechceu.gtceu.utils.GTUtil; import com.lowdragmc.lowdraglib.client.bakedpipeline.FaceQuad; import com.lowdragmc.lowdraglib.client.model.ModelFactory; +import com.lowdragmc.lowdraglib.client.model.forge.LDLRendererModel; import com.lowdragmc.lowdraglib.client.renderer.IRenderer; import net.minecraft.client.renderer.block.model.BakedQuad; @@ -23,6 +25,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.EnumSet; import java.util.LinkedList; import java.util.List; @@ -82,7 +85,9 @@ default void renderCovers(List quads, @Nullable Direction side, Rando .cube(cube).cubeUV().tintIndex(-1).bake()); } } - cover.getCoverRenderer().renderCover(quads, side, rand, cover, modelFacing, pos, level, modelState); + cover.getRenderer().addQuads(quads, face, rand, EnumSet.noneOf(Direction.class), false, + LDLRendererModel.RendererBakedModel.CURRENT_MODEL_DATA.get(), new ColorData(), + LDLRendererModel.RendererBakedModel.CURRENT_RENDER_TYPE.get()); } } } diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/PumpCoverRenderer.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/PumpCoverRenderer.java deleted file mode 100644 index 30b92cff69..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/PumpCoverRenderer.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.gregtechceu.gtceu.client.renderer.cover; - -import com.gregtechceu.gtceu.GTCEu; -import com.gregtechceu.gtceu.api.capability.recipe.IO; -import com.gregtechceu.gtceu.api.cover.CoverBehavior; -import com.gregtechceu.gtceu.client.util.StaticFaceBakery; -import com.gregtechceu.gtceu.common.cover.PumpCover; - -import com.lowdragmc.lowdraglib.client.model.ModelFactory; - -import net.minecraft.client.renderer.block.model.BakedQuad; -import net.minecraft.client.renderer.texture.TextureAtlas; -import net.minecraft.client.resources.model.ModelState; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.util.RandomSource; -import net.minecraft.world.level.BlockAndTintGetter; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; - -import org.jetbrains.annotations.NotNull; - -import java.util.List; -import java.util.function.Consumer; - -/** - * @author KilaBash - * @date 2023/3/12 - * @implNote ConveyorCoverRenderer - */ -public class PumpCoverRenderer implements ICoverRenderer { - - public final static PumpCoverRenderer INSTANCE = new PumpCoverRenderer(); - public final static ResourceLocation PUMP_OVERLAY_OUT = GTCEu.id("block/cover/overlay_pump"); - public final static ResourceLocation PUMP_OVERLAY_IN = GTCEu.id("block/cover/overlay_pump_inverted"); - - protected PumpCoverRenderer() { - if (GTCEu.isClientSide()) { - registerEvent(); - } - } - - @Override - @OnlyIn(Dist.CLIENT) - public void renderCover(List quads, Direction side, RandomSource rand, - @NotNull CoverBehavior coverBehavior, Direction modelFacing, BlockPos pos, - BlockAndTintGetter level, ModelState modelState) { - if (side == coverBehavior.attachedSide && coverBehavior instanceof PumpCover pump && modelFacing != null) { - quads.add(StaticFaceBakery.bakeFace(modelFacing, - ModelFactory.getBlockSprite(pump.getIo() == IO.OUT ? PUMP_OVERLAY_OUT : PUMP_OVERLAY_IN), - modelState)); - } - } - - @Override - @OnlyIn(Dist.CLIENT) - public void onPrepareTextureAtlas(ResourceLocation atlasName, Consumer register) { - if (atlasName.equals(TextureAtlas.LOCATION_BLOCKS)) { - register.accept(PUMP_OVERLAY_IN); - register.accept(PUMP_OVERLAY_OUT); - } - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/RobotArmCoverRenderer.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/RobotArmCoverRenderer.java deleted file mode 100644 index 3eae8fa24b..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/RobotArmCoverRenderer.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.gregtechceu.gtceu.client.renderer.cover; - -import com.gregtechceu.gtceu.GTCEu; -import com.gregtechceu.gtceu.api.capability.recipe.IO; -import com.gregtechceu.gtceu.api.cover.CoverBehavior; -import com.gregtechceu.gtceu.client.util.StaticFaceBakery; -import com.gregtechceu.gtceu.common.cover.RobotArmCover; - -import com.lowdragmc.lowdraglib.client.model.ModelFactory; - -import net.minecraft.client.renderer.block.model.BakedQuad; -import net.minecraft.client.renderer.texture.TextureAtlas; -import net.minecraft.client.resources.model.ModelState; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.util.RandomSource; -import net.minecraft.world.level.BlockAndTintGetter; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.List; -import java.util.function.Consumer; - -public class RobotArmCoverRenderer implements ICoverRenderer { - - public final static RobotArmCoverRenderer INSTANCE = new RobotArmCoverRenderer(); - public final static ResourceLocation ARM_OVERLAY = GTCEu.id("block/cover/overlay_arm"); - public final static ResourceLocation ARM_OVERLAY_OUT = GTCEu.id("block/cover/overlay_arm_emissive"); - public final static ResourceLocation AR_OVERLAY_IN = GTCEu.id("block/cover/overlay_arm_inverted_emissive"); - - protected RobotArmCoverRenderer() { - if (GTCEu.isClientSide()) { - registerEvent(); - } - } - - @Override - @OnlyIn(Dist.CLIENT) - public void renderCover(List quads, @Nullable Direction side, RandomSource rand, - @NotNull CoverBehavior coverBehavior, @Nullable Direction modelFacing, BlockPos pos, - BlockAndTintGetter level, ModelState modelState) { - if (side == coverBehavior.attachedSide && coverBehavior instanceof RobotArmCover robotArm && - modelFacing != null) { - quads.add(StaticFaceBakery.bakeFace(modelFacing, ModelFactory.getBlockSprite(ARM_OVERLAY), modelState)); - quads.add(StaticFaceBakery.bakeFace(modelFacing, - ModelFactory.getBlockSprite(robotArm.getIo() == IO.OUT ? ARM_OVERLAY_OUT : AR_OVERLAY_IN), - modelState, -101, 15)); - } - } - - @Override - @OnlyIn(Dist.CLIENT) - public void onPrepareTextureAtlas(ResourceLocation atlasName, Consumer register) { - if (atlasName.equals(TextureAtlas.LOCATION_BLOCKS)) { - register.accept(ARM_OVERLAY); - register.accept(AR_OVERLAY_IN); - register.accept(ARM_OVERLAY_OUT); - } - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/SimpleCoverRenderer.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/SimpleCoverRenderer.java deleted file mode 100644 index b761f531ac..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/SimpleCoverRenderer.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.gregtechceu.gtceu.client.renderer.cover; - -import com.gregtechceu.gtceu.GTCEu; -import com.gregtechceu.gtceu.api.cover.CoverBehavior; -import com.gregtechceu.gtceu.client.util.StaticFaceBakery; - -import com.lowdragmc.lowdraglib.client.model.ModelFactory; -import com.lowdragmc.lowdraglib.utils.ResourceHelper; - -import net.minecraft.client.renderer.block.model.BakedQuad; -import net.minecraft.client.renderer.texture.TextureAtlas; -import net.minecraft.client.resources.model.ModelState; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.util.RandomSource; -import net.minecraft.world.level.BlockAndTintGetter; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; - -import org.jetbrains.annotations.NotNull; - -import java.util.List; -import java.util.function.Consumer; - -/** - * @author KilaBash - * @date 2023/2/24 - * @implNote SimpleCoverRenderer - */ -public class SimpleCoverRenderer implements ICoverRenderer { - - ResourceLocation texture; - ResourceLocation emissiveTexture; - - public SimpleCoverRenderer(ResourceLocation texture) { - this.texture = texture; - if (GTCEu.isClientSide()) { - registerEvent(); - } - } - - @Override - @OnlyIn(Dist.CLIENT) - public void onPrepareTextureAtlas(ResourceLocation atlasName, Consumer register) { - if (atlasName.equals(TextureAtlas.LOCATION_BLOCKS)) { - register.accept(texture); - emissiveTexture = new ResourceLocation(texture.getNamespace(), texture.getPath() + "_emissive"); - if (ResourceHelper.isTextureExist(emissiveTexture)) register.accept(emissiveTexture); - else emissiveTexture = null; - } - } - - @OnlyIn(Dist.CLIENT) - public void renderCover(List quads, Direction side, RandomSource rand, - @NotNull CoverBehavior coverBehavior, Direction modelFacing, BlockPos pos, - BlockAndTintGetter level, ModelState modelState) { - if (side == coverBehavior.attachedSide && modelFacing != null) { - quads.add(StaticFaceBakery.bakeFace(modelFacing, ModelFactory.getBlockSprite(texture), modelState)); - if (emissiveTexture != null) { - quads.add(StaticFaceBakery.bakeFace(modelFacing, ModelFactory.getBlockSprite(emissiveTexture), - modelState)); - } - } - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/machine/QuantumChestRenderer.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/machine/QuantumChestRenderer.java index 69cd15f42a..309b3c9c45 100644 --- a/src/main/java/com/gregtechceu/gtceu/client/renderer/machine/QuantumChestRenderer.java +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/machine/QuantumChestRenderer.java @@ -2,6 +2,7 @@ import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.client.ClientProxy; import com.gregtechceu.gtceu.common.data.GTMachines; import com.gregtechceu.gtceu.common.machine.storage.CreativeChestMachine; import com.gregtechceu.gtceu.common.machine.storage.QuantumChestMachine; @@ -67,7 +68,7 @@ public void renderItem(ItemStack stack, ItemDisplayContext transformType, boolea ItemStack itemStack = ItemStack.of(stack.getOrCreateTagElement("stored")); long storedAmount = stack.getOrCreateTag().getLong("storedAmount"); - float tick = Minecraft.getInstance().level.getGameTime() + Minecraft.getInstance().getFrameTime(); + float tick = Minecraft.getInstance().levelRenderer.getTicks() + Minecraft.getInstance().getFrameTime(); // Don't need to handle locked items here since they don't get saved to the item renderChest(poseStack, buffer, Direction.NORTH, itemStack, storedAmount, tick, ItemStack.EMPTY, stack.is(CREATIVE_CHEST_ITEM)); @@ -83,9 +84,8 @@ public void render(BlockEntity blockEntity, float partialTicks, PoseStack poseSt int combinedLight, int combinedOverlay) { if (blockEntity instanceof IMachineBlockEntity machineBlockEntity && machineBlockEntity.getMetaMachine() instanceof QuantumChestMachine machine) { - var level = machine.getLevel(); var frontFacing = machine.getFrontFacing(); - float tick = level.getGameTime() + partialTicks; + float tick = ClientProxy.getServerTickCount() + partialTicks; renderChest(poseStack, buffer, frontFacing, machine.getStored(), machine.getStoredAmount(), tick, machine.getLockedItem(), machine instanceof CreativeChestMachine); } diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/AbstractPipeModel.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/AbstractPipeModel.java new file mode 100644 index 0000000000..2d823b091d --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/AbstractPipeModel.java @@ -0,0 +1,178 @@ +package com.gregtechceu.gtceu.client.renderer.pipe; + +import com.gregtechceu.gtceu.api.data.chemical.material.Material; +import com.gregtechceu.gtceu.api.data.tag.TagPrefix; +import com.gregtechceu.gtceu.client.renderer.pipe.cache.ColorQuadCache; +import com.gregtechceu.gtceu.client.renderer.pipe.cache.StructureQuadCache; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRendererPackage; +import com.gregtechceu.gtceu.client.renderer.pipe.quad.PipeQuadHelper; +import com.gregtechceu.gtceu.client.renderer.pipe.util.CacheKey; +import com.gregtechceu.gtceu.client.renderer.pipe.util.ColorData; +import com.gregtechceu.gtceu.client.renderer.pipe.util.SpriteInformation; +import com.gregtechceu.gtceu.common.data.GTMaterialBlocks; +import com.gregtechceu.gtceu.utils.GTUtil; +import com.gregtechceu.gtceu.utils.reference.WeakHashSet; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.core.Direction; +import net.minecraft.util.RandomSource; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.client.ChunkRenderTypeSet; +import net.minecraftforge.client.model.data.ModelData; +import net.minecraftforge.client.model.data.ModelProperty; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.List; + +public abstract class AbstractPipeModel { + + public static ModelProperty THICKNESS_PROPERTY = new ModelProperty<>(); + + public static ModelProperty FRAME_MATERIAL_PROPERTY = new ModelProperty<>(); + public static ModelProperty FRAME_MASK_PROPERTY = new ModelProperty<>(); + + public static ModelProperty CONNECTED_MASK_PROPERTY = new ModelProperty<>(); + public static ModelProperty CLOSED_MASK_PROPERTY = new ModelProperty<>(); + public static ModelProperty BLOCKED_MASK_PROPERTY = new ModelProperty<>(); + + public static ModelProperty COLOR_PROPERTY = new ModelProperty<>(); + public static final ModelProperty MATERIAL_PROPERTY = new ModelProperty<>(); + + protected final Object2ObjectOpenHashMap frameCache = new Object2ObjectOpenHashMap<>(); + protected final Object2ObjectOpenHashMap pipeCache; + + protected static final WeakHashSet> PIPE_CACHES = new WeakHashSet<>(); + + public static void invalidateCaches() { + for (var cache : PIPE_CACHES) { + cache.clear(); + cache.trim(16); + } + } + + public AbstractPipeModel() { + pipeCache = new Object2ObjectOpenHashMap<>(); + PIPE_CACHES.add(pipeCache); + } + + @OnlyIn(Dist.CLIENT) + public @NotNull List getQuads(@Nullable BlockState state, @Nullable Direction side, + @NotNull RandomSource rand, @NotNull ModelData modelData, + @Nullable RenderType renderType) { + if (side == null) { + ColorData data = computeColorData(modelData); + CoverRendererPackage rendererPackage = modelData.get(CoverRendererPackage.PROPERTY); + byte coverMask = rendererPackage == null ? 0 : rendererPackage.getMask(); + List quads = getQuads(toKey(modelData), safeByte(modelData.get(CONNECTED_MASK_PROPERTY)), + safeByte(modelData.get(CLOSED_MASK_PROPERTY)), safeByte(modelData.get(BLOCKED_MASK_PROPERTY)), + data, modelData.get(FRAME_MATERIAL_PROPERTY), + safeByte(modelData.get(FRAME_MASK_PROPERTY)), coverMask, + rand, modelData, renderType); + if (rendererPackage != null) renderCovers(quads, rendererPackage, rand, modelData, renderType); + return quads; + } + return Collections.emptyList(); + } + + @OnlyIn(Dist.CLIENT) + protected void renderCovers(List quads, @NotNull CoverRendererPackage rendererPackage, + RandomSource rand, @NotNull ModelData data, RenderType renderType) { + int color = safeInt(data.get(COLOR_PROPERTY)); + if (data.get(AbstractPipeModel.MATERIAL_PROPERTY) != null) { + Material material = data.get(AbstractPipeModel.MATERIAL_PROPERTY); + if (material != null) { + int matColor = GTUtil.convertRGBtoARGB(material.getMaterialRGB()); + if (color == 0 || color == matColor) { + // unpainted + color = 0xFFFFFFFF; + } + } + } + rendererPackage.addQuads(quads, rand, data, new ColorData(color), renderType); + } + + protected ColorData computeColorData(@NotNull ModelData data) { + return new ColorData(safeInt(data.get(COLOR_PROPERTY))); + } + + protected static byte safeByte(@Nullable Byte abyte) { + return abyte == null ? 0 : abyte; + } + + protected static int safeInt(@Nullable Integer integer) { + return integer == null ? 0 : integer; + } + + @OnlyIn(Dist.CLIENT) + public @NotNull List getQuads(K key, byte connectionMask, byte closedMask, byte blockedMask, + ColorData data, + @Nullable Material frameMaterial, byte frameMask, byte coverMask, + RandomSource randomSource, ModelData modelData, RenderType renderType) { + List quads = new ObjectArrayList<>(); + + StructureQuadCache cache = pipeCache.computeIfAbsent(key, this::constructForKey); + cache.addToList(quads, connectionMask, closedMask, + blockedMask, data, coverMask); + + if (frameMaterial != null) { + BlockState state = GTMaterialBlocks.MATERIAL_BLOCKS.get(TagPrefix.frameGt, frameMaterial).getDefaultState(); + ColorQuadCache frame = frameCache.get(state); + if (frame == null) { + BakedModel model = Minecraft.getInstance().getBlockRenderer().getBlockModel(state); + frame = new ColorQuadCache(PipeQuadHelper + .createFrame(model, randomSource, modelData, renderType)); + frameCache.put(state, frame); + } + List frameQuads = frame + .getQuads(new ColorData(GTUtil.convertRGBtoARGB(frameMaterial.getMaterialRGB()))); + for (Direction dir : GTUtil.DIRECTIONS) { + if ((frameMask & (1 << dir.ordinal())) > 0) { + quads.addAll(frameQuads.stream().filter(quad -> quad.getDirection() == dir).toList()); + } + } + } + return quads; + } + + protected abstract @NotNull K toKey(@NotNull ModelData state); + + protected final @NotNull CacheKey defaultKey(@NotNull ModelData state) { + return CacheKey.of(state.get(THICKNESS_PROPERTY)); + } + + protected abstract StructureQuadCache constructForKey(K key); + + public TextureAtlasSprite getParticleTexture(int paintColor, @Nullable Material material) { + SpriteInformation spriteInformation = getParticleSprite(material); + return spriteInformation.sprite(); + } + + public TextureAtlasSprite getParticleIcon(@NotNull ModelData data) { + return getParticleTexture(safeInt(data.get(COLOR_PROPERTY)), data.get(MATERIAL_PROPERTY)); + } + + public abstract SpriteInformation getParticleSprite(@Nullable Material material); + + @Nullable + protected abstract PipeItemModel getItemModel(PipeModelRedirector redirector, @NotNull ItemStack stack, + ClientLevel world, LivingEntity entity); + + public ChunkRenderTypeSet getRenderTypes(@NotNull BlockState state, @NotNull RandomSource rand, + @NotNull ModelData data) { + return ChunkRenderTypeSet.of(RenderType.cutoutMipped()); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/ActivablePipeModel.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/ActivablePipeModel.java new file mode 100644 index 0000000000..0835563cfe --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/ActivablePipeModel.java @@ -0,0 +1,132 @@ +package com.gregtechceu.gtceu.client.renderer.pipe; + +import com.gregtechceu.gtceu.api.data.chemical.material.Material; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.block.PipeBlock; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; +import com.gregtechceu.gtceu.client.renderer.pipe.cache.ActivableSQC; +import com.gregtechceu.gtceu.client.renderer.pipe.cache.StructureQuadCache; +import com.gregtechceu.gtceu.client.renderer.pipe.quad.PipeQuadHelper; +import com.gregtechceu.gtceu.client.renderer.pipe.util.ActivableCacheKey; +import com.gregtechceu.gtceu.client.renderer.pipe.util.ColorData; +import com.gregtechceu.gtceu.client.renderer.pipe.util.SpriteInformation; +import com.gregtechceu.gtceu.client.renderer.pipe.util.TextureInformation; +import com.gregtechceu.gtceu.config.ConfigHolder; + +import com.lowdragmc.lowdraglib.client.bakedpipeline.Quad; +import com.lowdragmc.lowdraglib.client.model.ModelFactory; + +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.util.RandomSource; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.client.model.data.ModelData; +import net.minecraftforge.client.model.data.ModelProperty; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.stream.Collectors; + +public class ActivablePipeModel extends AbstractPipeModel { + + public static final ModelProperty ACTIVE_PROPERTY = new ModelProperty<>(); + + private final TextureInformation inTex; + private final TextureInformation sideTex; + private final TextureInformation overlayTex; + private final TextureInformation overlayActiveTex; + + private SpriteInformation inSprite; + private SpriteInformation sideSprite; + private SpriteInformation overlaySprite; + private SpriteInformation overlayActiveSprite; + + private final boolean emissiveActive; + + public ActivablePipeModel(@NotNull TextureInformation inTex, + @NotNull TextureInformation sideTex, + @NotNull TextureInformation overlayTex, + @NotNull TextureInformation overlayActiveTex, boolean emissiveActive) { + this.inTex = inTex; + this.sideTex = sideTex; + this.overlayTex = overlayTex; + this.overlayActiveTex = overlayActiveTex; + this.emissiveActive = emissiveActive; + } + + @Override + @OnlyIn(Dist.CLIENT) + public @NotNull List getQuads(ActivableCacheKey key, + byte connectionMask, byte closedMask, byte blockedMask, + ColorData data, @Nullable Material frameMaterial, + byte frameMask, byte coverMask, + RandomSource randomSource, ModelData modelData, RenderType renderType) { + List quads = super.getQuads(key, connectionMask, closedMask, blockedMask, data, frameMaterial, + frameMask, coverMask, randomSource, modelData, renderType); + + if (key.isActive() && allowActive()) { + if (emissiveActive) { + ((ActivableSQC) pipeCache.get(key)).addOverlay(quads, connectionMask, data, true); + // TODO bake this into the original quads + quads = quads.stream() + .map(quad -> Quad.from(quad).setLight(15, 15).rebake()) + .collect(Collectors.toList()); + } + ((ActivableSQC) pipeCache.get(key)).addOverlay(quads, connectionMask, data, true); + } else { + ((ActivableSQC) pipeCache.get(key)).addOverlay(quads, connectionMask, data, false); + } + return quads; + } + + @Override + protected @NotNull ActivableCacheKey toKey(@NotNull ModelData state) { + return ActivableCacheKey.of(state.get(THICKNESS_PROPERTY), state.get(ACTIVE_PROPERTY)); + } + + @Override + public SpriteInformation getParticleSprite(@Nullable Material material) { + return sideSprite; + } + + @Override + protected StructureQuadCache constructForKey(ActivableCacheKey key) { + if (inSprite == null) { + inSprite = new SpriteInformation(ModelFactory.getBlockSprite(inTex.texture()), inTex.colorID()); + } + if (sideSprite == null) { + sideSprite = new SpriteInformation(ModelFactory.getBlockSprite(sideTex.texture()), sideTex.colorID()); + } + if (overlaySprite == null) { + overlaySprite = new SpriteInformation(ModelFactory.getBlockSprite(overlayTex.texture()), + overlayTex.colorID()); + } + if (overlayActiveSprite == null) { + overlayActiveSprite = new SpriteInformation(ModelFactory.getBlockSprite(overlayActiveTex.texture()), + overlayActiveTex.colorID()); + } + + return ActivableSQC.create(PipeQuadHelper.create(key.getThickness()), inSprite, sideSprite, + overlaySprite, overlayActiveSprite); + } + + public boolean allowActive() { + return !ConfigHolder.INSTANCE.client.preventAnimatedCables; + } + + @Override + protected @Nullable PipeItemModel getItemModel(PipeModelRedirector redirector, + @NotNull ItemStack stack, ClientLevel world, + LivingEntity entity) { + PipeBlock block = PipeBlock.getBlockFromItem(stack); + if (block == null) return null; + return new PipeItemModel<>(redirector, this, + new ActivableCacheKey(block.getStructure().getRenderThickness(), false), + new ColorData(PipeBlockEntity.DEFAULT_COLOR)); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/CableModel.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/CableModel.java new file mode 100644 index 0000000000..9a30ff1fdd --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/CableModel.java @@ -0,0 +1,130 @@ +package com.gregtechceu.gtceu.client.renderer.pipe; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.data.chemical.material.Material; +import com.gregtechceu.gtceu.api.data.chemical.material.info.MaterialIconType; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.block.PipeBlock; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.block.PipeMaterialBlock; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; +import com.gregtechceu.gtceu.client.renderer.pipe.cache.ExtraCappedSQC; +import com.gregtechceu.gtceu.client.renderer.pipe.cache.StructureQuadCache; +import com.gregtechceu.gtceu.client.renderer.pipe.quad.PipeQuadHelper; +import com.gregtechceu.gtceu.client.renderer.pipe.util.CacheKey; +import com.gregtechceu.gtceu.client.renderer.pipe.util.ColorData; +import com.gregtechceu.gtceu.client.renderer.pipe.util.SpriteInformation; +import com.gregtechceu.gtceu.client.renderer.pipe.util.TextureInformation; +import com.gregtechceu.gtceu.utils.GTUtil; + +import com.lowdragmc.lowdraglib.client.model.ModelFactory; + +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.client.model.data.ModelData; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class CableModel extends AbstractPipeModel { + + public static final int DEFAULT_INSULATION_COLOR = 0xFF404040; + + private static final ResourceLocation loc = GTCEu.id("pipe_cable"); + + public static final TextureInformation WIRE = new TextureInformation(GTCEu.id("block/cable/wire"), 0); + public static final TextureInformation[] INSULATION = new TextureInformation[5]; + public static final TextureInformation INSULATION_FULL = new TextureInformation( + GTCEu.id("block/cable/insulation_5"), 1); + + static { + for (int i = 0; i < INSULATION.length; i++) { + INSULATION[i] = new TextureInformation(GTCEu.id("block/cable/insulation_%s".formatted(i)), 1); + } + } + + private final TextureInformation wireTex; + private final TextureInformation insulationTex; + private final TextureInformation fullInsulationTex; + + private final Material material; + + private SpriteInformation wireSprite; + private SpriteInformation insulationSprite; + private SpriteInformation fullInsulationSprite; + + public CableModel(@Nullable Material material, @Nullable TextureInformation insulationTex, + @Nullable TextureInformation fullInsulationTex) { + this.material = material; + this.wireTex = material != null ? + new TextureInformation( + MaterialIconType.wire.getBlockTexturePath(material.getMaterialIconSet(), "side", true), 0) : + WIRE; + this.insulationTex = insulationTex; + this.fullInsulationTex = fullInsulationTex; + } + + public CableModel(@NotNull Material material) { + this(material, null, null); + } + + @Override + protected ColorData computeColorData(@NotNull ModelData ext) { + if (insulationTex == null) return super.computeColorData(ext); + Material material = ext.get(AbstractPipeModel.MATERIAL_PROPERTY); + int insulationColor = safeInt(ext.get(COLOR_PROPERTY)); + if (material != null) { + int matColor = GTUtil.convertRGBtoARGB(material.getMaterialRGB()); + if (insulationColor == 0 || insulationColor == matColor) { + // unpainted + insulationColor = DEFAULT_INSULATION_COLOR; + } + return new ColorData(matColor, insulationColor); + } + return new ColorData(0, 0); + } + + @Override + public SpriteInformation getParticleSprite(@Nullable Material material) { + return wireSprite; + } + + @Override + protected @NotNull CacheKey toKey(@NotNull ModelData state) { + return defaultKey(state); + } + + @Override + protected StructureQuadCache constructForKey(CacheKey key) { + if (fullInsulationSprite == null && fullInsulationTex != null) { + fullInsulationSprite = new SpriteInformation(ModelFactory.getBlockSprite(fullInsulationTex.texture()), + fullInsulationTex.colorID()); + } + if (insulationSprite == null && insulationTex != null) { + insulationSprite = new SpriteInformation(ModelFactory.getBlockSprite(insulationTex.texture()), + insulationTex.colorID()); + } + if (wireSprite == null && wireTex != null) { + wireSprite = new SpriteInformation(ModelFactory.getBlockSprite(wireTex.texture()), wireTex.colorID()); + } + + SpriteInformation sideTex = fullInsulationSprite != null ? fullInsulationSprite : wireSprite; + if (insulationSprite == null) { + return StructureQuadCache.create(PipeQuadHelper.create(key.getThickness()), wireSprite, sideTex); + } else { + return ExtraCappedSQC.create(PipeQuadHelper.create(key.getThickness()), wireSprite, sideTex, + insulationSprite); + } + } + + @Override + protected @Nullable PipeItemModel getItemModel(PipeModelRedirector redirector, @NotNull ItemStack stack, + ClientLevel world, LivingEntity entity) { + PipeBlock block = PipeBlock.getBlockFromItem(stack); + if (block == null) return null; + Material mater = block instanceof PipeMaterialBlock mat ? mat.material : null; + return new PipeItemModel<>(redirector, this, new CacheKey(block.getStructure().getRenderThickness()), + new ColorData(mater != null ? GTUtil.convertRGBtoARGB(mater.getMaterialRGB()) : + PipeBlockEntity.DEFAULT_COLOR, DEFAULT_INSULATION_COLOR)); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/DuctPipeModel.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/DuctPipeModel.java new file mode 100644 index 0000000000..490528a02f --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/DuctPipeModel.java @@ -0,0 +1,65 @@ +package com.gregtechceu.gtceu.client.renderer.pipe; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.data.chemical.material.Material; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.block.PipeBlock; +import com.gregtechceu.gtceu.client.renderer.pipe.cache.StructureQuadCache; +import com.gregtechceu.gtceu.client.renderer.pipe.quad.PipeQuadHelper; +import com.gregtechceu.gtceu.client.renderer.pipe.util.CacheKey; +import com.gregtechceu.gtceu.client.renderer.pipe.util.ColorData; +import com.gregtechceu.gtceu.client.renderer.pipe.util.SpriteInformation; + +import com.lowdragmc.lowdraglib.client.model.ModelFactory; + +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.client.model.data.ModelData; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class DuctPipeModel extends AbstractPipeModel { + + private static final ResourceLocation loc = GTCEu.id("pipe_duct"); + + public static final DuctPipeModel INSTANCE = new DuctPipeModel(); + + private static final ResourceLocation SIDE_TEXTURE = GTCEu.id("block/pipe/pipe_duct_side"); + private static final ResourceLocation END_TEXTURE = GTCEu.id("block/pipe/pipe_duct_in"); + + private SpriteInformation sideSprite; + private SpriteInformation endSprite; + + @Override + protected @NotNull CacheKey toKey(@NotNull ModelData state) { + return defaultKey(state); + } + + @Override + protected StructureQuadCache constructForKey(CacheKey key) { + if (sideSprite == null) { + sideSprite = new SpriteInformation(ModelFactory.getBlockSprite(SIDE_TEXTURE), -1); + } + if (endSprite == null) { + endSprite = new SpriteInformation(ModelFactory.getBlockSprite(END_TEXTURE), -1); + } + + return StructureQuadCache.create(PipeQuadHelper.create(key.getThickness()), endSprite, sideSprite); + } + + @Override + public SpriteInformation getParticleSprite(@Nullable Material material) { + return sideSprite; + } + + @Override + protected @Nullable PipeItemModel getItemModel(PipeModelRedirector redirector, @NotNull ItemStack stack, + ClientLevel world, LivingEntity entity) { + PipeBlock block = PipeBlock.getBlockFromItem(stack); + if (block == null) return null; + return new PipeItemModel<>(redirector, this, new CacheKey(block.getStructure().getRenderThickness()), + new ColorData()); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/PipeItemModel.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/PipeItemModel.java new file mode 100644 index 0000000000..c63e28ac66 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/PipeItemModel.java @@ -0,0 +1,152 @@ +package com.gregtechceu.gtceu.client.renderer.pipe; + +import com.gregtechceu.gtceu.client.renderer.pipe.util.CacheKey; +import com.gregtechceu.gtceu.client.renderer.pipe.util.ColorData; + +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.client.renderer.block.model.ItemOverrides; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.core.Direction; +import net.minecraft.util.RandomSource; +import net.minecraft.world.item.ItemDisplayContext; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.client.model.IDynamicBakedModel; +import net.minecraftforge.client.model.data.ModelData; + +import com.mojang.blaze3d.vertex.PoseStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.joml.Matrix4f; +import org.joml.Quaternionf; +import org.joml.Vector3f; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class PipeItemModel implements IDynamicBakedModel { + + private static final Map CAMERA_TRANSFORMS = new HashMap<>(); + + static { + CAMERA_TRANSFORMS.put(ItemDisplayContext.NONE, mul(null, null, null, null)); + CAMERA_TRANSFORMS.put(ItemDisplayContext.GUI, mul(null, rotDegrees(30, -45, 0), scale(0.625f), null)); + CAMERA_TRANSFORMS.put(ItemDisplayContext.GROUND, mul(null, null, scale(0.25f), null)); + CAMERA_TRANSFORMS.put(ItemDisplayContext.FIXED, mul(null, rotDegrees(0, 90, 0), scale(0.5f), null)); + Matrix4f matrix4f = mul(null, rotDegrees(75, 45, 0), scale(0.375f), null); + CAMERA_TRANSFORMS.put(ItemDisplayContext.THIRD_PERSON_RIGHT_HAND, matrix4f); + CAMERA_TRANSFORMS.put(ItemDisplayContext.THIRD_PERSON_LEFT_HAND, matrix4f); + matrix4f = mul(null, rotDegrees(0, 45, 0), scale(0.4f), null); + CAMERA_TRANSFORMS.put(ItemDisplayContext.FIRST_PERSON_RIGHT_HAND, matrix4f); + CAMERA_TRANSFORMS.put(ItemDisplayContext.FIRST_PERSON_LEFT_HAND, matrix4f); + } + + private static Vector3f scale(float scale) { + return new Vector3f(scale, scale, scale); + } + + private static Quaternionf rotDegrees(float x, float y, float z) { + return quatFromXYZDegrees(new Vector3f(x, y, z)); + } + + private final PipeModelRedirector redirector; + private final AbstractPipeModel basis; + private final K key; + private final ColorData data; + + public PipeItemModel(PipeModelRedirector redirector, AbstractPipeModel basis, K key, ColorData data) { + this.redirector = redirector; + this.basis = basis; + this.key = key; + this.data = data; + } + + @Override + public @NotNull List getQuads(@Nullable BlockState state, @Nullable Direction side, + @NotNull RandomSource rand, @NotNull ModelData modelData, + @Nullable RenderType renderType) { + byte z = 0; + return basis.getQuads(key, (byte) 0b1100, z, z, data, null, z, z, rand, modelData, renderType); + } + + @Override + public boolean useAmbientOcclusion() { + return redirector.useAmbientOcclusion(); + } + + @Override + public boolean isGui3d() { + return redirector.isGui3d(); + } + + @Override + public boolean usesBlockLight() { + return redirector.usesBlockLight(); + } + + @Override + public boolean isCustomRenderer() { + return false; + } + + @Override + public BakedModel applyTransform(ItemDisplayContext transformType, PoseStack poseStack, + boolean applyLeftHandTransform) { + poseStack.mulPoseMatrix(CAMERA_TRANSFORMS.get(transformType)); + return this; + } + + @Override + public TextureAtlasSprite getParticleIcon() { + return redirector.getParticleIcon(); + } + + @Override + public ItemOverrides getOverrides() { + return ItemOverrides.EMPTY; + } + + public static Matrix4f mul(@Nullable Vector3f translation, @Nullable Quaternionf leftRot, @Nullable Vector3f scale, + @Nullable Quaternionf rightRot) { + Matrix4f res = new Matrix4f(), t = new Matrix4f(); + res.identity(); + if (leftRot != null) { + t.set(leftRot); + res.mul(t); + } + if (scale != null) { + t.identity(); + t.m00(scale.x); + t.m11(scale.y); + t.m22(scale.z); + res.mul(t); + } + if (rightRot != null) { + t.set(rightRot); + res.mul(t); + } + if (translation != null) res.setTranslation(translation); + return res; + } + + public static Quaternionf quatFromXYZDegrees(Vector3f xyz) { + return quatFromXYZ((float) Math.toRadians(xyz.x), (float) Math.toRadians(xyz.y), (float) Math.toRadians(xyz.z)); + } + + public static Quaternionf quatFromXYZ(Vector3f xyz) { + return quatFromXYZ(xyz.x, xyz.y, xyz.z); + } + + public static Quaternionf quatFromXYZ(float x, float y, float z) { + Quaternionf ret = new Quaternionf(0, 0, 0, 1), t = new Quaternionf(); + t.set((float) Math.sin(x / 2), 0, 0, (float) Math.cos(x / 2)); + ret.mul(t); + t.set(0, (float) Math.sin(y / 2), 0, (float) Math.cos(y / 2)); + ret.mul(t); + t.set(0, 0, (float) Math.sin(z / 2), (float) Math.cos(z / 2)); + ret.mul(t); + return ret; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/PipeModel.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/PipeModel.java new file mode 100644 index 0000000000..c712805e49 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/PipeModel.java @@ -0,0 +1,102 @@ +package com.gregtechceu.gtceu.client.renderer.pipe; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.data.chemical.material.Material; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.block.PipeBlock; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.block.PipeMaterialBlock; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; +import com.gregtechceu.gtceu.client.renderer.pipe.cache.BlockableSQC; +import com.gregtechceu.gtceu.client.renderer.pipe.cache.RestrictiveSQC; +import com.gregtechceu.gtceu.client.renderer.pipe.cache.StructureQuadCache; +import com.gregtechceu.gtceu.client.renderer.pipe.quad.PipeQuadHelper; +import com.gregtechceu.gtceu.client.renderer.pipe.util.CacheKey; +import com.gregtechceu.gtceu.client.renderer.pipe.util.ColorData; +import com.gregtechceu.gtceu.client.renderer.pipe.util.SpriteInformation; +import com.gregtechceu.gtceu.client.renderer.pipe.util.TextureInformation; +import com.gregtechceu.gtceu.utils.GTUtil; + +import com.lowdragmc.lowdraglib.client.model.ModelFactory; + +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.client.model.data.ModelData; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class PipeModel extends AbstractPipeModel { + + private final @NotNull TextureInformation inTex; + private final @NotNull TextureInformation sideTex; + private final @Nullable TextureInformation restrictiveTex; + private final @NotNull TextureInformation blockedTex; + + private SpriteInformation inSprite; + private SpriteInformation sideSprite; + private SpriteInformation restrictiveSprite; + private SpriteInformation blockedSprite; + + public PipeModel(@NotNull TextureInformation inTex, @NotNull TextureInformation sideTex, + @Nullable TextureInformation restrictiveTex, + @NotNull TextureInformation blockedTex) { + this.inTex = inTex; + this.sideTex = sideTex; + this.restrictiveTex = restrictiveTex; + this.blockedTex = blockedTex; + } + + public PipeModel(@NotNull TextureInformation inTex, @NotNull TextureInformation sideTex, + boolean restrictive) { + this(inTex, sideTex, restrictive ? new TextureInformation(GTCEu.id("block/pipe/pipe_restrictive"), -1) : null, + new TextureInformation(GTCEu.id("block/pipe/pipe_blocked"), -1)); + } + + @Override + public SpriteInformation getParticleSprite(@Nullable Material material) { + return sideSprite; + } + + @Override + protected @NotNull CacheKey toKey(@NotNull ModelData data) { + return defaultKey(data); + } + + @Override + protected StructureQuadCache constructForKey(CacheKey key) { + if (inSprite == null) { + inSprite = new SpriteInformation(ModelFactory.getBlockSprite(inTex.texture()), inTex.colorID()); + } + if (sideSprite == null) { + sideSprite = new SpriteInformation(ModelFactory.getBlockSprite(sideTex.texture()), sideTex.colorID()); + } + if (restrictiveSprite == null && restrictiveTex != null) { + restrictiveSprite = new SpriteInformation(ModelFactory.getBlockSprite(restrictiveTex.texture()), + restrictiveTex.colorID()); + } + if (blockedSprite == null) { + blockedSprite = new SpriteInformation(ModelFactory.getBlockSprite(blockedTex.texture()), + blockedTex.colorID()); + } + + if (restrictiveTex != null) { + return RestrictiveSQC.create(PipeQuadHelper.create(key.getThickness()), inSprite, sideSprite, + blockedSprite, restrictiveSprite); + } else { + return BlockableSQC.create(PipeQuadHelper.create(key.getThickness()), inSprite, sideSprite, + blockedSprite); + } + } + + @Override + @Nullable + protected PipeItemModel getItemModel(PipeModelRedirector redirector, @NotNull ItemStack stack, + ClientLevel world, LivingEntity entity) { + PipeBlock block = PipeBlock.getBlockFromItem(stack); + if (block == null) return null; + Material mater = block instanceof PipeMaterialBlock matBlock ? matBlock.material : null; + return new PipeItemModel<>(redirector, this, new CacheKey(block.getStructure().getRenderThickness()), + new ColorData(mater != null ? GTUtil.convertRGBtoARGB(mater.getMaterialRGB()) : + PipeBlockEntity.DEFAULT_COLOR)); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/PipeModelRedirector.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/PipeModelRedirector.java new file mode 100644 index 0000000000..b617e08d3a --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/PipeModelRedirector.java @@ -0,0 +1,137 @@ +package com.gregtechceu.gtceu.client.renderer.pipe; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.data.chemical.material.Material; +import com.gregtechceu.gtceu.client.renderer.pipe.util.MaterialModelSupplier; +import com.gregtechceu.gtceu.common.pipelike.block.pipe.MaterialPipeBlock; + +import com.lowdragmc.lowdraglib.client.model.ModelFactory; + +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.client.renderer.block.model.ItemOverrides; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.client.resources.model.ModelResourceLocation; +import net.minecraft.core.Direction; +import net.minecraft.util.RandomSource; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.client.model.data.ModelData; + +import lombok.Getter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.Function; + +@OnlyIn(Dist.CLIENT) +public class PipeModelRedirector implements BakedModel { + + private final boolean ambientOcclusion; + private final boolean gui3d; + + public final MaterialModelSupplier supplier; + public final Function stackMaterialFunction; + + @Getter + private final ModelResourceLocation loc; + + private final FakeItemOverrides fakeItemOverrideList = new FakeItemOverrides(); + + public PipeModelRedirector(ModelResourceLocation loc, MaterialModelSupplier supplier, + Function stackMaterialFunction) { + this(loc, supplier, stackMaterialFunction, true, true); + } + + public PipeModelRedirector(ModelResourceLocation loc, MaterialModelSupplier supplier, + Function stackMaterialFunction, + boolean ambientOcclusion, boolean gui3d) { + this.loc = loc; + this.supplier = supplier; + this.stackMaterialFunction = stackMaterialFunction; + this.ambientOcclusion = ambientOcclusion; + this.gui3d = gui3d; + + PipeModelRegistry.MODELS.put(loc, this); + } + + @Override + public List getQuads(@Nullable BlockState state, @Nullable Direction direction, RandomSource random) { + return List.of(); + } + + @Override + public @NotNull List getQuads(@Nullable BlockState state, @Nullable Direction side, + @NotNull RandomSource rand, @NotNull ModelData data, + @Nullable RenderType renderType) { + Material mat = data.get(AbstractPipeModel.MATERIAL_PROPERTY); + if (mat == null && state != null && state.getBlock() instanceof MaterialPipeBlock block) { + mat = block.material; + } + AbstractPipeModel model = supplier.getModel(mat); + // this can happen when transferring old data, apparently. + // noinspection ConstantValue + if (model == null) { + return List.of(); + } + return model.getQuads(state, side, rand, data, renderType); + } + + @Override + public boolean useAmbientOcclusion() { + return ambientOcclusion; + } + + @Override + public boolean isGui3d() { + return gui3d; + } + + @Override + public boolean usesBlockLight() { + return false; + } + + @Override + public boolean isCustomRenderer() { + return false; + } + + @Override + public TextureAtlasSprite getParticleIcon() { + return ModelFactory.getBlockSprite(GTCEu.id("block/cable/wire")); + } + + @Override + public @NotNull ItemOverrides getOverrides() { + return fakeItemOverrideList; + } + + @FunctionalInterface + public interface ModelRedirectorSupplier { + + PipeModelRedirector create(ModelResourceLocation loc, MaterialModelSupplier supplier, + Function stackMaterialFunction); + } + + protected class FakeItemOverrides extends ItemOverrides { + + @Nullable + @Override + public BakedModel resolve(BakedModel originalModel, ItemStack stack, @Nullable ClientLevel level, + @Nullable LivingEntity entity, int seed) { + if (originalModel instanceof PipeModelRedirector model) { + PipeItemModel item = model.supplier.getModel(model.stackMaterialFunction.apply(stack)) + .getItemModel(PipeModelRedirector.this, stack, level, entity); + if (item != null) return item; + } + return originalModel; + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/PipeModelRegistry.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/PipeModelRegistry.java new file mode 100644 index 0000000000..2aefabfbd6 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/PipeModelRegistry.java @@ -0,0 +1,266 @@ +package com.gregtechceu.gtceu.client.renderer.pipe; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.data.chemical.material.Material; +import com.gregtechceu.gtceu.api.data.chemical.material.properties.PropertyKey; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.block.PipeMaterialBlock; +import com.gregtechceu.gtceu.client.renderer.pipe.util.MaterialModelOverride; +import com.gregtechceu.gtceu.client.renderer.pipe.util.MaterialModelSupplier; +import com.gregtechceu.gtceu.client.renderer.pipe.util.TextureInformation; + +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.client.resources.model.ModelResourceLocation; +import net.minecraft.resources.ResourceLocation; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import com.google.common.collect.Tables; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Range; + +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.Map; +import java.util.function.BiConsumer; + +@OnlyIn(Dist.CLIENT) +public final class PipeModelRegistry { + + public static final Map MODELS = new HashMap<>(); + + public static final int PIPE_MODEL_COUNT = 7; + private static final Object2ObjectOpenHashMap PIPE = new Object2ObjectOpenHashMap<>(); + private static final PipeModelRedirector[] PIPE_MODELS = new PipeModelRedirector[PIPE_MODEL_COUNT]; + private static final ObjectLinkedOpenHashSet> PIPE_OVERRIDES = new ObjectLinkedOpenHashSet<>(); + private static final Object2ObjectOpenHashMap PIPE_RESTRICTIVE = new Object2ObjectOpenHashMap<>(); + private static final PipeModelRedirector[] PIPE_RESTRICTIVE_MODELS = new PipeModelRedirector[PIPE_MODEL_COUNT]; + private static final ObjectLinkedOpenHashSet> PIPE_RESTRICTIVE_OVERRIDES = new ObjectLinkedOpenHashSet<>(); + + public static final int CABLE_MODEL_COUNT = 6; + private static final Object2ObjectOpenHashMap CABLE = new Object2ObjectOpenHashMap<>(); + private static final PipeModelRedirector[] CABLE_MODELS = new PipeModelRedirector[CABLE_MODEL_COUNT]; + private static final ObjectLinkedOpenHashSet> CABLE_OVERRIDES = new ObjectLinkedOpenHashSet<>(); + + private static final ActivablePipeModel OPTICAL; + private static final PipeModelRedirector OPTICAL_MODEL; + + private static final ActivablePipeModel LASER; + private static final PipeModelRedirector LASER_MODEL; + + private static final DuctPipeModel DUCT; + private static final PipeModelRedirector DUCT_MODEL; + + static { + initPipes(); + initCables(); + ResourceLocation loc = GTCEu.id("pipe_activable"); + OPTICAL = new ActivablePipeModel( + new TextureInformation(GTCEu.id("block/pipe/pipe_optical_in"), -1), + new TextureInformation(GTCEu.id("block/pipe/pipe_optical_side"), -1), + new TextureInformation(GTCEu.id("block/pipe/pipe_optical_side_overlay"), 0), + new TextureInformation(GTCEu.id("block/pipe/pipe_optical_side_overlay_active"), 0), + false); + OPTICAL_MODEL = new PipeModelRedirector(new ModelResourceLocation(loc, "optical"), m -> OPTICAL, s -> null); + LASER = new ActivablePipeModel( + new TextureInformation(GTCEu.id("block/pipe/pipe_laser_in"), -1), + new TextureInformation(GTCEu.id("block/pipe/pipe_laser_side"), -1), + new TextureInformation(GTCEu.id("block/pipe/pipe_laser_side_overlay"), 0), + new TextureInformation(GTCEu.id("block/pipe/pipe_laser_side_overlay_emissive"), 0), + true); + LASER_MODEL = new PipeModelRedirector(new ModelResourceLocation(loc, "laser"), m -> LASER, s -> null); + DUCT = new DuctPipeModel(); + DUCT_MODEL = new PipeModelRedirector(new ModelResourceLocation(GTCEu.id("pipe_duct"), ""), m -> DUCT, + s -> null); + } + + public static void registerPipeOverride(@NotNull MaterialModelOverride override) { + PIPE_OVERRIDES.addAndMoveToFirst(override); + PIPE.clear(); + PIPE.trim(16); + } + + public static void registerPipeRestrictiveOverride(@NotNull MaterialModelOverride override) { + PIPE_RESTRICTIVE_OVERRIDES.addAndMoveToFirst(override); + PIPE_RESTRICTIVE.clear(); + PIPE_RESTRICTIVE.trim(16); + } + + public static void registerCableOverride(@NotNull MaterialModelOverride override) { + CABLE_OVERRIDES.addAndMoveToFirst(override); + CABLE.clear(); + CABLE.trim(16); + } + + public static PipeModelRedirector getPipeModel(@Range(from = 0, to = PIPE_MODEL_COUNT - 1) int i) { + return PIPE_MODELS[i]; + } + + public static PipeModelRedirector getPipeRestrictiveModel(@Range(from = 0, to = PIPE_MODEL_COUNT - 1) int i) { + return PIPE_RESTRICTIVE_MODELS[i]; + } + + public static PipeModelRedirector getCableModel(@Range(from = 0, to = CABLE_MODEL_COUNT - 1) int i) { + return CABLE_MODELS[i]; + } + + public static PipeModelRedirector getOpticalModel() { + return OPTICAL_MODEL; + } + + public static PipeModelRedirector getLaserModel() { + return LASER_MODEL; + } + + public static PipeModelRedirector getDuctModel() { + return DUCT_MODEL; + } + + public static void registerModels(@NotNull BiConsumer registry) { + for (PipeModelRedirector redirector : PIPE_MODELS) { + registry.accept(redirector.getLoc(), redirector); + } + for (PipeModelRedirector redirector : PIPE_RESTRICTIVE_MODELS) { + registry.accept(redirector.getLoc(), redirector); + } + for (PipeModelRedirector redirector : CABLE_MODELS) { + registry.accept(redirector.getLoc(), redirector); + } + registry.accept(OPTICAL_MODEL.getLoc(), OPTICAL_MODEL); + registry.accept(LASER_MODEL.getLoc(), LASER_MODEL); + } + + public static PipeModelRedirector materialModel(@NotNull ResourceLocation loc, MaterialModelSupplier supplier, + @NotNull String variant, + PipeModelRedirector.@NotNull ModelRedirectorSupplier redirectorSupplier) { + return redirectorSupplier.create(new ModelResourceLocation(loc, variant), supplier, + stack -> { + PipeMaterialBlock pipe = PipeMaterialBlock.getBlockFromItem(stack); + if (pipe == null) return null; + else return pipe.material; + }); + } + + public static PipeModelRedirector materialModel(@NotNull ResourceLocation loc, MaterialModelSupplier supplier, + @NotNull String variant) { + return new PipeModelRedirector(new ModelResourceLocation(loc, variant), supplier, + stack -> { + PipeMaterialBlock pipe = PipeMaterialBlock.getBlockFromItem(stack); + if (pipe == null) return null; + else return pipe.material; + }); + } + + private static void initPipes() { + TextureInformation pipeTiny = new TextureInformation(GTCEu.id("block/pipe/pipe_tiny_in"), 0); + TextureInformation pipeSmall = new TextureInformation(GTCEu.id("block/pipe/pipe_small_in"), 0); + TextureInformation pipeNormal = new TextureInformation(GTCEu.id("block/pipe/pipe_normal_in"), 0); + TextureInformation pipeLarge = new TextureInformation(GTCEu.id("block/pipe/pipe_large_in"), 0); + TextureInformation pipeHuge = new TextureInformation(GTCEu.id("block/pipe/pipe_huge_in"), 0); + TextureInformation pipeQuadruple = new TextureInformation(GTCEu.id("block/pipe/pipe_quadruple_in"), 0); + TextureInformation pipeNonuple = new TextureInformation(GTCEu.id("block/pipe/pipe_nonuple_in"), 0); + TextureInformation pipeSide = new TextureInformation(GTCEu.id("block/pipe/pipe_side"), 0); + + TextureInformation pipeSmallWood = new TextureInformation(GTCEu.id("block/pipe/pipe_small_in_wood"), 0); + TextureInformation pipeNormalWood = new TextureInformation(GTCEu.id("block/pipe/pipe_normal_in_wood"), 0); + TextureInformation pipeLargeWood = new TextureInformation(GTCEu.id("block/pipe/pipe_large_in_wood"), 0); + TextureInformation pipeSideWood = new TextureInformation(GTCEu.id("block/pipe/pipe_side_wood"), 0); + + PipeModel[] array = new PipeModel[PIPE_MODEL_COUNT]; + // standard + array[0] = new PipeModel(pipeTiny, pipeSide, false); + array[1] = new PipeModel(pipeSmall, pipeSide, false); + array[2] = new PipeModel(pipeNormal, pipeSide, false); + array[3] = new PipeModel(pipeLarge, pipeSide, false); + array[4] = new PipeModel(pipeHuge, pipeSide, false); + array[5] = new PipeModel(pipeQuadruple, pipeSide, false); + array[6] = new PipeModel(pipeNonuple, pipeSide, false); + PIPE_OVERRIDES.addAndMoveToLast(new MaterialModelOverride.StandardOverride<>(array, m -> true)); + + array = new PipeModel[PIPE_MODEL_COUNT]; + array[1] = new PipeModel(pipeSmallWood, pipeSideWood, false); + array[2] = new PipeModel(pipeNormalWood, pipeSideWood, false); + array[3] = new PipeModel(pipeLargeWood, pipeSideWood, false); + registerPipeOverride( + new MaterialModelOverride.StandardOverride<>(array, m -> m != null && m.hasProperty(PropertyKey.WOOD))); + + array = new PipeModel[PIPE_MODEL_COUNT]; + array[0] = new PipeModel(pipeTiny, pipeSide, true); + array[1] = new PipeModel(pipeSmall, pipeSide, true); + array[2] = new PipeModel(pipeNormal, pipeSide, true); + array[3] = new PipeModel(pipeLarge, pipeSide, true); + array[4] = new PipeModel(pipeHuge, pipeSide, true); + array[5] = new PipeModel(pipeQuadruple, pipeSide, true); + array[6] = new PipeModel(pipeNonuple, pipeSide, true); + PIPE_RESTRICTIVE_OVERRIDES.addAndMoveToLast(new MaterialModelOverride.StandardOverride<>(array, m -> true)); + + ResourceLocation loc = GTCEu.id("pipe_material"); + for (int i = 0; i < PIPE_MODEL_COUNT; i++) { + int finalI = i; + PIPE_MODELS[i] = materialModel(loc, m -> getOrCachePipeModel(m, finalI), String.valueOf(i)); + PIPE_RESTRICTIVE_MODELS[i] = materialModel(loc, m -> getOrCachePipeRestrictiveModel(m, finalI), + "restrictive_" + i); + } + } + + private static PipeModel getOrCachePipeModel(@Nullable Material m, int i) { + if (m == null) return PIPE_OVERRIDES.last().getModel(null, i); + PipeModel[] cached = PIPE.computeIfAbsent(m, k -> new PipeModel[PIPE_MODEL_COUNT]); + PipeModel selected = cached[i]; + if (selected == null) { + for (MaterialModelOverride override : PIPE_OVERRIDES) { + selected = override.getModel(m, i); + if (selected != null) break; + } + cached[i] = selected; + } + return selected; + } + + private static PipeModel getOrCachePipeRestrictiveModel(Material m, int i) { + if (m == null) return PIPE_RESTRICTIVE_OVERRIDES.last().getModel(null, i); + PipeModel[] cached = PIPE_RESTRICTIVE.computeIfAbsent(m, k -> new PipeModel[PIPE_MODEL_COUNT]); + PipeModel selected = cached[i]; + if (selected == null) { + for (MaterialModelOverride override : PIPE_RESTRICTIVE_OVERRIDES) { + selected = override.getModel(m, i); + if (selected != null) break; + } + cached[i] = selected; + } + return selected; + } + + private static void initCables() { + CABLE_OVERRIDES.addAndMoveToLast(new MaterialModelOverride.PerMaterialOverride<>( + Tables.newCustomTable(new IdentityHashMap<>(), Int2ObjectOpenHashMap::new), (material, insulation) -> { + if (insulation == 0) { + return new CableModel(material); + } + return new CableModel(material, CableModel.INSULATION[insulation - 1], CableModel.INSULATION_FULL); + }, m -> true)); + + ResourceLocation loc = GTCEu.id("cable"); + for (int i = 0; i < CABLE_MODEL_COUNT; i++) { + int finalI = i; + CABLE_MODELS[i] = materialModel(loc, m -> getOrCacheCableModel(m, finalI), String.valueOf(i)); + } + } + + private static CableModel getOrCacheCableModel(@Nullable Material m, int i) { + if (m == null) return CABLE_OVERRIDES.last().getModel(null, i); + CableModel[] cached = CABLE.computeIfAbsent(m, k -> new CableModel[CABLE_MODEL_COUNT]); + CableModel selected = cached[i]; + if (selected == null) { + for (MaterialModelOverride override : CABLE_OVERRIDES) { + selected = override.getModel(m, i); + if (selected != null) break; + } + cached[i] = selected; + } + return selected; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/UnbakedPipeModel.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/UnbakedPipeModel.java new file mode 100644 index 0000000000..eb2d081151 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/UnbakedPipeModel.java @@ -0,0 +1,49 @@ +package com.gregtechceu.gtceu.client.renderer.pipe; + +import net.minecraft.client.renderer.block.model.ItemOverrides; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.resources.model.*; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.GsonHelper; +import net.minecraftforge.client.model.geometry.IGeometryBakingContext; +import net.minecraftforge.client.model.geometry.IGeometryLoader; +import net.minecraftforge.client.model.geometry.IUnbakedGeometry; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import lombok.RequiredArgsConstructor; + +import java.util.function.Function; + +@RequiredArgsConstructor +public class UnbakedPipeModel implements IUnbakedGeometry { + + private final PipeModelRedirector model; + + @Override + public BakedModel bake(IGeometryBakingContext iGeometryBakingContext, ModelBaker baker, + Function function, ModelState arg2, ItemOverrides arg3, + ResourceLocation arg4) { + return model; + } + + public static final class Loader implements IGeometryLoader { + + public static final UnbakedPipeModel.Loader INSTANCE = new UnbakedPipeModel.Loader(); + + private Loader() {} + + @Override + public UnbakedPipeModel read(JsonObject jsonObject, + JsonDeserializationContext deserializationContext) throws JsonParseException { + if (!jsonObject.has("model_id")) + throw new JsonParseException("An UnbakedPipeModel must have an \"model_id\" member."); + + String[] id = GsonHelper.getAsString(jsonObject, "model_id").split("#"); + ResourceLocation modelId = new ModelResourceLocation(new ResourceLocation(id[0]), + id.length > 1 ? id[1] : ""); + return new UnbakedPipeModel(PipeModelRegistry.MODELS.get(modelId)); + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/cache/ActivableSQC.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/cache/ActivableSQC.java new file mode 100644 index 0000000000..819fd513e8 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/cache/ActivableSQC.java @@ -0,0 +1,85 @@ +package com.gregtechceu.gtceu.client.renderer.pipe.cache; + +import com.gregtechceu.gtceu.client.renderer.pipe.quad.PipeQuadHelper; +import com.gregtechceu.gtceu.client.renderer.pipe.quad.QuadHelper; +import com.gregtechceu.gtceu.client.renderer.pipe.util.ColorData; +import com.gregtechceu.gtceu.client.renderer.pipe.util.SpriteInformation; +import com.gregtechceu.gtceu.utils.GTUtil; + +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.core.Direction; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import org.jetbrains.annotations.NotNull; + +import java.util.EnumMap; +import java.util.List; + +@OnlyIn(Dist.CLIENT) +public class ActivableSQC extends StructureQuadCache { + + protected final EnumMap overlayCoords = new EnumMap<>(Direction.class); + protected final EnumMap overlayActiveCoords = new EnumMap<>(Direction.class); + + protected final SpriteInformation overlayTex; + protected final SpriteInformation overlayActiveTex; + + protected ActivableSQC(PipeQuadHelper helper, SpriteInformation endTex, SpriteInformation sideTex, + SpriteInformation overlayTex, SpriteInformation overlayActiveTex) { + super(helper, endTex, sideTex); + this.overlayTex = overlayTex; + this.overlayActiveTex = overlayActiveTex; + if (helper.getLayerCount() < 2) throw new IllegalStateException( + "Cannot create an ActivableSQC without 2 or more layers present on the helper!"); + } + + public static @NotNull ActivableSQC create(PipeQuadHelper helper, SpriteInformation endTex, + SpriteInformation sideTex, SpriteInformation overlayTex, + SpriteInformation overlayActiveTex) { + helper.initialize((facing, x1, y1, z1, x2, y2, z2) -> QuadHelper.tubeOverlay(facing, x1, y1, z1, x2, y2, z2, + OVERLAY_DIST_1)); + ActivableSQC cache = new ActivableSQC(helper, endTex, sideTex, overlayTex, overlayActiveTex); + cache.buildPrototype(); + return cache; + } + + @Override + protected List buildPrototypeInternal() { + List quads = super.buildPrototypeInternal(); + buildOverlay(quads); + buildOverlayActive(quads); + return quads; + } + + protected void buildOverlay(List list) { + helper.setTargetSprite(overlayTex); + for (Direction facing : GTUtil.DIRECTIONS) { + int start = list.size(); + list.addAll(helper.visitTube(facing, 1)); + overlayCoords.put(facing, new SubListAddress(start, list.size())); + } + } + + protected void buildOverlayActive(List list) { + helper.setTargetSprite(overlayActiveTex); + for (Direction facing : GTUtil.DIRECTIONS) { + int start = list.size(); + list.addAll(helper.visitTube(facing, 1)); + overlayActiveCoords.put(facing, new SubListAddress(start, list.size())); + } + } + + public void addOverlay(List list, byte overlayMask, ColorData data, boolean active) { + List quads = cache.getQuads(data); + for (Direction facing : GTUtil.DIRECTIONS) { + if (GTUtil.evalMask(facing, overlayMask)) { + if (active) { + list.addAll(overlayActiveCoords.get(facing).getSublist(quads)); + } else { + list.addAll(overlayCoords.get(facing).getSublist(quads)); + } + } + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/cache/BlockableSQC.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/cache/BlockableSQC.java new file mode 100644 index 0000000000..645e1e971a --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/cache/BlockableSQC.java @@ -0,0 +1,95 @@ +package com.gregtechceu.gtceu.client.renderer.pipe.cache; + +import com.gregtechceu.gtceu.client.renderer.pipe.quad.PipeQuadHelper; +import com.gregtechceu.gtceu.client.renderer.pipe.quad.QuadHelper; +import com.gregtechceu.gtceu.client.renderer.pipe.util.ColorData; +import com.gregtechceu.gtceu.client.renderer.pipe.util.SpriteInformation; +import com.gregtechceu.gtceu.utils.GTUtil; + +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.core.Direction; + +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.joml.Vector3f; + +import java.util.EnumMap; +import java.util.List; + +public class BlockableSQC extends StructureQuadCache { + + protected final EnumMap blockedCoords = new EnumMap<>(Direction.class); + + protected final SpriteInformation blockedTex; + + protected BlockableSQC(PipeQuadHelper helper, SpriteInformation endTex, SpriteInformation sideTex, + SpriteInformation blockedTex) { + super(helper, endTex, sideTex); + this.blockedTex = blockedTex; + if (helper.getLayerCount() < 2) throw new IllegalStateException( + "Cannot create a BlockableSQC without 2 or more layers present on the helper!"); + } + + public static @NotNull BlockableSQC create(PipeQuadHelper helper, SpriteInformation endTex, + SpriteInformation sideTex, SpriteInformation blockedTex) { + helper.initialize((facing, x1, y1, z1, x2, y2, z2) -> minLengthTube(facing, x1, y1, z1, x2, y2, z2, + OVERLAY_DIST_1, 4)); + BlockableSQC cache = new BlockableSQC(helper, endTex, sideTex, blockedTex); + cache.buildPrototype(); + return cache; + } + + public static ImmutablePair minLengthTube(@Nullable Direction facing, float x1, float y1, + float z1, float x2, + float y2, float z2, float g, float minLength) { + if (facing == null) return QuadHelper.tubeOverlay(facing, x1, y1, z1, x2, y2, z2, g); + return switch (facing) { + case UP -> QuadHelper.tubeOverlay(facing, x1, Math.min(y1, y2 - minLength), z1, x2, y2, z2, g); + case DOWN -> QuadHelper.tubeOverlay(facing, x1, y1, z1, x2, Math.max(y2, y1 + minLength), z2, g); + case EAST -> QuadHelper.tubeOverlay(facing, Math.min(x1, x2 - minLength), y1, z1, x2, y2, z2, g); + case WEST -> QuadHelper.tubeOverlay(facing, x1, y1, z1, Math.max(x2, x1 + minLength), y2, z2, g); + case SOUTH -> QuadHelper.tubeOverlay(facing, x1, y1, Math.min(z1, z2 - minLength), x2, y2, z2, g); + case NORTH -> QuadHelper.tubeOverlay(facing, x1, y1, z1, x2, y2, Math.max(z2, z1 + minLength), g); + }; + } + + @Override + protected List buildPrototypeInternal() { + List quads = super.buildPrototypeInternal(); + buildBlocked(quads); + return quads; + } + + protected void buildBlocked(List list) { + helper.setTargetSprite(blockedTex); + for (Direction facing : GTUtil.DIRECTIONS) { + int start = list.size(); + list.addAll(helper.visitTube(facing, 1)); + blockedCoords.put(facing, new SubListAddress(start, list.size())); + } + } + + @Override + public void addToList(List list, byte connectionMask, byte closedMask, byte blockedMask, ColorData data, + byte coverMask) { + List quads = cache.getQuads(data); + for (Direction facing : GTUtil.DIRECTIONS) { + if (GTUtil.evalMask(facing, connectionMask)) { + list.addAll(tubeCoords.get(facing).getSublist(quads)); + if (!GTUtil.evalMask(facing, coverMask)) { + if (GTUtil.evalMask(facing, closedMask)) { + list.addAll(capperClosedCoords.get(facing).getSublist(quads)); + } else { + list.addAll(capperCoords.get(facing).getSublist(quads)); + } + } + if (GTUtil.evalMask(facing, blockedMask)) { + list.addAll(blockedCoords.get(facing).getSublist(quads)); + } + } else { + list.addAll(coreCoords.get(facing).getSublist(quads)); + } + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/cache/ColorQuadCache.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/cache/ColorQuadCache.java new file mode 100644 index 0000000000..53e585ad2b --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/cache/ColorQuadCache.java @@ -0,0 +1,37 @@ +package com.gregtechceu.gtceu.client.renderer.pipe.cache; + +import com.gregtechceu.gtceu.client.renderer.pipe.util.ColorData; + +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap; + +import java.util.List; + +@OnlyIn(Dist.CLIENT) +public final class ColorQuadCache { + + private final List prototypes; + + private final Object2ObjectLinkedOpenHashMap> cache; + + public ColorQuadCache(List prototypes) { + this.prototypes = prototypes; + this.cache = new Object2ObjectLinkedOpenHashMap<>(); + } + + public List getQuads(ColorData data) { + // List existing = cache.get(data); + // if (existing == null) { + // existing = new ObjectArrayList<>(); + // for (BakedQuad quad : prototypes) { + // existing.add(quad); + // } + // cache.put(data, existing); + // // if (cache.size() > 20) cache.removeLast(); + // } + return prototypes; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/cache/ExtraCappedSQC.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/cache/ExtraCappedSQC.java new file mode 100644 index 0000000000..1e4558cea0 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/cache/ExtraCappedSQC.java @@ -0,0 +1,79 @@ +package com.gregtechceu.gtceu.client.renderer.pipe.cache; + +import com.gregtechceu.gtceu.client.renderer.pipe.quad.PipeQuadHelper; +import com.gregtechceu.gtceu.client.renderer.pipe.quad.QuadHelper; +import com.gregtechceu.gtceu.client.renderer.pipe.util.ColorData; +import com.gregtechceu.gtceu.client.renderer.pipe.util.SpriteInformation; +import com.gregtechceu.gtceu.utils.GTUtil; + +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.core.Direction; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import org.jetbrains.annotations.NotNull; + +import java.util.EnumMap; +import java.util.List; + +@OnlyIn(Dist.CLIENT) +public class ExtraCappedSQC extends StructureQuadCache { + + protected final EnumMap extraCapperCoords = new EnumMap<>(Direction.class); + + protected final SpriteInformation extraEndTex; + + protected ExtraCappedSQC(PipeQuadHelper helper, SpriteInformation endTex, SpriteInformation sideTex, + SpriteInformation extraEndTex) { + super(helper, endTex, sideTex); + this.extraEndTex = extraEndTex; + if (helper.getLayerCount() < 2) throw new IllegalStateException( + "Cannot create an ExtraCappedSQC without 2 or more layers present on the helper!"); + } + + public static @NotNull ExtraCappedSQC create(PipeQuadHelper helper, SpriteInformation endTex, + SpriteInformation sideTex, SpriteInformation extraEndTex) { + helper.initialize((facing, x1, y1, z1, x2, y2, z2) -> QuadHelper.capOverlay(facing, x1, y1, z1, x2, y2, z2, + OVERLAY_DIST_1)); + ExtraCappedSQC cache = new ExtraCappedSQC(helper, endTex, sideTex, extraEndTex); + cache.buildPrototype(); + return cache; + } + + @Override + protected List buildPrototypeInternal() { + List quads = super.buildPrototypeInternal(); + buildExtraCapper(quads); + return quads; + } + + protected void buildExtraCapper(List list) { + helper.setTargetSprite(extraEndTex); + for (Direction facing : GTUtil.DIRECTIONS) { + int start = list.size(); + list.add(helper.visitCapper(facing, 1)); + extraCapperCoords.put(facing, new SubListAddress(start, list.size())); + } + } + + @Override + public void addToList(List list, byte connectionMask, byte closedMask, byte blockedMask, ColorData data, + byte coverMask) { + List quads = cache.getQuads(data); + for (Direction facing : GTUtil.DIRECTIONS) { + if (GTUtil.evalMask(facing, connectionMask)) { + list.addAll(tubeCoords.get(facing).getSublist(quads)); + if (!GTUtil.evalMask(facing, coverMask)) { + if (GTUtil.evalMask(facing, closedMask)) { + list.addAll(capperClosedCoords.get(facing).getSublist(quads)); + } else { + list.addAll(capperCoords.get(facing).getSublist(quads)); + list.addAll(extraCapperCoords.get(facing).getSublist(quads)); + } + } + } else { + list.addAll(coreCoords.get(facing).getSublist(quads)); + } + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/cache/RestrictiveSQC.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/cache/RestrictiveSQC.java new file mode 100644 index 0000000000..f5f0d76269 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/cache/RestrictiveSQC.java @@ -0,0 +1,82 @@ +package com.gregtechceu.gtceu.client.renderer.pipe.cache; + +import com.gregtechceu.gtceu.client.renderer.pipe.quad.PipeQuadHelper; +import com.gregtechceu.gtceu.client.renderer.pipe.util.ColorData; +import com.gregtechceu.gtceu.client.renderer.pipe.util.SpriteInformation; +import com.gregtechceu.gtceu.utils.GTUtil; + +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.core.Direction; + +import org.jetbrains.annotations.NotNull; + +import java.util.EnumMap; +import java.util.List; + +public class RestrictiveSQC extends BlockableSQC { + + protected final EnumMap restrictiveCoords = new EnumMap<>(Direction.class); + + private final SpriteInformation restrictiveTex; + + protected RestrictiveSQC(PipeQuadHelper helper, SpriteInformation endTex, SpriteInformation sideTex, + SpriteInformation blockedTex, SpriteInformation restrictiveTex) { + super(helper, endTex, sideTex, blockedTex); + this.restrictiveTex = restrictiveTex; + if (helper.getLayerCount() < 3) throw new IllegalStateException( + "Cannot create a RestrictiveSQC without 3 or more layers present on the helper!"); + } + + public static @NotNull RestrictiveSQC create(PipeQuadHelper helper, SpriteInformation endTex, + SpriteInformation sideTex, + SpriteInformation blockedTex, SpriteInformation restrictiveTex) { + helper.initialize( + (facing, x1, y1, z1, x2, y2, z2) -> minLengthTube(facing, x1, y1, z1, x2, y2, z2, + OVERLAY_DIST_2, 4), + (facing, x1, y1, z1, x2, y2, z2) -> minLengthTube(facing, x1, y1, z1, x2, y2, z2, + OVERLAY_DIST_1, 2)); + RestrictiveSQC sqc = new RestrictiveSQC(helper, endTex, sideTex, blockedTex, restrictiveTex); + sqc.buildPrototype(); + return sqc; + } + + @Override + protected List buildPrototypeInternal() { + List quads = super.buildPrototypeInternal(); + buildRestrictive(quads); + return quads; + } + + protected void buildRestrictive(List list) { + helper.setTargetSprite(restrictiveTex); + for (Direction facing : GTUtil.DIRECTIONS) { + int start = list.size(); + list.addAll(helper.visitTube(facing, 2)); + restrictiveCoords.put(facing, new SubListAddress(start, list.size())); + } + } + + @Override + public void addToList(List list, byte connectionMask, byte closedMask, byte blockedMask, ColorData data, + byte coverMask) { + List quads = cache.getQuads(data); + for (Direction facing : GTUtil.DIRECTIONS) { + if (GTUtil.evalMask(facing, connectionMask)) { + list.addAll(tubeCoords.get(facing).getSublist(quads)); + list.addAll(restrictiveCoords.get(facing).getSublist(quads)); + if (!GTUtil.evalMask(facing, coverMask)) { + if (GTUtil.evalMask(facing, closedMask)) { + list.addAll(capperClosedCoords.get(facing).getSublist(quads)); + } else { + list.addAll(capperCoords.get(facing).getSublist(quads)); + } + } + if (GTUtil.evalMask(facing, blockedMask)) { + list.addAll(blockedCoords.get(facing).getSublist(quads)); + } + } else { + list.addAll(coreCoords.get(facing).getSublist(quads)); + } + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/cache/StructureQuadCache.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/cache/StructureQuadCache.java new file mode 100644 index 0000000000..0933a5c060 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/cache/StructureQuadCache.java @@ -0,0 +1,120 @@ +package com.gregtechceu.gtceu.client.renderer.pipe.cache; + +import com.gregtechceu.gtceu.client.renderer.pipe.quad.PipeQuadHelper; +import com.gregtechceu.gtceu.client.renderer.pipe.util.ColorData; +import com.gregtechceu.gtceu.client.renderer.pipe.util.SpriteInformation; +import com.gregtechceu.gtceu.utils.GTUtil; + +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.core.Direction; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import org.jetbrains.annotations.NotNull; + +import java.util.EnumMap; +import java.util.List; + +@OnlyIn(Dist.CLIENT) +public class StructureQuadCache { + + public static final float OVERLAY_DIST_1 = 0.003f; + public static final float OVERLAY_DIST_2 = 0.006f; + + protected final PipeQuadHelper helper; + + protected ColorQuadCache cache; + + protected final EnumMap tubeCoords = new EnumMap<>(Direction.class); + + protected final EnumMap coreCoords = new EnumMap<>(Direction.class); + protected final EnumMap capperCoords = new EnumMap<>(Direction.class); + protected final EnumMap capperClosedCoords = new EnumMap<>(Direction.class); + + protected final SpriteInformation endTex; + protected final SpriteInformation sideTex; + + protected StructureQuadCache(PipeQuadHelper helper, SpriteInformation endTex, SpriteInformation sideTex) { + this.helper = helper; + this.endTex = endTex; + this.sideTex = sideTex; + if (helper.getLayerCount() < 1) + throw new IllegalStateException("Cannot create an SQC without at least one layer present on the helper!"); + } + + public static @NotNull StructureQuadCache create(PipeQuadHelper helper, SpriteInformation endTex, + SpriteInformation sideTex) { + StructureQuadCache cache = new StructureQuadCache(helper.initialize(), endTex, sideTex); + cache.buildPrototype(); + return cache; + } + + protected void buildPrototype() { + this.cache = new ColorQuadCache(this.buildPrototypeInternal()); + } + + protected List buildPrototypeInternal() { + List quads = new ObjectArrayList<>(); + buildTube(quads); + buildCore(quads); + buildCapper(quads); + buildCapperClosed(quads); + return quads; + } + + protected void buildTube(List list) { + helper.setTargetSprite(sideTex); + for (Direction facing : GTUtil.DIRECTIONS) { + int start = list.size(); + list.addAll(helper.visitTube(facing)); + tubeCoords.put(facing, new SubListAddress(start, list.size())); + } + } + + protected void buildCore(List list) { + helper.setTargetSprite(sideTex); + for (Direction facing : GTUtil.DIRECTIONS) { + int start = list.size(); + list.add(helper.visitCore(facing)); + coreCoords.put(facing, new SubListAddress(start, start + 1)); + } + } + + protected void buildCapper(List list) { + helper.setTargetSprite(endTex); + for (Direction facing : GTUtil.DIRECTIONS) { + int start = list.size(); + list.add(helper.visitCapper(facing)); + capperCoords.put(facing, new SubListAddress(start, start + 1)); + } + } + + protected void buildCapperClosed(List list) { + helper.setTargetSprite(sideTex); + for (Direction facing : GTUtil.DIRECTIONS) { + int start = list.size(); + list.add(helper.visitCapper(facing)); + capperClosedCoords.put(facing, new SubListAddress(start, start + 1)); + } + } + + public void addToList(List list, byte connectionMask, byte closedMask, byte blockedMask, ColorData data, + byte coverMask) { + List quads = cache.getQuads(data); + for (Direction facing : GTUtil.DIRECTIONS) { + if (GTUtil.evalMask(facing, connectionMask)) { + list.addAll(tubeCoords.get(facing).getSublist(quads)); + if (!GTUtil.evalMask(facing, coverMask)) { + if (GTUtil.evalMask(facing, closedMask)) { + list.addAll(capperClosedCoords.get(facing).getSublist(quads)); + } else { + list.addAll(capperCoords.get(facing).getSublist(quads)); + } + } + } else { + list.addAll(coreCoords.get(facing).getSublist(quads)); + } + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/cache/SubListAddress.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/cache/SubListAddress.java new file mode 100644 index 0000000000..9241556796 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/cache/SubListAddress.java @@ -0,0 +1,12 @@ +package com.gregtechceu.gtceu.client.renderer.pipe.cache; + +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public record SubListAddress(int startInclusive, int endExclusive) { + + public @NotNull List getSublist(@NotNull List list) { + return list.subList(startInclusive, endExclusive); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/cover/CoverRenderer.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/cover/CoverRenderer.java new file mode 100644 index 0000000000..cea3cec916 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/cover/CoverRenderer.java @@ -0,0 +1,23 @@ +package com.gregtechceu.gtceu.client.renderer.pipe.cover; + +import com.gregtechceu.gtceu.client.renderer.pipe.util.ColorData; + +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.core.Direction; +import net.minecraft.util.RandomSource; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.client.model.data.ModelData; + +import java.util.EnumSet; +import java.util.List; + +@FunctionalInterface +@OnlyIn(Dist.CLIENT) +public interface CoverRenderer { + + void addQuads(List quads, Direction side, RandomSource rand, EnumSet renderPlate, + boolean renderBackside, + ModelData modelData, ColorData data, RenderType renderType); +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/cover/CoverRendererBuilder.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/cover/CoverRendererBuilder.java new file mode 100644 index 0000000000..3b9bdc492e --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/cover/CoverRendererBuilder.java @@ -0,0 +1,166 @@ +package com.gregtechceu.gtceu.client.renderer.pipe.cover; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.capability.ICoverable; +import com.gregtechceu.gtceu.client.renderer.pipe.cache.ColorQuadCache; +import com.gregtechceu.gtceu.client.renderer.pipe.cache.SubListAddress; +import com.gregtechceu.gtceu.client.renderer.pipe.quad.QuadHelper; +import com.gregtechceu.gtceu.client.renderer.pipe.quad.UVMapper; +import com.gregtechceu.gtceu.client.renderer.pipe.util.ColorData; +import com.gregtechceu.gtceu.client.renderer.pipe.util.SpriteInformation; +import com.gregtechceu.gtceu.utils.GTUtil; + +import com.lowdragmc.lowdraglib.client.model.ModelFactory; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.core.Direction; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.inventory.InventoryMenu; +import net.minecraft.world.phys.AABB; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.joml.Vector3f; + +import java.util.EnumMap; +import java.util.EnumSet; +import java.util.List; + +@OnlyIn(Dist.CLIENT) +public class CoverRendererBuilder { + + private static final float OVERLAY_DIST_1 = 0.003f; + private static final float OVERLAY_DIST_2 = 0.006f; + + private static final ColorQuadCache PLATE_QUADS; + private static final EnumMap PLATE_COORDS = new EnumMap<>(Direction.class); + + public static final EnumMap PLATE_AABBS = new EnumMap<>(Direction.class); + private static final EnumMap> PLATE_BOXES = new EnumMap<>(Direction.class); + private static final EnumMap> OVERLAY_BOXES_1 = new EnumMap<>( + Direction.class); + private static final EnumMap> OVERLAY_BOXES_2 = new EnumMap<>( + Direction.class); + + private static final UVMapper defaultMapper = UVMapper.standard(0); + + static { + for (Direction facing : GTUtil.DIRECTIONS) { + PLATE_AABBS.put(facing, ICoverable.getCoverPlateBox(facing, 1d / 16).bounds()); + } + for (var value : PLATE_AABBS.entrySet()) { + // make sure that plates render slightly below any normal block quad + PLATE_BOXES.put(value.getKey(), + QuadHelper.fullOverlay(value.getKey(), value.getValue(), -OVERLAY_DIST_1)); + OVERLAY_BOXES_1.put(value.getKey(), + QuadHelper.fullOverlay(value.getKey(), value.getValue(), OVERLAY_DIST_1)); + OVERLAY_BOXES_2.put(value.getKey(), + QuadHelper.fullOverlay(value.getKey(), value.getValue(), OVERLAY_DIST_2)); + } + PLATE_QUADS = buildPlates(new SpriteInformation(defaultPlateSprite(), 0)); + } + + private static @NotNull TextureAtlasSprite defaultPlateSprite() { + return Minecraft.getInstance().getTextureAtlas(InventoryMenu.BLOCK_ATLAS) + .apply(GTCEu.id("block/casings/voltage/lv/side")); + } + + public static ColorQuadCache buildPlates(SpriteInformation sprite) { + List quads = new ObjectArrayList<>(); + for (Direction facing : GTUtil.DIRECTIONS) { + PLATE_COORDS.put(facing, buildPlates(quads, facing, sprite)); + } + return new ColorQuadCache(quads); + } + + protected static SubListAddress buildPlates(List quads, Direction facing, + SpriteInformation sprite) { + int start = quads.size(); + Pair box = PLATE_BOXES.get(facing); + for (Direction dir : Direction.values()) { + quads.add(QuadHelper.buildQuad(dir, box, CoverRendererBuilder.defaultMapper, sprite)); + } + return new SubListAddress(start, quads.size()); + } + + protected static void addPlates(List quads, List plateQuads, EnumSet plates) { + for (Direction facing : plates) { + quads.add(plateQuads.get(facing.ordinal())); + } + } + + protected final ResourceLocation texture; + protected final ResourceLocation textureEmissive; + + protected TextureAtlasSprite sprite; + protected TextureAtlasSprite spriteEmissive; + + protected UVMapper mapper = defaultMapper; + protected UVMapper mapperEmissive = defaultMapper; + + protected ColorQuadCache plateQuads = PLATE_QUADS; + + public CoverRendererBuilder(@NotNull ResourceLocation texture, @Nullable ResourceLocation textureEmissive) { + this.texture = texture; + this.textureEmissive = textureEmissive; + } + + public CoverRendererBuilder setMapper(@NotNull UVMapper mapper) { + this.mapper = mapper; + return this; + } + + public CoverRendererBuilder setMapperEmissive(@NotNull UVMapper mapperEmissive) { + this.mapperEmissive = mapperEmissive; + return this; + } + + public CoverRendererBuilder setPlateQuads(ColorQuadCache cache) { + this.plateQuads = cache; + return this; + } + + protected static List getPlates(Direction facing, ColorData data, ColorQuadCache plateQuads) { + return PLATE_COORDS.get(facing).getSublist(plateQuads.getQuads(data)); + } + + public CoverRenderer build() { + if (sprite == null) { + sprite = ModelFactory.getBlockSprite(texture); + } + if (spriteEmissive == null && textureEmissive != null) { + spriteEmissive = ModelFactory.getBlockSprite(textureEmissive); + } + + EnumMap> spriteQuads = new EnumMap<>(Direction.class); + EnumMap> spriteEmissiveQuads = textureEmissive != null ? + new EnumMap<>(Direction.class) : null; + for (Direction facing : GTUtil.DIRECTIONS) { + spriteQuads.put(facing, ImmutablePair.of( + QuadHelper.buildQuad(facing, OVERLAY_BOXES_1.get(facing), mapper, sprite), + QuadHelper.buildQuad(facing.getOpposite(), OVERLAY_BOXES_1.get(facing), mapper, sprite))); + if (textureEmissive != null) spriteEmissiveQuads.put(facing, ImmutablePair.of( + QuadHelper.buildQuad(facing, OVERLAY_BOXES_2.get(facing), mapperEmissive, spriteEmissive), + QuadHelper.buildQuad(facing.getOpposite(), OVERLAY_BOXES_2.get(facing), mapperEmissive, + spriteEmissive))); + } + + return (quads, side, rand, renderPlate, renderBackside, modelData, data, renderType) -> { + addPlates(quads, getPlates(side, data, plateQuads), renderPlate); + quads.add(spriteQuads.get(side).getLeft()); + if (renderBackside) quads.add(spriteQuads.get(side).getRight()); + + if (spriteEmissiveQuads != null) { + quads.add(spriteEmissiveQuads.get(side).getLeft()); + if (renderBackside) quads.add(spriteEmissiveQuads.get(side).getRight()); + } + }; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/cover/CoverRendererPackage.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/cover/CoverRendererPackage.java new file mode 100644 index 0000000000..33ef71ded2 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/cover/CoverRendererPackage.java @@ -0,0 +1,60 @@ +package com.gregtechceu.gtceu.client.renderer.pipe.cover; + +import com.gregtechceu.gtceu.client.renderer.pipe.util.ColorData; + +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.core.Direction; +import net.minecraft.util.RandomSource; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.client.model.data.ModelData; +import net.minecraftforge.client.model.data.ModelProperty; + +import org.jetbrains.annotations.NotNull; + +import java.util.EnumMap; +import java.util.EnumSet; +import java.util.List; + +@OnlyIn(Dist.CLIENT) +public final class CoverRendererPackage { + + public static final ModelProperty PROPERTY = new ModelProperty<>(); + + public static final CoverRendererPackage EMPTY = new CoverRendererPackage(false); + + private final EnumMap renderers = new EnumMap<>(Direction.class); + private final EnumSet plates = EnumSet.allOf(Direction.class); + + private final boolean renderBackside; + + public CoverRendererPackage(boolean renderBackside) { + this.renderBackside = renderBackside; + } + + public void addRenderer(CoverRenderer renderer, @NotNull Direction facing) { + renderers.put(facing, renderer); + plates.remove(facing); + } + + public void addQuads(List quads, RandomSource rand, ModelData modelData, ColorData data, + RenderType renderType) { + for (var renderer : renderers.entrySet()) { + EnumSet plates = EnumSet.copyOf(this.plates); + // force front and back plates to render + plates.add(renderer.getKey()); + plates.add(renderer.getKey().getOpposite()); + renderer.getValue().addQuads(quads, renderer.getKey(), rand, plates, renderBackside, modelData, data, + renderType); + } + } + + public byte getMask() { + byte mask = 0; + for (Direction facing : renderers.keySet()) { + mask |= 1 << facing.ordinal(); + } + return mask; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/quad/OverlayLayerDefinition.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/quad/OverlayLayerDefinition.java new file mode 100644 index 0000000000..34581b01c2 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/quad/OverlayLayerDefinition.java @@ -0,0 +1,17 @@ +package com.gregtechceu.gtceu.client.renderer.pipe.quad; + +import net.minecraft.core.Direction; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.jetbrains.annotations.Nullable; +import org.joml.Vector3f; + +@FunctionalInterface +@OnlyIn(Dist.CLIENT) +public interface OverlayLayerDefinition { + + ImmutablePair computeBox(@Nullable Direction facing, float x1, float y1, float z1, float x2, + float y2, float z2); +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/quad/PipeQuadHelper.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/quad/PipeQuadHelper.java new file mode 100644 index 0000000000..f7c085f9f4 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/quad/PipeQuadHelper.java @@ -0,0 +1,172 @@ +package com.gregtechceu.gtceu.client.renderer.pipe.quad; + +import com.gregtechceu.gtceu.client.renderer.pipe.util.SpriteInformation; +import com.gregtechceu.gtceu.utils.GTUtil; + +import com.lowdragmc.lowdraglib.client.bakedpipeline.Quad; + +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.core.Direction; +import net.minecraft.util.RandomSource; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.client.model.data.ModelData; + +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.joml.Vector3f; + +import java.util.EnumMap; +import java.util.List; + +@OnlyIn(Dist.CLIENT) +public final class PipeQuadHelper { + + private SpriteInformation targetSprite; + + private final List> coreBoxList = new ObjectArrayList<>(); + private final List>> sideBoxesList = new ObjectArrayList<>(); + + private float[] definition; + + public PipeQuadHelper(float x, float y, float z, float small, float large) { + float xS = (x + small) * 16; + float xL = (x + large) * 16; + float yS = (y + small) * 16; + float yL = (y + large) * 16; + float zS = (z + small) * 16; + float zL = (z + large) * 16; + definition = new float[] { xS, xL, yS, yL, zS, zL }; + } + + @Contract("_ -> this") + public PipeQuadHelper initialize(OverlayLayerDefinition... overlayLayers) { + if (definition != null) { + float xS = definition[0]; + float xL = definition[1]; + float yS = definition[2]; + float yL = definition[3]; + float zS = definition[4]; + float zL = definition[5]; + definition = null; + generateBox(xS, xL, yS, yL, zS, zL, + (facing, x1, y1, z1, x2, y2, z2) -> QuadHelper.toPair(x1, y1, z1, x2, y2, z2)); + for (OverlayLayerDefinition definition : overlayLayers) { + generateBox(xS, xL, yS, yL, zS, zL, definition); + } + } + return this; + } + + public int getLayerCount() { + return coreBoxList.size(); + } + + private void generateBox(float xS, float xL, float yS, float yL, float zS, float zL, + @NotNull OverlayLayerDefinition definition) { + coreBoxList.add(definition.computeBox(null, xS, yS, zS, xL, yL, zL)); + EnumMap> sideBoxes = new EnumMap<>(Direction.class); + sideBoxes.put(Direction.DOWN, definition.computeBox(Direction.DOWN, xS, 0, zS, xL, yS, zL)); + sideBoxes.put(Direction.UP, definition.computeBox(Direction.UP, xS, yL, zS, xL, 16, zL)); + sideBoxes.put(Direction.NORTH, definition.computeBox(Direction.NORTH, xS, yS, 0, xL, yL, zS)); + sideBoxes.put(Direction.SOUTH, definition.computeBox(Direction.SOUTH, xS, yS, zL, xL, yL, 16)); + sideBoxes.put(Direction.WEST, definition.computeBox(Direction.WEST, 0, yS, zS, xS, yL, zL)); + sideBoxes.put(Direction.EAST, definition.computeBox(Direction.EAST, xL, yS, zS, 16, yL, zL)); + sideBoxesList.add(sideBoxes); + } + + @Contract("_, _, _, _ -> new") + public static @NotNull PipeQuadHelper create(float thickness, double x, double y, double z) { + float small = 0.5f - thickness / 2; + float large = 0.5f + thickness / 2; + return new PipeQuadHelper((float) x, (float) y, (float) z, small, large); + } + + @Contract("_ -> new") + public static @NotNull PipeQuadHelper create(float thickness) { + return create(thickness, 0, 0, 0); + } + + public void setTargetSprite(SpriteInformation sprite) { + this.targetSprite = sprite; + } + + public @NotNull BakedQuad visitCore(Direction facing) { + return visitCore(facing, 0); + } + + public @NotNull BakedQuad visitCore(Direction facing, int overlayLayer) { + return visitQuad(facing, coreBoxList.get(overlayLayer), UVMapper.standard(0)); + } + + public @NotNull List visitTube(Direction facing) { + return visitTube(facing, 0); + } + + public @NotNull List visitTube(Direction facing, int overlayLayer) { + List list = new ObjectArrayList<>(); + Pair box = sideBoxesList.get(overlayLayer).get(facing); + switch (facing.getAxis()) { + case X -> { + list.add(visitQuad(Direction.UP, box, UVMapper.standard(0))); + list.add(visitQuad(Direction.DOWN, box, UVMapper.standard(0))); + list.add(visitQuad(Direction.SOUTH, box, UVMapper.standard(0))); + list.add(visitQuad(Direction.NORTH, box, UVMapper.flipped(0))); + } + case Y -> { + list.add(visitQuad(Direction.EAST, box, UVMapper.standard(0))); + list.add(visitQuad(Direction.WEST, box, UVMapper.standard(0))); + list.add(visitQuad(Direction.SOUTH, box, UVMapper.standard(0))); + list.add(visitQuad(Direction.NORTH, box, UVMapper.standard(0))); + } + case Z -> { + list.add(visitQuad(Direction.UP, box, UVMapper.flipped(0))); + list.add(visitQuad(Direction.DOWN, box, UVMapper.standard(0))); + list.add(visitQuad(Direction.EAST, box, UVMapper.flipped(0))); + list.add(visitQuad(Direction.WEST, box, UVMapper.standard(0))); + } + } + return list; + } + + public @NotNull BakedQuad visitCapper(Direction facing) { + return visitCapper(facing, 0); + } + + public @NotNull BakedQuad visitCapper(Direction facing, int overlayLayer) { + return visitQuad(facing, sideBoxesList.get(overlayLayer).get(facing), UVMapper.standard(0)); + } + + public @NotNull BakedQuad visitQuad(Direction normal, Pair box, UVMapper uv) { + return QuadHelper.buildQuad(normal, box, uv, targetSprite); + } + + public static @NotNull List createFrame(BakedModel frameModel, RandomSource randomSource, + ModelData modelData, RenderType renderType) { + List list = new ObjectArrayList<>(); + // should always work + list.addAll(frameModel.getQuads(null, null, randomSource, modelData, renderType) + .stream() + .map(quad -> { + BakedQuad q = Quad.from(quad, -0.002f).rebake(); + return new BakedQuad(q.getVertices(), q.getTintIndex() + 3, + q.getDirection(), q.getSprite(), q.isShade(), q.hasAmbientOcclusion()); + }) + .toList()); + for (Direction facing : GTUtil.DIRECTIONS) { + list.addAll(frameModel.getQuads(null, facing, randomSource, modelData, renderType) + .stream() + .map(quad -> { + BakedQuad q = Quad.from(quad, -0.002f).rebake(); + return new BakedQuad(q.getVertices(), q.getTintIndex() + 3, + q.getDirection(), q.getSprite(), q.isShade(), q.hasAmbientOcclusion()); + }) + .toList()); + } + return list; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/quad/QuadHelper.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/quad/QuadHelper.java new file mode 100644 index 0000000000..7c5cf6644e --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/quad/QuadHelper.java @@ -0,0 +1,111 @@ +package com.gregtechceu.gtceu.client.renderer.pipe.quad; + +import com.gregtechceu.gtceu.client.renderer.pipe.util.SpriteInformation; +import com.gregtechceu.gtceu.client.util.StaticFaceBakery; + +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.client.renderer.block.model.BlockElementFace; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.resources.model.BlockModelRotation; +import net.minecraft.core.Direction; +import net.minecraft.world.phys.AABB; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.client.model.ForgeFaceData; + +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.joml.Vector3f; + +@OnlyIn(Dist.CLIENT) +public final class QuadHelper { + + private QuadHelper() {} + + public static @NotNull BakedQuad buildQuad(Direction normal, Pair box, + @NotNull UVMapper uv, @NotNull SpriteInformation targetSprite) { + BlockElementFace face = new BlockElementFace(null, -1, targetSprite.sprite().contents().name().toString(), + uv.map(normal, box), ForgeFaceData.DEFAULT); + return StaticFaceBakery.bakeRecolorableQuad(box.getLeft(), box.getRight(), face, targetSprite, normal, + BlockModelRotation.X0_Y0, null, true, 0); + } + + public static @NotNull BakedQuad buildQuad(Direction normal, Pair box, + @NotNull UVMapper uv, @NotNull TextureAtlasSprite targetSprite) { + BlockElementFace face = new BlockElementFace(null, -1, targetSprite.contents().name().toString(), + uv.map(normal, box)); + return StaticFaceBakery.bakeQuad(box.getLeft(), box.getRight(), face, targetSprite, normal, + BlockModelRotation.X0_Y0, + null, true, 0); + } + + @Contract("_ -> new") + public static @NotNull ImmutablePair toPair(@NotNull AABB bb) { + return ImmutablePair.of(new Vector3f((float) bb.minX * 16, (float) bb.minY * 16, (float) bb.minZ * 16), + new Vector3f((float) bb.maxX * 16, (float) bb.maxY * 16, (float) bb.maxZ * 16)); + } + + @Contract("_, _, _, _, _, _ -> new") + public static @NotNull ImmutablePair toPair(float x1, float y1, float z1, float x2, float y2, + float z2) { + return ImmutablePair.of(new Vector3f(x1, y1, z1), new Vector3f(x2, y2, z2)); + } + + @Contract("_, _, _ -> new") + public static @NotNull ImmutablePair capOverlay(@Nullable Direction facing, + @NotNull AABB bb, float g) { + return capOverlay(facing, (float) bb.minX * 16, (float) bb.minY * 16, (float) bb.minZ * 16, + (float) bb.maxX * 16, (float) bb.maxY * 16, + (float) bb.maxZ * 16, g); + } + + @Contract("_, _, _, _, _, _, _, _ -> new") + public static @NotNull ImmutablePair capOverlay(@Nullable Direction facing, float x1, float y1, + float z1, float x2, float y2, float z2, + float g) { + if (facing == null) return toPair(x1 - g, y1 - g, z1 - g, x2 + g, y2 + g, z2 + g); + return switch (facing.getAxis()) { + case X -> toPair(x1 - g, y1, z1, x2 + g, y2, z2); + case Y -> toPair(x1, y1 - g, z1, x2, y2 + g, z2); + case Z -> toPair(x1, y1, z1 - g, x2, y2, z2 + g); + }; + } + + @Contract("_, _, _ -> new") + public static @NotNull ImmutablePair tubeOverlay(@Nullable Direction facing, + @NotNull AABB bb, float g) { + return tubeOverlay(facing, (float) bb.minX * 16, (float) bb.minY * 16, (float) bb.minZ * 16, + (float) bb.maxX * 16, (float) bb.maxY * 16, + (float) bb.maxZ * 16, g); + } + + @Contract("_, _, _, _, _, _, _, _ -> new") + public static @NotNull ImmutablePair tubeOverlay(@Nullable Direction facing, float x1, + float y1, float z1, float x2, float y2, + float z2, float g) { + if (facing == null) return toPair(x1, y1, z1, x2, y2, z2); + return switch (facing.getAxis()) { + case X -> toPair(x1, y1 - g, z1 - g, x2, y2 + g, z2 + g); + case Y -> toPair(x1 - g, y1, z1 - g, x2 + g, y2, z2 + g); + case Z -> toPair(x1 - g, y1 - g, z1, x2 + g, y2 + g, z2); + }; + } + + @Contract("_, _, _ -> new") + public static @NotNull ImmutablePair fullOverlay(@Nullable Direction facing, + @NotNull AABB bb, float g) { + return fullOverlay(facing, (float) bb.minX * 16, (float) bb.minY * 16, (float) bb.minZ * 16, + (float) bb.maxX * 16, (float) bb.maxY * 16, + (float) bb.maxZ * 16, g); + } + + @Contract("_, _, _, _, _, _, _, _ -> new") + public static @NotNull ImmutablePair fullOverlay(@Nullable Direction facing, float x1, + float y1, float z1, float x2, float y2, + float z2, float g) { + return toPair(x1 - g, y1 - g, z1 - g, x2 + g, y2 + g, z2 + g); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/quad/UVMapper.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/quad/UVMapper.java new file mode 100644 index 0000000000..16e026e537 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/quad/UVMapper.java @@ -0,0 +1,44 @@ +package com.gregtechceu.gtceu.client.renderer.pipe.quad; + +import net.minecraft.client.renderer.block.model.BlockFaceUV; +import net.minecraft.core.Direction; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; +import org.apache.commons.lang3.tuple.Pair; +import org.joml.Vector3f; + +@FunctionalInterface +@OnlyIn(Dist.CLIENT) +public interface UVMapper { + + Int2ObjectArrayMap STANDARD = new Int2ObjectArrayMap<>(4); + Int2ObjectArrayMap FLIPPED = new Int2ObjectArrayMap<>(4); + + static UVMapper standard(int rot) { + return FLIPPED.computeIfAbsent(rot, (r) -> (normal, box) -> { + Vector3f small = box.getLeft(); + Vector3f large = box.getRight(); + return switch (normal.getAxis()) { + case X -> new BlockFaceUV(new float[] { small.z, 16 - large.y, large.z, 16 - small.y }, r); + case Y -> new BlockFaceUV(new float[] { small.x, 16 - large.z, large.x, 16 - small.z }, r); + case Z -> new BlockFaceUV(new float[] { small.x, 16 - large.y, large.x, 16 - small.y }, r); + }; + }); + } + + static UVMapper flipped(int rot) { + return STANDARD.computeIfAbsent(rot, (r) -> (normal, box) -> { + Vector3f small = box.getLeft(); + Vector3f large = box.getRight(); + return switch (normal.getAxis()) { + case X -> new BlockFaceUV(new float[] { 16 - large.z, small.y, 16 - small.z, large.y }, r); + case Y -> new BlockFaceUV(new float[] { 16 - large.x, small.z, 16 - small.x, large.z }, r); + case Z -> new BlockFaceUV(new float[] { 16 - large.x, small.y, 16 - small.x, large.y }, r); + }; + }); + } + + BlockFaceUV map(Direction normal, Pair box); +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/util/ActivableCacheKey.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/util/ActivableCacheKey.java new file mode 100644 index 0000000000..6360f67c49 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/util/ActivableCacheKey.java @@ -0,0 +1,27 @@ +package com.gregtechceu.gtceu.client.renderer.pipe.util; + +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import lombok.Getter; +import org.jetbrains.annotations.Nullable; + +@OnlyIn(Dist.CLIENT) +public class ActivableCacheKey extends CacheKey { + + @Getter + private final boolean active; + + public ActivableCacheKey(float thickness, boolean active) { + super(thickness); + this.active = active; + } + + public static ActivableCacheKey of(@Nullable Float thickness, @Nullable Boolean active) { + float thick = thickness == null ? 0.5f : thickness; + boolean act = active != null && active; + return new ActivableCacheKey(thick, act); + } + + // activeness is merely a way to pass information onwards, it does not result in separate mappings. +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/util/CacheKey.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/util/CacheKey.java new file mode 100644 index 0000000000..e81e9771f6 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/util/CacheKey.java @@ -0,0 +1,54 @@ +package com.gregtechceu.gtceu.client.renderer.pipe.util; + +import net.minecraft.util.StringRepresentable; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; + +@OnlyIn(Dist.CLIENT) +public class CacheKey implements StringRepresentable { + + protected final float thickness; + + private final int hash; + + public CacheKey(float thickness) { + this.thickness = thickness; + this.hash = computeHash(); + } + + public static CacheKey of(@Nullable Float thickness) { + float thick = thickness == null ? 0.5f : thickness; + return new CacheKey(thick); + } + + public float getThickness() { + return thickness; + } + + protected int computeHash() { + return Objects.hash(thickness); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null || obj.getClass() != this.getClass()) return false; + var that = (CacheKey) obj; + return Float.floatToIntBits(this.thickness) == Float.floatToIntBits(that.thickness); + } + + @Override + public final int hashCode() { + return hash; + } + + @Override + public @NotNull String getSerializedName() { + return String.valueOf(Float.floatToIntBits(thickness)); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/util/ColorData.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/util/ColorData.java new file mode 100644 index 0000000000..894c2a277b --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/util/ColorData.java @@ -0,0 +1,18 @@ +package com.gregtechceu.gtceu.client.renderer.pipe.util; + +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import java.util.Arrays; + +@OnlyIn(Dist.CLIENT) +public record ColorData(int... colorsARGB) { + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null || obj.getClass() != this.getClass()) return false; + var that = (ColorData) obj; + return Arrays.equals(this.colorsARGB, that.colorsARGB); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/util/MaterialModelOverride.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/util/MaterialModelOverride.java new file mode 100644 index 0000000000..767d44cbfc --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/util/MaterialModelOverride.java @@ -0,0 +1,45 @@ +package com.gregtechceu.gtceu.client.renderer.pipe.util; + +import com.gregtechceu.gtceu.api.data.chemical.material.Material; +import com.gregtechceu.gtceu.client.renderer.pipe.AbstractPipeModel; + +import com.google.common.collect.Table; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.function.BiFunction; +import java.util.function.Predicate; + +public interface MaterialModelOverride> { + + @Nullable + T getModel(Material material, int i); + + record StandardOverride>(@NotNull T[] models, + @NotNull Predicate<@Nullable Material> predicate) + implements MaterialModelOverride { + + @Override + public @Nullable T getModel(Material material, int i) { + if (material == null || !predicate.test(material)) return null; + else return models[i]; + } + } + + record PerMaterialOverride>(@NotNull Table models, + @NotNull BiFunction createFunction, + @NotNull Predicate<@Nullable Material> predicate) + implements MaterialModelOverride { + + @Override + public @Nullable T getModel(Material material, int i) { + if (material == null || !predicate.test(material)) return null; + if (!models.contains(material, i)) { + T model = createFunction.apply(material, i); + models.put(material, i, model); + return model; + } + return models.get(material, i); + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/util/MaterialModelSupplier.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/util/MaterialModelSupplier.java new file mode 100644 index 0000000000..70407ff13a --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/util/MaterialModelSupplier.java @@ -0,0 +1,14 @@ +package com.gregtechceu.gtceu.client.renderer.pipe.util; + +import com.gregtechceu.gtceu.api.data.chemical.material.Material; +import com.gregtechceu.gtceu.client.renderer.pipe.AbstractPipeModel; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +@FunctionalInterface +public interface MaterialModelSupplier { + + @NotNull + AbstractPipeModel getModel(@Nullable Material material); +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/util/SpriteInformation.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/util/SpriteInformation.java new file mode 100644 index 0000000000..818527f0b8 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/util/SpriteInformation.java @@ -0,0 +1,13 @@ +package com.gregtechceu.gtceu.client.renderer.pipe.util; + +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +@OnlyIn(Dist.CLIENT) +public record SpriteInformation(TextureAtlasSprite sprite, int colorID) { + + public boolean colorable() { + return colorID >= 0; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/util/TextureInformation.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/util/TextureInformation.java new file mode 100644 index 0000000000..74fdc58750 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/pipe/util/TextureInformation.java @@ -0,0 +1,13 @@ +package com.gregtechceu.gtceu.client.renderer.pipe.util; + +import net.minecraft.resources.ResourceLocation; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +@OnlyIn(Dist.CLIENT) +public record TextureInformation(ResourceLocation texture, int colorID) { + + public boolean colorable() { + return colorID >= 0; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/util/DrawUtil.java b/src/main/java/com/gregtechceu/gtceu/client/util/DrawUtil.java index 55cf4941c7..c90bf1ac5a 100644 --- a/src/main/java/com/gregtechceu/gtceu/client/util/DrawUtil.java +++ b/src/main/java/com/gregtechceu/gtceu/client/util/DrawUtil.java @@ -64,16 +64,34 @@ private static void fillHorizontalGradient(GuiGraphics graphics, VertexConsumer consumer.vertex(matrix4f, (float) x2, (float) y1, (float) z).color(r2, g2, b2, a2).endVertex(); } + public static int interpolateColor(int color1, int color2, float blend) { + int a1 = color1 >> 24 & 255; + int r1 = color1 >> 16 & 255; + int g1 = color1 >> 8 & 255; + int b1 = color1 >> 0 & 255; + + int a2 = color2 >> 24 & 255; + int r2 = color2 >> 16 & 255; + int g2 = color2 >> 8 & 255; + int b2 = color2 >> 0 & 255; + + int a = (int) (a1 * (1 - blend) + a2 * blend); + int r = (int) (r1 * (1 - blend) + r2 * blend); + int g = (int) (g1 * (1 - blend) + g2 * blend); + int b = (int) (b1 * (1 - blend) + b2 * blend); + return a << 24 | r << 16 | g << 8 | b; + } + /** * Converts an (A)RGB integer color into an array of floats, for use in GL calls - * + * * @return float[]{R, G, B, A} */ public static float[] floats(int argb) { return new float[] { (float) (argb >> 16 & 255) / 255.0F, (float) (argb >> 8 & 255) / 255.0F, - (float) (argb & 255) / 255.0F, + (float) (argb >> 0 & 255) / 255.0F, (float) (argb >> 24 & 255) / 255.0F }; } diff --git a/src/main/java/com/gregtechceu/gtceu/client/util/EffectRenderContext.java b/src/main/java/com/gregtechceu/gtceu/client/util/EffectRenderContext.java new file mode 100644 index 0000000000..653858b3fb --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/util/EffectRenderContext.java @@ -0,0 +1,84 @@ +package com.gregtechceu.gtceu.client.util; + +import net.minecraft.client.Camera; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.culling.Frustum; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.phys.Vec3; + +import lombok.Getter; +import lombok.experimental.Accessors; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; + +/** + * Collection of various information for rendering purposes. + */ +@Accessors(fluent = true) +public final class EffectRenderContext { + + private static final EffectRenderContext instance = new EffectRenderContext(); + + public static EffectRenderContext getInstance() { + return instance; + } + + @Getter + private Frustum frustum = new Frustum(Minecraft.getInstance().levelRenderer.getFrustum()); + + @Nullable + private Entity renderViewEntity; + @Getter + private float partialTicks; + @Getter + private double cameraX; + @Getter + private double cameraY; + @Getter + private double cameraZ; + @NotNull + @Getter + private Vec3 cameraViewDir = Vec3.ZERO; + @Getter + private float rotationY; + @Getter + private float rotationX; + @Getter + private float rotationYZ; + @Getter + private float rotationXY; + @Getter + private float rotationXZ; + + @NotNull + public EffectRenderContext update(@NotNull Entity renderViewEntity, Camera camera, Frustum frustum, + float partialTicks) { + this.renderViewEntity = renderViewEntity; + this.partialTicks = partialTicks; + + this.cameraX = camera.getPosition().x; + this.cameraY = camera.getPosition().y; + this.cameraZ = camera.getPosition().z; + this.cameraViewDir = renderViewEntity.getViewVector(partialTicks); + + this.rotationY = camera.getYRot(); + this.rotationX = camera.getXRot(); + // this.rotationYZ = ActiveRenderInfo.getRotationYZ(); + // this.rotationXY = ActiveRenderInfo.getRotationXY(); + // this.rotationXZ = ActiveRenderInfo.getRotationXZ(); + + this.frustum = frustum; + + return this; + } + + /** + * @return render view entity + */ + @NotNull + public Entity renderViewEntity() { + return Objects.requireNonNull(renderViewEntity, "renderViewEntity not available yet"); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/util/RenderBufferHelper.java b/src/main/java/com/gregtechceu/gtceu/client/util/RenderBufferHelper.java index 5acfe490fb..ac5189d994 100644 --- a/src/main/java/com/gregtechceu/gtceu/client/util/RenderBufferHelper.java +++ b/src/main/java/com/gregtechceu/gtceu/client/util/RenderBufferHelper.java @@ -2,9 +2,11 @@ import net.minecraft.core.Direction; import net.minecraft.util.Mth; +import net.minecraft.world.phys.AABB; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; +import com.mojang.blaze3d.vertex.BufferBuilder; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import org.joml.Matrix4f; @@ -88,4 +90,118 @@ public static void renderRing(PoseStack poseStack, VertexConsumer buffer, float } } + + public static void renderCubeFrame(BufferBuilder buffer, double minX, double minY, double minZ, double maxX, + double maxY, double maxZ, float r, float g, float b, float a) { + buffer.vertex(minX, minY, minZ).color(r, g, b, a).endVertex(); + buffer.vertex(maxX, minY, minZ).color(r, g, b, a).endVertex(); + + buffer.vertex(minX, minY, minZ).color(r, g, b, a).endVertex(); + buffer.vertex(minX, maxY, minZ).color(r, g, b, a).endVertex(); + + buffer.vertex(minX, minY, minZ).color(r, g, b, a).endVertex(); + buffer.vertex(minX, minY, maxZ).color(r, g, b, a).endVertex(); + + buffer.vertex(maxX, maxY, maxZ).color(r, g, b, a).endVertex(); + buffer.vertex(minX, maxY, maxZ).color(r, g, b, a).endVertex(); + + buffer.vertex(maxX, maxY, maxZ).color(r, g, b, a).endVertex(); + buffer.vertex(maxX, minY, maxZ).color(r, g, b, a).endVertex(); + + buffer.vertex(maxX, maxY, maxZ).color(r, g, b, a).endVertex(); + buffer.vertex(maxX, maxY, minZ).color(r, g, b, a).endVertex(); + + buffer.vertex(minX, maxY, minZ).color(r, g, b, a).endVertex(); + buffer.vertex(minX, maxY, maxZ).color(r, g, b, a).endVertex(); + + buffer.vertex(minX, maxY, minZ).color(r, g, b, a).endVertex(); + buffer.vertex(maxX, maxY, minZ).color(r, g, b, a).endVertex(); + + buffer.vertex(maxX, minY, minZ).color(r, g, b, a).endVertex(); + buffer.vertex(maxX, minY, maxZ).color(r, g, b, a).endVertex(); + + buffer.vertex(maxX, minY, minZ).color(r, g, b, a).endVertex(); + buffer.vertex(maxX, maxY, minZ).color(r, g, b, a).endVertex(); + + buffer.vertex(minX, minY, maxZ).color(r, g, b, a).endVertex(); + buffer.vertex(maxX, minY, maxZ).color(r, g, b, a).endVertex(); + + buffer.vertex(minX, minY, maxZ).color(r, g, b, a).endVertex(); + buffer.vertex(minX, maxY, maxZ).color(r, g, b, a).endVertex(); + } + + public static void renderCubeFace(PoseStack poseStack, VertexConsumer buffer, AABB cuboid, float r, float g, + float b, float a, + boolean shade) { + renderCubeFace(poseStack, buffer, + (float) cuboid.minX, (float) cuboid.minY, (float) cuboid.minZ, + (float) cuboid.maxX, (float) cuboid.maxY, (float) cuboid.maxZ, + r, g, b, a, shade); + } + + public static void renderCubeFace(PoseStack poseStack, VertexConsumer buffer, + float minX, float minY, float minZ, + float maxX, float maxY, float maxZ, + float red, float green, float blue, float alpha) { + renderCubeFace(poseStack, buffer, minX, minY, minZ, maxX, maxY, maxZ, red, green, blue, alpha, false); + } + + public static void renderCubeFace(PoseStack poseStack, VertexConsumer buffer, + float minX, float minY, float minZ, + float maxX, float maxY, float maxZ, + float red, float green, float blue, float a, + boolean shade) { + Matrix4f pose = poseStack.last().pose(); + float r = red, g = green, b = blue; + + if (shade) { + r *= 0.6f; + g *= 0.6f; + b *= 0.6f; + } + buffer.vertex(pose, minX, minY, minZ).color(r, g, b, a).endVertex(); + buffer.vertex(pose, minX, minY, maxZ).color(r, g, b, a).endVertex(); + buffer.vertex(pose, minX, maxY, maxZ).color(r, g, b, a).endVertex(); + buffer.vertex(pose, minX, maxY, minZ).color(r, g, b, a).endVertex(); + + buffer.vertex(pose, maxX, minY, minZ).color(r, g, b, a).endVertex(); + buffer.vertex(pose, maxX, maxY, minZ).color(r, g, b, a).endVertex(); + buffer.vertex(pose, maxX, maxY, maxZ).color(r, g, b, a).endVertex(); + buffer.vertex(pose, maxX, minY, maxZ).color(r, g, b, a).endVertex(); + + if (shade) { + r = red * 0.5f; + g = green * 0.5f; + b = blue * 0.5f; + } + buffer.vertex(pose, minX, minY, minZ).color(r, g, b, a).endVertex(); + buffer.vertex(pose, maxX, minY, minZ).color(r, g, b, a).endVertex(); + buffer.vertex(pose, maxX, minY, maxZ).color(r, g, b, a).endVertex(); + buffer.vertex(pose, minX, minY, maxZ).color(r, g, b, a).endVertex(); + + if (shade) { + r = red; + g = green; + b = blue; + } + buffer.vertex(pose, minX, maxY, minZ).color(r, g, b, a).endVertex(); + buffer.vertex(pose, minX, maxY, maxZ).color(r, g, b, a).endVertex(); + buffer.vertex(pose, maxX, maxY, maxZ).color(r, g, b, a).endVertex(); + buffer.vertex(pose, maxX, maxY, minZ).color(r, g, b, a).endVertex(); + + if (shade) { + r = red * 0.8f; + g = green * 0.8f; + b = blue * 0.8f; + } + buffer.vertex(pose, minX, minY, minZ).color(r, g, b, a).endVertex(); + buffer.vertex(pose, minX, maxY, minZ).color(r, g, b, a).endVertex(); + buffer.vertex(pose, maxX, maxY, minZ).color(r, g, b, a).endVertex(); + buffer.vertex(pose, maxX, minY, minZ).color(r, g, b, a).endVertex(); + + buffer.vertex(pose, minX, minY, maxZ).color(r, g, b, a).endVertex(); + buffer.vertex(pose, maxX, minY, maxZ).color(r, g, b, a).endVertex(); + buffer.vertex(pose, maxX, maxY, maxZ).color(r, g, b, a).endVertex(); + buffer.vertex(pose, minX, maxY, maxZ).color(r, g, b, a).endVertex(); + } } diff --git a/src/main/java/com/gregtechceu/gtceu/client/util/StaticFaceBakery.java b/src/main/java/com/gregtechceu/gtceu/client/util/StaticFaceBakery.java index 3f55ef5a2a..5e584cb484 100644 --- a/src/main/java/com/gregtechceu/gtceu/client/util/StaticFaceBakery.java +++ b/src/main/java/com/gregtechceu/gtceu/client/util/StaticFaceBakery.java @@ -1,5 +1,7 @@ package com.gregtechceu.gtceu.client.util; +import com.gregtechceu.gtceu.client.renderer.pipe.util.SpriteInformation; + import com.lowdragmc.lowdraglib.client.bakedpipeline.FaceQuad; import net.minecraft.client.renderer.FaceInfo; @@ -19,6 +21,7 @@ import net.minecraftforge.client.model.QuadTransformers; import com.mojang.math.Transformation; +import org.jetbrains.annotations.Nullable; import org.joml.*; import java.lang.Math; @@ -93,7 +96,7 @@ public static BakedQuad bakeQuad( TextureAtlasSprite sprite, Direction facing, ModelState transform, - @javax.annotation.Nullable BlockElementRotation partRotation, + @Nullable BlockElementRotation partRotation, boolean shade, int emissivity) { BlockFaceUV blockfaceuv = face.uv; @@ -131,6 +134,52 @@ public static BakedQuad bakeQuad( return quad; } + public static BakedQuad bakeRecolorableQuad(Vector3f posFrom, + Vector3f posTo, + BlockElementFace face, + SpriteInformation sprite, + Direction facing, + ModelState transform, + @Nullable BlockElementRotation partRotation, + boolean shade, + int emissivity) { + BlockFaceUV blockfaceuv = face.uv; + if (transform.isUvLocked()) { + blockfaceuv = recomputeUVs(face.uv, facing, transform.getRotation()); + } + + float[] afloat = new float[blockfaceuv.uvs.length]; + System.arraycopy(blockfaceuv.uvs, 0, afloat, 0, afloat.length); + float f = sprite.sprite().uvShrinkRatio(); + float f1 = (blockfaceuv.uvs[0] + blockfaceuv.uvs[0] + blockfaceuv.uvs[2] + blockfaceuv.uvs[2]) / VERTEX_COUNT; + float f2 = (blockfaceuv.uvs[1] + blockfaceuv.uvs[1] + blockfaceuv.uvs[3] + blockfaceuv.uvs[3]) / VERTEX_COUNT; + blockfaceuv.uvs[0] = Mth.lerp(f, blockfaceuv.uvs[0], f1); + blockfaceuv.uvs[2] = Mth.lerp(f, blockfaceuv.uvs[2], f1); + blockfaceuv.uvs[1] = Mth.lerp(f, blockfaceuv.uvs[1], f2); + blockfaceuv.uvs[3] = Mth.lerp(f, blockfaceuv.uvs[3], f2); + int[] aint = makeVertices(blockfaceuv, sprite.sprite(), facing, setupShape(posFrom, posTo), + transform.getRotation(), + partRotation, shade); + Direction direction = calculateFacing(aint); + System.arraycopy(afloat, 0, blockfaceuv.uvs, 0, afloat.length); + if (partRotation == null) { + recalculateWinding(aint, direction); + } + + ForgeHooksClient.fillNormal(aint, direction); + ForgeFaceData data = face.getFaceData(); + BakedQuad quad = new BakedQuad(aint, sprite.colorID(), direction, sprite.sprite(), shade, + data.ambientOcclusion()); + if (!ForgeFaceData.DEFAULT.equals(data)) { + QuadTransformers.applyingLightmap(data.blockLight(), data.skyLight()).processInPlace(quad); + QuadTransformers.applyingColor(data.color()).processInPlace(quad); + } + com.lowdragmc.lowdraglib.client.bakedpipeline.QuadTransformers.settingEmissivity(emissivity) + .processInPlace(quad); + + return quad; + } + public static BlockFaceUV recomputeUVs(BlockFaceUV uv, Direction facing, Transformation modelRotation) { Matrix4f matrix4f = BlockMath .getUVLockTransform(modelRotation, facing, () -> "Unable to resolve UVLock for model").getMatrix(); @@ -180,7 +229,7 @@ private static int[] makeVertices( Direction orientation, float[] posDiv16, Transformation rotation, - @javax.annotation.Nullable BlockElementRotation partRotation, + @Nullable BlockElementRotation partRotation, boolean shade) { int[] aint = new int[32]; @@ -199,7 +248,7 @@ private static void bakeVertex( float[] posDiv16, TextureAtlasSprite sprite, Transformation rotation, - @javax.annotation.Nullable BlockElementRotation partRotation, + @Nullable BlockElementRotation partRotation, boolean shade) { FaceInfo.VertexInfo faceinfo$vertexinfo = FaceInfo.fromFacing(facing).getVertexInfo(vertexIndex); Vector3f vector3f = new Vector3f(posDiv16[faceinfo$vertexinfo.xFace], posDiv16[faceinfo$vertexinfo.yFace], @@ -236,7 +285,7 @@ private static float[] setupShape(Vector3f min, Vector3f max) { } private static void applyElementRotation(Vector3f vec, - @javax.annotation.Nullable BlockElementRotation partRotation) { + @Nullable BlockElementRotation partRotation) { if (partRotation != null) { Vector3f vector3f; Vector3f vector3f1; @@ -288,43 +337,43 @@ private static void rotateVertexBy(Vector3f pos, Vector3f origin, Matrix4f trans } private static void recalculateWinding(int[] vertices, Direction direction) { - int[] aint = new int[vertices.length]; - System.arraycopy(vertices, 0, aint, 0, vertices.length); - float[] afloat = new float[Direction.values().length]; - afloat[FaceInfo.Constants.MIN_X] = 999.0F; - afloat[FaceInfo.Constants.MIN_Y] = 999.0F; - afloat[FaceInfo.Constants.MIN_Z] = 999.0F; - afloat[FaceInfo.Constants.MAX_X] = -999.0F; - afloat[FaceInfo.Constants.MAX_Y] = -999.0F; - afloat[FaceInfo.Constants.MAX_Z] = -999.0F; + int[] newVertices = new int[vertices.length]; + System.arraycopy(vertices, 0, newVertices, 0, vertices.length); + float[] normals = new float[Direction.values().length]; + normals[FaceInfo.Constants.MIN_X] = 999.0F; + normals[FaceInfo.Constants.MIN_Y] = 999.0F; + normals[FaceInfo.Constants.MIN_Z] = 999.0F; + normals[FaceInfo.Constants.MAX_X] = -999.0F; + normals[FaceInfo.Constants.MAX_Y] = -999.0F; + normals[FaceInfo.Constants.MAX_Z] = -999.0F; for (int i = 0; i < 4; ++i) { int j = 8 * i; - float f = Float.intBitsToFloat(aint[j]); - float f1 = Float.intBitsToFloat(aint[j + 1]); - float f2 = Float.intBitsToFloat(aint[j + 2]); - if (f < afloat[FaceInfo.Constants.MIN_X]) { - afloat[FaceInfo.Constants.MIN_X] = f; + float f = Float.intBitsToFloat(newVertices[j]); + float f1 = Float.intBitsToFloat(newVertices[j + 1]); + float f2 = Float.intBitsToFloat(newVertices[j + 2]); + if (f < normals[FaceInfo.Constants.MIN_X]) { + normals[FaceInfo.Constants.MIN_X] = f; } - if (f1 < afloat[FaceInfo.Constants.MIN_Y]) { - afloat[FaceInfo.Constants.MIN_Y] = f1; + if (f1 < normals[FaceInfo.Constants.MIN_Y]) { + normals[FaceInfo.Constants.MIN_Y] = f1; } - if (f2 < afloat[FaceInfo.Constants.MIN_Z]) { - afloat[FaceInfo.Constants.MIN_Z] = f2; + if (f2 < normals[FaceInfo.Constants.MIN_Z]) { + normals[FaceInfo.Constants.MIN_Z] = f2; } - if (f > afloat[FaceInfo.Constants.MAX_X]) { - afloat[FaceInfo.Constants.MAX_X] = f; + if (f > normals[FaceInfo.Constants.MAX_X]) { + normals[FaceInfo.Constants.MAX_X] = f; } - if (f1 > afloat[FaceInfo.Constants.MAX_Y]) { - afloat[FaceInfo.Constants.MAX_Y] = f1; + if (f1 > normals[FaceInfo.Constants.MAX_Y]) { + normals[FaceInfo.Constants.MAX_Y] = f1; } - if (f2 > afloat[FaceInfo.Constants.MAX_Z]) { - afloat[FaceInfo.Constants.MAX_Z] = f2; + if (f2 > normals[FaceInfo.Constants.MAX_Z]) { + normals[FaceInfo.Constants.MAX_Z] = f2; } } @@ -332,22 +381,23 @@ private static void recalculateWinding(int[] vertices, Direction direction) { for (int i1 = 0; i1 < 4; ++i1) { int j1 = 8 * i1; - FaceInfo.VertexInfo faceinfo$vertexinfo = faceinfo.getVertexInfo(i1); - float f8 = afloat[faceinfo$vertexinfo.xFace]; - float f3 = afloat[faceinfo$vertexinfo.yFace]; - float f4 = afloat[faceinfo$vertexinfo.zFace]; - vertices[j1] = Float.floatToRawIntBits(f8); - vertices[j1 + 1] = Float.floatToRawIntBits(f3); - vertices[j1 + 2] = Float.floatToRawIntBits(f4); + FaceInfo.VertexInfo vertexInfo = faceinfo.getVertexInfo(i1); + float x = normals[vertexInfo.xFace]; + float y = normals[vertexInfo.yFace]; + float z = normals[vertexInfo.zFace]; + vertices[j1] = Float.floatToRawIntBits(x); + vertices[j1 + 1] = Float.floatToRawIntBits(y); + vertices[j1 + 2] = Float.floatToRawIntBits(z); for (int k = 0; k < 4; ++k) { int l = 8 * k; - float f5 = Float.intBitsToFloat(aint[l]); - float f6 = Float.intBitsToFloat(aint[l + 1]); - float f7 = Float.intBitsToFloat(aint[l + 2]); - if (Mth.equal(f8, f5) && Mth.equal(f3, f6) && Mth.equal(f4, f7)) { - vertices[j1 + 4] = aint[l + 4]; - vertices[j1 + 4 + 1] = aint[l + 4 + 1]; + float nX = Float.intBitsToFloat(newVertices[l]); + float xY = Float.intBitsToFloat(newVertices[l + 1]); + float nZ = Float.intBitsToFloat(newVertices[l + 2]); + // noinspection SuspiciousNameCombination + if (Mth.equal(x, nX) && Mth.equal(y, xY) && Mth.equal(z, nZ)) { + vertices[j1 + 4] = newVertices[l + 4]; + vertices[j1 + 4 + 1] = newVertices[l + 4 + 1]; } } } diff --git a/src/main/java/com/gregtechceu/gtceu/common/CommonProxy.java b/src/main/java/com/gregtechceu/gtceu/common/CommonProxy.java index 32882139d3..cc0a4f06be 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/CommonProxy.java +++ b/src/main/java/com/gregtechceu/gtceu/common/CommonProxy.java @@ -16,6 +16,21 @@ import com.gregtechceu.gtceu.api.data.worldgen.WorldGenLayers; import com.gregtechceu.gtceu.api.data.worldgen.generator.IndicatorGenerators; import com.gregtechceu.gtceu.api.data.worldgen.generator.VeinGenerators; +import com.gregtechceu.gtceu.api.graphnet.GraphClassRegistrationEvent; +import com.gregtechceu.gtceu.api.graphnet.logic.ChannelCountLogic; +import com.gregtechceu.gtceu.api.graphnet.logic.NetLogicRegistrationEvent; +import com.gregtechceu.gtceu.api.graphnet.logic.ThroughputLogic; +import com.gregtechceu.gtceu.api.graphnet.logic.WeightFactorLogic; +import com.gregtechceu.gtceu.api.graphnet.net.BlankNetNode; +import com.gregtechceu.gtceu.api.graphnet.net.BlockPosNode; +import com.gregtechceu.gtceu.api.graphnet.net.NetEdge; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeCapConnectionNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.logic.TemperatureLogic; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.PipeStructureRegistrationEvent; +import com.gregtechceu.gtceu.api.graphnet.pipenet.predicate.BlockedPredicate; +import com.gregtechceu.gtceu.api.graphnet.pipenet.predicate.FilterPredicate; +import com.gregtechceu.gtceu.api.graphnet.predicate.NetPredicateRegistrationEvent; import com.gregtechceu.gtceu.api.gui.factory.CoverUIFactory; import com.gregtechceu.gtceu.api.gui.factory.GTUIEditorFactory; import com.gregtechceu.gtceu.api.gui.factory.MachineUIFactory; @@ -26,11 +41,23 @@ import com.gregtechceu.gtceu.api.recipe.ingredient.SizedIngredient; import com.gregtechceu.gtceu.api.registry.GTRegistries; import com.gregtechceu.gtceu.common.data.*; +import com.gregtechceu.gtceu.common.data.datafixer.GTDataFixers; import com.gregtechceu.gtceu.common.data.machines.GTMachineUtils; import com.gregtechceu.gtceu.common.data.materials.GTFoods; import com.gregtechceu.gtceu.common.item.tool.rotation.CustomBlockRotations; import com.gregtechceu.gtceu.common.machine.multiblock.electric.FusionReactorMachine; import com.gregtechceu.gtceu.common.network.GTNetwork; +import com.gregtechceu.gtceu.common.pipelike.block.cable.CableStructure; +import com.gregtechceu.gtceu.common.pipelike.block.duct.DuctStructure; +import com.gregtechceu.gtceu.common.pipelike.block.laser.LaserStructure; +import com.gregtechceu.gtceu.common.pipelike.block.optical.OpticalStructure; +import com.gregtechceu.gtceu.common.pipelike.block.pipe.MaterialPipeStructure; +import com.gregtechceu.gtceu.common.pipelike.net.duct.DuctFlowLogic; +import com.gregtechceu.gtceu.common.pipelike.net.duct.DuctThroughputLogic; +import com.gregtechceu.gtceu.common.pipelike.net.energy.*; +import com.gregtechceu.gtceu.common.pipelike.net.fluid.FluidContainmentLogic; +import com.gregtechceu.gtceu.common.pipelike.net.fluid.FluidFlowLogic; +import com.gregtechceu.gtceu.common.pipelike.net.item.ItemFlowLogic; import com.gregtechceu.gtceu.common.registry.GTRegistration; import com.gregtechceu.gtceu.common.unification.material.MaterialRegistryManager; import com.gregtechceu.gtceu.config.ConfigHolder; @@ -174,6 +201,7 @@ public static void init() { GTFeatures.register(); CustomBlockRotations.init(); KeyBind.init(); + GTDataFixers.init(); FusionReactorMachine.registerFusionTier(GTValues.LuV, " (MKI)"); FusionReactorMachine.registerFusionTier(GTValues.ZPM, " (MKII)"); @@ -228,6 +256,48 @@ public void modConstruct(FMLConstructModEvent event) { event.enqueueWork(CommonProxy::init); } + @SubscribeEvent + public void registerPipeStructures(PipeStructureRegistrationEvent event) { + CableStructure.register(event); + MaterialPipeStructure.register(event); + LaserStructure.register(event); + OpticalStructure.register(event); + DuctStructure.register(event); + } + + @SubscribeEvent + public void registerNetLogics(NetLogicRegistrationEvent event) { + event.accept(ChannelCountLogic.TYPE); + event.accept(DuctFlowLogic.TYPE); + event.accept(EnergyFlowLogic.TYPE); + event.accept(FluidFlowLogic.TYPE); + event.accept(ItemFlowLogic.TYPE); + event.accept(FluidContainmentLogic.TYPE); + event.accept(SuperconductorLogic.TYPE); + event.accept(TemperatureLogic.TYPE); + event.accept(ThroughputLogic.TYPE); + event.accept(DuctThroughputLogic.TYPE); + event.accept(WeightFactorLogic.TYPE); + event.accept(VoltageLimitLogic.TYPE); + event.accept(VoltageLossLogic.TYPE); + event.accept(AmperageLimitLogic.TYPE); + } + + @SubscribeEvent + public static void registerGraphClasses(GraphClassRegistrationEvent event) { + event.accept(NetEdge.TYPE); + event.accept(WorldPipeNode.TYPE); + event.accept(WorldPipeCapConnectionNode.TYPE); + event.accept(BlockPosNode.TYPE); + event.accept(BlankNetNode.TYPE); + } + + @SubscribeEvent + public static void registerNetPredicates(NetPredicateRegistrationEvent event) { + event.accept(BlockedPredicate.TYPE); + event.accept(FilterPredicate.TYPE); + } + @SubscribeEvent public void commonSetup(FMLCommonSetupEvent event) { event.enqueueWork(() -> { diff --git a/src/main/java/com/gregtechceu/gtceu/common/block/CableBlock.java b/src/main/java/com/gregtechceu/gtceu/common/block/CableBlock.java deleted file mode 100644 index b1372d647b..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/block/CableBlock.java +++ /dev/null @@ -1,151 +0,0 @@ -package com.gregtechceu.gtceu.common.block; - -import com.gregtechceu.gtceu.GTCEu; -import com.gregtechceu.gtceu.api.GTValues; -import com.gregtechceu.gtceu.api.block.MaterialPipeBlock; -import com.gregtechceu.gtceu.api.blockentity.PipeBlockEntity; -import com.gregtechceu.gtceu.api.capability.forge.GTCapability; -import com.gregtechceu.gtceu.api.data.chemical.material.Material; -import com.gregtechceu.gtceu.api.data.chemical.material.properties.PropertyKey; -import com.gregtechceu.gtceu.api.data.chemical.material.properties.WireProperties; -import com.gregtechceu.gtceu.api.data.tag.TagPrefix; -import com.gregtechceu.gtceu.api.pipenet.IPipeNode; -import com.gregtechceu.gtceu.client.model.PipeModel; -import com.gregtechceu.gtceu.common.blockentity.CableBlockEntity; -import com.gregtechceu.gtceu.common.data.GTBlockEntities; -import com.gregtechceu.gtceu.common.data.GTDamageTypes; -import com.gregtechceu.gtceu.common.data.GTMaterialBlocks; -import com.gregtechceu.gtceu.common.pipelike.cable.Insulation; -import com.gregtechceu.gtceu.common.pipelike.cable.LevelEnergyNet; -import com.gregtechceu.gtceu.utils.GTUtil; - -import net.minecraft.MethodsReturnNonnullByDefault; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.network.chat.Component; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.TooltipFlag; -import net.minecraft.world.level.BlockAndTintGetter; -import net.minecraft.world.level.BlockGetter; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.entity.BlockEntityType; -import net.minecraft.world.level.block.state.BlockState; - -import org.jetbrains.annotations.Nullable; - -import java.util.List; - -import javax.annotation.ParametersAreNonnullByDefault; - -/** - * @author KilaBash - * @date 2023/3/1 - * @implNote CableBlock - */ -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault -public class CableBlock extends MaterialPipeBlock { - - public CableBlock(Properties properties, Insulation insulation, Material material) { - super(properties, insulation, material); - } - - @Override - public int tinted(BlockState blockState, @Nullable BlockAndTintGetter blockAndTintGetter, - @Nullable BlockPos blockPos, int index) { - if (pipeType.isCable && index == 0) { - return 0x404040; - } - return index == 0 || index == 1 ? material.getMaterialRGB() : -1; - } - - @Override - protected WireProperties createProperties(Insulation insulation, Material material) { - return insulation.modifyProperties(material.getProperty(PropertyKey.WIRE)); - } - - @Override - protected WireProperties createMaterialData() { - return material.getProperty(PropertyKey.WIRE); - } - - @Override - public LevelEnergyNet getWorldPipeNet(ServerLevel level) { - return LevelEnergyNet.getOrCreate(level); - } - - @Override - public BlockEntityType> getBlockEntityType() { - return GTBlockEntities.CABLE.get(); - } - - @Override - public boolean canPipesConnect(IPipeNode selfTile, Direction side, - IPipeNode sideTile) { - return selfTile instanceof CableBlockEntity && sideTile instanceof CableBlockEntity; - } - - @Override - public boolean canPipeConnectToBlock(IPipeNode selfTile, Direction side, - @Nullable BlockEntity tile) { - return tile != null && - tile.getCapability(GTCapability.CAPABILITY_ENERGY_CONTAINER, side.getOpposite()).isPresent(); - } - - @Override - protected PipeModel createPipeModel() { - return pipeType.createPipeModel(material); - } - - @Override - public void appendHoverText(ItemStack stack, @Nullable BlockGetter level, List tooltip, - TooltipFlag flag) { - super.appendHoverText(stack, level, tooltip, flag); - WireProperties wireProperties = createProperties(defaultBlockState(), stack); - int tier = GTUtil.getTierByVoltage(wireProperties.getVoltage()); - if (wireProperties.isSuperconductor()) - tooltip.add(Component.translatable("gtceu.cable.superconductor", GTValues.VN[tier])); - tooltip.add(Component.translatable("gtceu.cable.voltage", wireProperties.getVoltage(), GTValues.VNF[tier])); - tooltip.add(Component.translatable("gtceu.cable.amperage", wireProperties.getAmperage())); - tooltip.add(Component.translatable("gtceu.cable.loss_per_block", wireProperties.getLossPerBlock())); - } - - @Override - public void entityInside(BlockState state, Level level, BlockPos pos, Entity entity) { - // dont apply damage if there is a frame box - var pipeNode = getPipeTile(level, pos); - if (pipeNode == null) { - GTCEu.LOGGER.error("Pipe was null"); - return; - } - if (pipeNode.getFrameMaterial() != null) { - BlockState frameState = GTMaterialBlocks.MATERIAL_BLOCKS.get(TagPrefix.frameGt, pipeNode.getFrameMaterial()) - .getDefaultState(); - frameState.getBlock().entityInside(frameState, level, pos, entity); - return; - } - if (level.isClientSide) return; - - Insulation insulation = getPipeTile(level, pos).getPipeType(); - if (insulation.insulationLevel == -1 && entity instanceof LivingEntity entityLiving) { - CableBlockEntity cable = (CableBlockEntity) getPipeTile(level, pos); - if (cable != null && cable.getFrameMaterial() == null && cable.getNodeData().getLossPerBlock() > 0) { - long voltage = cable.getCurrentMaxVoltage(); - double amperage = cable.getAverageAmperage(); - if (voltage > 0L && amperage > 0L) { - float damageAmount = (float) ((GTUtil.getTierByVoltage(voltage) + 1) * amperage * 4); - entityLiving.hurt(GTDamageTypes.ELECTRIC.source(level), damageAmount); - if (entityLiving instanceof ServerPlayer) { - // TODO advancments - // AdvancementTriggers.ELECTROCUTION_DEATH.trigger((ServerPlayer) entityLiving); - } - } - } - } - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/block/DuctPipeBlock.java b/src/main/java/com/gregtechceu/gtceu/common/block/DuctPipeBlock.java deleted file mode 100644 index f08d13c97f..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/block/DuctPipeBlock.java +++ /dev/null @@ -1,110 +0,0 @@ -package com.gregtechceu.gtceu.common.block; - -import com.gregtechceu.gtceu.api.block.PipeBlock; -import com.gregtechceu.gtceu.api.blockentity.MetaMachineBlockEntity; -import com.gregtechceu.gtceu.api.blockentity.PipeBlockEntity; -import com.gregtechceu.gtceu.api.capability.forge.GTCapability; -import com.gregtechceu.gtceu.api.machine.feature.IEnvironmentalHazardCleaner; -import com.gregtechceu.gtceu.api.machine.feature.IEnvironmentalHazardEmitter; -import com.gregtechceu.gtceu.api.pipenet.IPipeNode; -import com.gregtechceu.gtceu.client.model.PipeModel; -import com.gregtechceu.gtceu.client.renderer.block.PipeBlockRenderer; -import com.gregtechceu.gtceu.common.blockentity.DuctPipeBlockEntity; -import com.gregtechceu.gtceu.common.data.GTBlockEntities; -import com.gregtechceu.gtceu.common.pipelike.duct.DuctPipeProperties; -import com.gregtechceu.gtceu.common.pipelike.duct.DuctPipeType; -import com.gregtechceu.gtceu.common.pipelike.duct.LevelDuctPipeNet; - -import net.minecraft.MethodsReturnNonnullByDefault; -import net.minecraft.core.Direction; -import net.minecraft.network.chat.Component; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.TooltipFlag; -import net.minecraft.world.level.BlockGetter; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.entity.BlockEntityType; -import net.minecraft.world.level.block.state.BlockState; - -import org.jetbrains.annotations.Nullable; - -import java.util.List; - -import javax.annotation.ParametersAreNonnullByDefault; - -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault -public class DuctPipeBlock extends PipeBlock { - - public final PipeBlockRenderer renderer; - public final PipeModel model; - private final DuctPipeProperties properties; - - public DuctPipeBlock(Properties properties, DuctPipeType type) { - super(properties, type); - this.properties = new DuctPipeProperties(type.getRateMultiplier()); - this.model = type.createPipeModel(); - this.renderer = new PipeBlockRenderer(this.model); - } - - @Override - public LevelDuctPipeNet getWorldPipeNet(ServerLevel world) { - return LevelDuctPipeNet.getOrCreate(world); - } - - @Override - public BlockEntityType> getBlockEntityType() { - return GTBlockEntities.DUCT_PIPE.get(); - } - - @Override - public DuctPipeProperties createRawData(BlockState pState, @Nullable ItemStack pStack) { - return properties; - } - - @Override - public DuctPipeProperties createProperties(IPipeNode pipeTile) { - DuctPipeType pipeType = pipeTile.getPipeType(); - if (pipeType == null) return getFallbackType(); - return this.pipeType.modifyProperties(properties); - } - - @Override - public DuctPipeProperties getFallbackType() { - return properties; - } - - @Override - public @Nullable PipeBlockRenderer getRenderer(BlockState state) { - return renderer; - } - - @Override - protected PipeModel getPipeModel() { - return model; - } - - @Override - public boolean canPipesConnect(IPipeNode selfTile, Direction side, - IPipeNode sideTile) { - return selfTile instanceof DuctPipeBlockEntity && sideTile instanceof DuctPipeBlockEntity; - } - - @Override - public boolean canPipeConnectToBlock(IPipeNode selfTile, Direction side, - @Nullable BlockEntity tile) { - return tile != null && - (tile.getCapability(GTCapability.CAPABILITY_HAZARD_CONTAINER, side.getOpposite()).isPresent() || - tile instanceof MetaMachineBlockEntity metaMachine && - (metaMachine.getMetaMachine() instanceof IEnvironmentalHazardCleaner || - metaMachine.getMetaMachine() instanceof IEnvironmentalHazardEmitter)); - } - - @Override - public void appendHoverText(ItemStack stack, @Nullable BlockGetter level, List tooltip, - TooltipFlag flag) { - super.appendHoverText(stack, level, tooltip, flag); - tooltip.add(Component.translatable("gtceu.duct_pipe.transfer_rate", - this.pipeType.modifyProperties(this.properties).getTransferRate())); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/block/FluidPipeBlock.java b/src/main/java/com/gregtechceu/gtceu/common/block/FluidPipeBlock.java deleted file mode 100644 index b7a96e0725..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/block/FluidPipeBlock.java +++ /dev/null @@ -1,173 +0,0 @@ -package com.gregtechceu.gtceu.common.block; - -import com.gregtechceu.gtceu.GTCEu; -import com.gregtechceu.gtceu.api.block.MaterialPipeBlock; -import com.gregtechceu.gtceu.api.blockentity.PipeBlockEntity; -import com.gregtechceu.gtceu.api.data.chemical.material.Material; -import com.gregtechceu.gtceu.api.data.chemical.material.properties.FluidPipeProperties; -import com.gregtechceu.gtceu.api.data.chemical.material.properties.PropertyKey; -import com.gregtechceu.gtceu.api.data.tag.TagPrefix; -import com.gregtechceu.gtceu.api.pipenet.IPipeNode; -import com.gregtechceu.gtceu.client.model.PipeModel; -import com.gregtechceu.gtceu.common.blockentity.FluidPipeBlockEntity; -import com.gregtechceu.gtceu.common.data.GTBlockEntities; -import com.gregtechceu.gtceu.common.data.GTMaterialBlocks; -import com.gregtechceu.gtceu.common.pipelike.fluidpipe.FluidPipeType; -import com.gregtechceu.gtceu.common.pipelike.fluidpipe.LevelFluidPipeNet; -import com.gregtechceu.gtceu.utils.EntityDamageUtil; -import com.gregtechceu.gtceu.utils.GTUtil; - -import net.minecraft.MethodsReturnNonnullByDefault; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.network.chat.Component; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.TooltipFlag; -import net.minecraft.world.level.BlockGetter; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.entity.BlockEntityType; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraftforge.common.capabilities.ForgeCapabilities; -import net.minecraftforge.fluids.FluidStack; - -import org.jetbrains.annotations.Nullable; - -import java.util.List; - -import javax.annotation.ParametersAreNonnullByDefault; - -/** - * @author KilaBash - * @date 2023/3/1 - * @implNote FluidPipeBlock - */ -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault -public class FluidPipeBlock extends MaterialPipeBlock { - - public FluidPipeBlock(Properties properties, FluidPipeType fluidPipeType, Material material) { - super(properties, fluidPipeType, material); - } - - @Override - protected FluidPipeProperties createProperties(FluidPipeType fluidPipeType, Material material) { - return fluidPipeType.modifyProperties(material.getProperty(PropertyKey.FLUID_PIPE)); - } - - @Override - protected FluidPipeProperties createMaterialData() { - return material.getProperty(PropertyKey.FLUID_PIPE); - } - - @Override - public LevelFluidPipeNet getWorldPipeNet(ServerLevel level) { - return LevelFluidPipeNet.getOrCreate(level); - } - - @Override - public BlockEntityType> getBlockEntityType() { - return GTBlockEntities.FLUID_PIPE.get(); - } - - @Override - public boolean canPipesConnect(IPipeNode selfTile, Direction side, - IPipeNode sideTile) { - return selfTile instanceof FluidPipeBlockEntity && sideTile instanceof FluidPipeBlockEntity; - } - - @Override - public boolean canPipeConnectToBlock(IPipeNode selfTile, Direction side, - @Nullable BlockEntity tile) { - return tile != null && tile.getCapability(ForgeCapabilities.FLUID_HANDLER, side.getOpposite()).isPresent(); - } - - @Override - protected PipeModel createPipeModel() { - return pipeType.createPipeModel(material); - } - - @Override - public void appendHoverText(ItemStack stack, @Nullable BlockGetter level, List tooltip, - TooltipFlag flag) { - super.appendHoverText(stack, level, tooltip, flag); - FluidPipeProperties properties = createProperties(defaultBlockState(), stack); - - tooltip.add(Component.translatable("gtceu.universal.tooltip.fluid_transfer_rate", properties.getThroughput())); - tooltip.add(Component.translatable("gtceu.fluid_pipe.max_temperature", properties.getMaxFluidTemperature())); - - if (properties.getChannels() > 1) { - tooltip.add(Component.translatable("gtceu.fluid_pipe.channels", properties.getChannels())); - } - - if (!GTUtil.isShiftDown()) { - tooltip.add(Component.translatable("gtceu.tooltip.fluid_pipe_hold_shift")); - return; - } - - if (properties.isGasProof()) - tooltip.add(Component.translatable("gtceu.fluid_pipe.gas_proof")); - else - tooltip.add(Component.translatable("gtceu.fluid_pipe.not_gas_proof")); - - if (properties.isAcidProof()) tooltip.add(Component.translatable("gtceu.fluid_pipe.acid_proof")); - if (properties.isCryoProof()) tooltip.add(Component.translatable("gtceu.fluid_pipe.cryo_proof")); - if (properties.isPlasmaProof()) tooltip.add(Component.translatable("gtceu.fluid_pipe.plasma_proof")); - } - - @Override - public void entityInside(BlockState state, Level level, BlockPos pos, Entity entity) { - // dont apply damage if there is a frame box - var pipeNode = getPipeTile(level, pos); - if (pipeNode == null) { - GTCEu.LOGGER.error("Pipe was null"); - return; - } - if (pipeNode.getFrameMaterial() != null) { - BlockState frameState = GTMaterialBlocks.MATERIAL_BLOCKS.get(TagPrefix.frameGt, pipeNode.getFrameMaterial()) - .getDefaultState(); - frameState.getBlock().entityInside(frameState, level, pos, entity); - return; - } - if (level.isClientSide) return; - if (level.getBlockEntity(pos) == null) return; - FluidPipeBlockEntity pipe = (FluidPipeBlockEntity) level.getBlockEntity(pos); - - if (pipe.getOffsetTimer() % 10 == 0) { - if (entity instanceof LivingEntity livingEntity) { - if (pipe.getFluidTanks().length > 1) { - // apply temperature damage for the hottest and coldest pipe (multi fluid pipes) - int maxTemperature = Integer.MIN_VALUE; - int minTemperature = Integer.MAX_VALUE; - for (var tank : pipe.getFluidTanks()) { - FluidStack stack = tank.getFluid(); - if (tank.getFluid() != null && tank.getFluid().getAmount() > 0) { - maxTemperature = Math.max(maxTemperature, - stack.getFluid().getFluidType().getTemperature(stack)); - minTemperature = Math.min(minTemperature, - stack.getFluid().getFluidType().getTemperature(stack)); - } - } - if (maxTemperature != Integer.MIN_VALUE) { - EntityDamageUtil.applyTemperatureDamage(livingEntity, maxTemperature, 1.0F, 20); - } - if (minTemperature != Integer.MAX_VALUE) { - EntityDamageUtil.applyTemperatureDamage(livingEntity, minTemperature, 1.0F, 20); - } - } else { - var tank = pipe.getFluidTanks()[0]; - if (tank.getFluid() != null && tank.getFluid().getAmount() > 0) { - // Apply temperature damage for the pipe (single fluid pipes) - FluidStack stack = tank.getFluid(); - EntityDamageUtil.applyTemperatureDamage(livingEntity, - stack.getFluid().getFluidType().getTemperature(stack), 1.0F, 20); - } - } - } - } - super.entityInside(state, level, pos, entity); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/block/ItemPipeBlock.java b/src/main/java/com/gregtechceu/gtceu/common/block/ItemPipeBlock.java deleted file mode 100644 index c7affc0a67..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/block/ItemPipeBlock.java +++ /dev/null @@ -1,94 +0,0 @@ -package com.gregtechceu.gtceu.common.block; - -import com.gregtechceu.gtceu.api.block.MaterialPipeBlock; -import com.gregtechceu.gtceu.api.blockentity.PipeBlockEntity; -import com.gregtechceu.gtceu.api.data.chemical.material.Material; -import com.gregtechceu.gtceu.api.data.chemical.material.properties.ItemPipeProperties; -import com.gregtechceu.gtceu.api.data.chemical.material.properties.PropertyKey; -import com.gregtechceu.gtceu.api.pipenet.IPipeNode; -import com.gregtechceu.gtceu.client.model.PipeModel; -import com.gregtechceu.gtceu.common.blockentity.ItemPipeBlockEntity; -import com.gregtechceu.gtceu.common.data.GTBlockEntities; -import com.gregtechceu.gtceu.common.pipelike.item.ItemPipeType; -import com.gregtechceu.gtceu.common.pipelike.item.LevelItemPipeNet; - -import net.minecraft.MethodsReturnNonnullByDefault; -import net.minecraft.core.Direction; -import net.minecraft.network.chat.Component; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.TooltipFlag; -import net.minecraft.world.level.BlockGetter; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.entity.BlockEntityType; -import net.minecraftforge.common.capabilities.ForgeCapabilities; - -import org.jetbrains.annotations.Nullable; - -import java.util.List; - -import javax.annotation.ParametersAreNonnullByDefault; - -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault -public class ItemPipeBlock extends MaterialPipeBlock { - - public ItemPipeBlock(Properties properties, ItemPipeType itemPipeType, Material material) { - super(properties, itemPipeType, material); - } - - @Override - protected ItemPipeProperties createProperties(ItemPipeType itemPipeType, Material material) { - return itemPipeType.modifyProperties(material.getProperty(PropertyKey.ITEM_PIPE)); - } - - @Override - protected ItemPipeProperties createMaterialData() { - return material.getProperty(PropertyKey.ITEM_PIPE); - } - - @Override - protected PipeModel createPipeModel() { - return pipeType.createPipeModel(material); - } - - @Override - public LevelItemPipeNet getWorldPipeNet(ServerLevel level) { - return LevelItemPipeNet.getOrCreate(level); - } - - @Override - public BlockEntityType> getBlockEntityType() { - return GTBlockEntities.ITEM_PIPE.get(); - } - - @Override - public void appendHoverText(ItemStack stack, @Nullable BlockGetter level, List tooltip, - TooltipFlag flag) { - super.appendHoverText(stack, level, tooltip, flag); - ItemPipeProperties properties = createProperties(defaultBlockState(), stack); - - if (properties.getTransferRate() % 1 != 0) { - tooltip.add(Component.translatable("gtceu.universal.tooltip.item_transfer_rate", - (int) ((properties.getTransferRate() * 64) + 0.5))); - } else { - tooltip.add(Component.translatable("gtceu.universal.tooltip.item_transfer_rate_stacks", - (int) properties.getTransferRate())); - } - - tooltip.add(Component.translatable("gtceu.item_pipe.priority", properties.getPriority())); - } - - @Override - public boolean canPipesConnect(IPipeNode selfTile, Direction side, - IPipeNode sideTile) { - return selfTile instanceof ItemPipeBlockEntity && sideTile instanceof ItemPipeBlockEntity; - } - - @Override - public boolean canPipeConnectToBlock(IPipeNode selfTile, Direction side, - @Nullable BlockEntity tile) { - return tile != null && - tile.getCapability(ForgeCapabilities.ITEM_HANDLER, side.getOpposite()).isPresent(); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/block/LaserPipeBlock.java b/src/main/java/com/gregtechceu/gtceu/common/block/LaserPipeBlock.java deleted file mode 100644 index 7819738747..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/block/LaserPipeBlock.java +++ /dev/null @@ -1,115 +0,0 @@ -package com.gregtechceu.gtceu.common.block; - -import com.gregtechceu.gtceu.GTCEu; -import com.gregtechceu.gtceu.api.block.PipeBlock; -import com.gregtechceu.gtceu.api.blockentity.PipeBlockEntity; -import com.gregtechceu.gtceu.api.capability.forge.GTCapability; -import com.gregtechceu.gtceu.api.pipenet.IPipeNode; -import com.gregtechceu.gtceu.client.model.PipeModel; -import com.gregtechceu.gtceu.client.renderer.block.PipeBlockRenderer; -import com.gregtechceu.gtceu.common.blockentity.LaserPipeBlockEntity; -import com.gregtechceu.gtceu.common.data.GTBlockEntities; -import com.gregtechceu.gtceu.common.pipelike.laser.LaserPipeProperties; -import com.gregtechceu.gtceu.common.pipelike.laser.LaserPipeType; -import com.gregtechceu.gtceu.common.pipelike.laser.LevelLaserPipeNet; - -import net.minecraft.MethodsReturnNonnullByDefault; -import net.minecraft.client.color.block.BlockColor; -import net.minecraft.core.Direction; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.entity.BlockEntityType; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; - -import org.jetbrains.annotations.Nullable; - -import javax.annotation.ParametersAreNonnullByDefault; - -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault -public class LaserPipeBlock extends PipeBlock { - - public final PipeBlockRenderer renderer; - public final PipeModel model; - private final LaserPipeProperties properties; - - public LaserPipeBlock(Properties properties, LaserPipeType type) { - super(properties, type); - this.properties = LaserPipeProperties.INSTANCE; - this.model = new PipeModel(LaserPipeType.NORMAL.getThickness(), () -> GTCEu.id("block/pipe/pipe_laser_side"), - () -> GTCEu.id("block/pipe/pipe_laser_in"), null, null); - this.renderer = new PipeBlockRenderer(this.model); - } - - @OnlyIn(Dist.CLIENT) - public static BlockColor tintedColor() { - return (blockState, level, blockPos, index) -> { - if (blockPos != null && level != null && - level.getBlockEntity(blockPos) instanceof PipeBlockEntity pipe) { - if (pipe.getFrameMaterial() != null) { - if (index == 3) { - return pipe.getFrameMaterial().getMaterialRGB(); - } else if (index == 4) { - return pipe.getFrameMaterial().getMaterialSecondaryRGB(); - } - } - if (pipe.isPainted()) { - return pipe.getRealColor(); - } - } - return -1; - }; - } - - @Override - public LevelLaserPipeNet getWorldPipeNet(ServerLevel world) { - return LevelLaserPipeNet.getOrCreate(world); - } - - @Override - public BlockEntityType> getBlockEntityType() { - return GTBlockEntities.LASER_PIPE.get(); - } - - @Override - public LaserPipeProperties createRawData(BlockState pState, @Nullable ItemStack pStack) { - return LaserPipeProperties.INSTANCE; - } - - @Override - public LaserPipeProperties createProperties(IPipeNode pipeTile) { - LaserPipeType pipeType = pipeTile.getPipeType(); - if (pipeType == null) return getFallbackType(); - return this.pipeType.modifyProperties(properties); - } - - @Override - public LaserPipeProperties getFallbackType() { - return LaserPipeProperties.INSTANCE; - } - - @Override - public @Nullable PipeBlockRenderer getRenderer(BlockState state) { - return renderer; - } - - @Override - protected PipeModel getPipeModel() { - return model; - } - - @Override - public boolean canPipesConnect(IPipeNode selfTile, Direction side, - IPipeNode sideTile) { - return selfTile instanceof LaserPipeBlockEntity && sideTile instanceof LaserPipeBlockEntity; - } - - @Override - public boolean canPipeConnectToBlock(IPipeNode selfTile, Direction side, - @Nullable BlockEntity tile) { - return tile != null && tile.getCapability(GTCapability.CAPABILITY_LASER, side.getOpposite()).isPresent(); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/block/OpticalPipeBlock.java b/src/main/java/com/gregtechceu/gtceu/common/block/OpticalPipeBlock.java deleted file mode 100644 index 015d2b002d..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/block/OpticalPipeBlock.java +++ /dev/null @@ -1,117 +0,0 @@ -package com.gregtechceu.gtceu.common.block; - -import com.gregtechceu.gtceu.GTCEu; -import com.gregtechceu.gtceu.api.block.PipeBlock; -import com.gregtechceu.gtceu.api.blockentity.PipeBlockEntity; -import com.gregtechceu.gtceu.api.capability.forge.GTCapability; -import com.gregtechceu.gtceu.api.pipenet.IPipeNode; -import com.gregtechceu.gtceu.client.model.PipeModel; -import com.gregtechceu.gtceu.client.renderer.block.PipeBlockRenderer; -import com.gregtechceu.gtceu.common.blockentity.OpticalPipeBlockEntity; -import com.gregtechceu.gtceu.common.data.GTBlockEntities; -import com.gregtechceu.gtceu.common.pipelike.optical.LevelOpticalPipeNet; -import com.gregtechceu.gtceu.common.pipelike.optical.OpticalPipeProperties; -import com.gregtechceu.gtceu.common.pipelike.optical.OpticalPipeType; - -import net.minecraft.client.color.block.BlockColor; -import net.minecraft.core.Direction; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.entity.BlockEntityType; -import net.minecraft.world.level.block.state.BlockBehaviour; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; - -import lombok.Getter; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.annotation.ParametersAreNonnullByDefault; - -@ParametersAreNonnullByDefault -public class OpticalPipeBlock extends PipeBlock { - - public final PipeBlockRenderer renderer; - @Getter - public final PipeModel pipeModel; - - private final OpticalPipeType pipeType; - private final OpticalPipeProperties properties; - - public OpticalPipeBlock(BlockBehaviour.Properties properties, @NotNull OpticalPipeType pipeType) { - super(properties, pipeType); - this.pipeType = pipeType; - this.properties = OpticalPipeProperties.INSTANCE; - this.pipeModel = new PipeModel(pipeType.getThickness(), () -> GTCEu.id("block/pipe/pipe_optical_side"), - () -> GTCEu.id("block/pipe/pipe_optical_in"), null, null); - this.renderer = new PipeBlockRenderer(this.pipeModel); - } - - @Override - public LevelOpticalPipeNet getWorldPipeNet(ServerLevel level) { - return LevelOpticalPipeNet.getOrCreate(level); - } - - @Override - public BlockEntityType> getBlockEntityType() { - return GTBlockEntities.OPTICAL_PIPE.get(); - } - - @Override - public OpticalPipeProperties createRawData(BlockState pState, @Nullable ItemStack pStack) { - return null; - } - - @Override - public OpticalPipeProperties createProperties(@NotNull IPipeNode pipeTile) { - OpticalPipeType pipeType = pipeTile.getPipeType(); - if (pipeType == null) return getFallbackType(); - return this.pipeType.modifyProperties(properties); - } - - @Override - public OpticalPipeProperties getFallbackType() { - return OpticalPipeProperties.INSTANCE; - } - - @Override - public @Nullable PipeBlockRenderer getRenderer(BlockState state) { - return renderer; - } - - @OnlyIn(Dist.CLIENT) - public static BlockColor tintedColor() { - return (blockState, level, blockPos, index) -> { - if (blockPos != null && level != null && - level.getBlockEntity(blockPos) instanceof PipeBlockEntity pipe) { - if (pipe.getFrameMaterial() != null) { - if (index == 3) { - return pipe.getFrameMaterial().getMaterialRGB(); - } else if (index == 4) { - return pipe.getFrameMaterial().getMaterialSecondaryRGB(); - } - } - if (pipe.isPainted()) { - return pipe.getRealColor(); - } - } - return -1; - }; - } - - @Override - public boolean canPipesConnect(IPipeNode selfTile, Direction side, - IPipeNode sideTile) { - return selfTile instanceof OpticalPipeBlockEntity && sideTile instanceof OpticalPipeBlockEntity; - } - - @Override - public boolean canPipeConnectToBlock(IPipeNode selfTile, Direction side, - @Nullable BlockEntity tile) { - if (tile == null) return false; - if (tile.getCapability(GTCapability.CAPABILITY_DATA_ACCESS, side.getOpposite()).isPresent()) return true; - return tile.getCapability(GTCapability.CAPABILITY_COMPUTATION_PROVIDER, side.getOpposite()).isPresent(); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/block/StoneTypes.java b/src/main/java/com/gregtechceu/gtceu/common/block/StoneTypes.java index 96706d4a42..8ec9e8610b 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/block/StoneTypes.java +++ b/src/main/java/com/gregtechceu/gtceu/common/block/StoneTypes.java @@ -22,7 +22,7 @@ public enum StoneTypes implements StringRepresentable { DEEPSLATE("deepslate", MapColor.DEEPSLATE, true, () -> Blocks.DEEPSLATE::defaultBlockState, GTMaterials.Deepslate, false), RED_GRANITE("red_granite", MapColor.COLOR_RED, true, () -> GTBlocks.RED_GRANITE::getDefaultState, - GTMaterials.GraniteRed), + GTMaterials.RedGranite), MARBLE("marble", MapColor.QUARTZ, true, () -> GTBlocks.MARBLE::getDefaultState, GTMaterials.Marble), ANDESITE("andesite", MapColor.STONE, true, () -> Blocks.ANDESITE::defaultBlockState, GTMaterials.Andesite, false), GRANITE("granite", MapColor.DIRT, true, () -> Blocks.GRANITE::defaultBlockState, GTMaterials.Granite, false), diff --git a/src/main/java/com/gregtechceu/gtceu/common/blockentity/CableBlockEntity.java b/src/main/java/com/gregtechceu/gtceu/common/blockentity/CableBlockEntity.java deleted file mode 100644 index 4af5ac9de1..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/blockentity/CableBlockEntity.java +++ /dev/null @@ -1,367 +0,0 @@ -package com.gregtechceu.gtceu.common.blockentity; - -import com.gregtechceu.gtceu.api.GTValues; -import com.gregtechceu.gtceu.api.blockentity.PipeBlockEntity; -import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; -import com.gregtechceu.gtceu.api.capability.IEnergyContainer; -import com.gregtechceu.gtceu.api.capability.forge.GTCapability; -import com.gregtechceu.gtceu.api.data.chemical.material.properties.WireProperties; -import com.gregtechceu.gtceu.api.gui.GuiTextures; -import com.gregtechceu.gtceu.api.item.tool.GTToolType; -import com.gregtechceu.gtceu.api.machine.TickableSubscription; -import com.gregtechceu.gtceu.api.machine.feature.IDataInfoProvider; -import com.gregtechceu.gtceu.common.block.CableBlock; -import com.gregtechceu.gtceu.common.data.GTMaterialBlocks; -import com.gregtechceu.gtceu.common.item.PortableScannerBehavior; -import com.gregtechceu.gtceu.common.pipelike.cable.*; -import com.gregtechceu.gtceu.utils.FormattingUtil; -import com.gregtechceu.gtceu.utils.GTMath; -import com.gregtechceu.gtceu.utils.GTUtil; - -import com.lowdragmc.lowdraglib.gui.texture.ResourceTexture; -import com.lowdragmc.lowdraglib.syncdata.annotation.DescSynced; -import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; -import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; - -import net.minecraft.ChatFormatting; -import net.minecraft.MethodsReturnNonnullByDefault; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.core.particles.ParticleTypes; -import net.minecraft.network.chat.Component; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.entity.BlockEntityType; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraftforge.common.capabilities.Capability; -import net.minecraftforge.common.util.LazyOptional; - -import lombok.Getter; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.EnumMap; -import java.util.List; - -import javax.annotation.ParametersAreNonnullByDefault; - -/** - * @author KilaBash - * @date 2023/3/1 - * @implNote CableBlockEntity - */ -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault -public class CableBlockEntity extends PipeBlockEntity implements IDataInfoProvider { - - public static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder(CableBlockEntity.class, - PipeBlockEntity.MANAGED_FIELD_HOLDER); - - protected WeakReference currentEnergyNet = new WeakReference<>(null); - - private static final int meltTemp = 3000; - - private final EnumMap handlers = new EnumMap<>(Direction.class); - private final PerTickLongCounter maxVoltageCounter = new PerTickLongCounter(); - private final AveragingPerTickCounter averageVoltageCounter = new AveragingPerTickCounter(); - private final AveragingPerTickCounter averageAmperageCounter = new AveragingPerTickCounter(); - private EnergyNetHandler defaultHandler; - private int heatQueue; - @Getter - @Persisted - @DescSynced - private int temperature = getDefaultTemp(); - private TickableSubscription heatSubs; - - public CableBlockEntity(BlockEntityType type, BlockPos pos, BlockState blockState) { - super(type, pos, blockState); - } - - public static CableBlockEntity create(BlockEntityType type, BlockPos pos, BlockState blockState) { - return new CableBlockEntity(type, pos, blockState); - } - - @Override - public @NotNull LazyOptional getCapability(@NotNull Capability cap, @Nullable Direction side) { - if (cap == GTCapability.CAPABILITY_ENERGY_CONTAINER) { - var container = getEnergyContainer(side); - if (container != null) { - return GTCapability.CAPABILITY_ENERGY_CONTAINER.orEmpty(cap, LazyOptional.of(() -> container)); - } - } else if (cap == GTCapability.CAPABILITY_COVERABLE) { - return GTCapability.CAPABILITY_COVERABLE.orEmpty(cap, LazyOptional.of(this::getCoverContainer)); - } else if (cap == GTCapability.CAPABILITY_TOOLABLE) { - return GTCapability.CAPABILITY_TOOLABLE.orEmpty(cap, LazyOptional.of(() -> this)); - } - return super.getCapability(cap, side); - } - - @Override - public boolean canAttachTo(Direction side) { - if (level != null) { - if (level.getBlockEntity(getBlockPos().relative(side)) instanceof CableBlockEntity) { - return false; - } - return GTCapabilityHelper.getEnergyContainer(level, getBlockPos().relative(side), side.getOpposite()) != - null; - } - return false; - } - - @Nullable - private EnergyNet getEnergyNet() { - if (!(level instanceof ServerLevel serverLevel)) - return null; - EnergyNet currentEnergyNet = this.currentEnergyNet.get(); - if (currentEnergyNet != null && currentEnergyNet.isValid() && - currentEnergyNet.containsNode(getBlockPos())) - return currentEnergyNet; // return current net if it is still valid - LevelEnergyNet worldENet = LevelEnergyNet.getOrCreate(serverLevel); - currentEnergyNet = worldENet.getNetFromPos(getBlockPos()); - if (currentEnergyNet != null) { - this.currentEnergyNet = new WeakReference<>(currentEnergyNet); - } - return currentEnergyNet; - } - - public void checkNetwork() { - if (defaultHandler != null) { - EnergyNet current = getEnergyNet(); - if (defaultHandler.getNet() != current) { - defaultHandler.updateNetwork(current); - for (EnergyNetHandler handler : handlers.values()) { - handler.updateNetwork(current); - } - } - } - } - - @Nullable - public IEnergyContainer getEnergyContainer(@Nullable Direction side) { - if (side != null && !isConnected(side)) return null; - // the EnergyNetHandler can only be created on the server, so we have an empty placeholder for the client - if (isRemote()) return IEnergyContainer.DEFAULT; - if (handlers.isEmpty()) - initHandlers(); - checkNetwork(); - return handlers.getOrDefault(side, defaultHandler); - } - - @Override - public boolean canHaveBlockedFaces() { - return false; - } - - private void initHandlers() { - EnergyNet net = getEnergyNet(); - if (net == null) { - return; - } - for (Direction facing : GTUtil.DIRECTIONS) { - handlers.put(facing, new EnergyNetHandler(net, this, facing)); - } - defaultHandler = new EnergyNetHandler(net, this, null); - } - - @Override - public void onLoad() { - super.onLoad(); - if (!level.isClientSide) { - setTemperature(temperature); - if (temperature > getDefaultTemp()) { - subscribeHeat(); - } - } - } - - private void subscribeHeat() { - if (this.heatSubs == null) { - this.heatSubs = subscribeServerTick(this::update); - } - } - - private void unsubscribeHeat() { - if (this.heatSubs != null) { - this.unsubscribe(this.heatSubs); - this.heatSubs = null; - } - } - - public CableBlock getPipeBlock() { - return (CableBlock) super.getPipeBlock(); - } - - public double getAverageAmperage() { - return averageAmperageCounter.getAverage(getLevel()); - } - - public long getCurrentMaxVoltage() { - return maxVoltageCounter.get(getLevel()); - } - - public double getAverageVoltage() { - return averageVoltageCounter.getAverage(getLevel()); - } - - public long getMaxAmperage() { - return getNodeData().getAmperage(); - } - - public long getMaxVoltage() { - return getNodeData().getVoltage(); - } - - public int getDefaultTemp() { - return 293; - } - - public static int getMeltTemp() { - return meltTemp; - } - - /** - * Should only be called internally - * - * @return if the cable should be destroyed - */ - public boolean incrementAmperage(long amps, long voltage) { - if (voltage > maxVoltageCounter.get(getLevel())) { - maxVoltageCounter.set(getLevel(), voltage); - } - averageVoltageCounter.increment(getLevel(), voltage * amps); - averageAmperageCounter.increment(getLevel(), amps); - - int dif = GTMath.saturatedCast(averageAmperageCounter.getLast(getLevel()) - getMaxAmperage()); - if (dif > 0) { - applyHeat(dif * 40); - return true; - } - - return false; - } - - public void applyHeat(int amount) { - heatQueue += amount; - if (!level.isClientSide && heatSubs == null && temperature + heatQueue > getDefaultTemp()) { - subscribeHeat(); - } - } - - private boolean update() { - if (heatQueue > 0) { - // if received heat from overvolting or overamping, add heat - setTemperature(temperature + heatQueue); - } - - if (temperature >= meltTemp) { - // cable melted - level.setBlockAndUpdate(worldPosition, Blocks.FIRE.defaultBlockState()); - return false; - } - - if (temperature <= getDefaultTemp()) { - unsubscribeHeat(); - return false; - } - - if (getPipeType().insulationLevel >= 0 && temperature >= 1500 && GTValues.RNG.nextFloat() < 0.1) { - // insulation melted - uninsulate(); - return false; - } - - if (heatQueue == 0) { - // otherwise cool down - setTemperature((int) (temperature - Math.pow(temperature - getDefaultTemp(), 0.35))); - } else { - heatQueue = 0; - } - return true; - } - - private void uninsulate() { - int temp = temperature; - setTemperature(getDefaultTemp()); - int index = getPipeType().insulationLevel; - CableBlock newBlock = GTMaterialBlocks.CABLE_BLOCKS - .get(Insulation.values()[index].tagPrefix, getPipeBlock().material) - .get(); - level.setBlockAndUpdate(getBlockPos(), newBlock.defaultBlockState()); - CableBlockEntity newCable = (CableBlockEntity) level.getBlockEntity(getBlockPos()); - if (newCable != null) { // should never be null - newCable.setTemperature(temp); - newCable.subscribeHeat(); - for (Direction facing : GTUtil.DIRECTIONS) { - if (isConnected(facing)) { - newCable.setConnection(facing, true, true); - } - } - newCable.setChanged(); - // force a block rerender - newCable.scheduleRenderUpdate(); - } - } - - public void setTemperature(int temperature) { - this.temperature = temperature; - level.getLightEngine().checkBlock(worldPosition); - if (!level.isClientSide && temperature >= meltTemp) { - var facing = Direction.UP; - float xPos = facing.getStepX() * 0.76F + worldPosition.getX() + 0.25F; - float yPos = facing.getStepY() * 0.76F + worldPosition.getY() + 0.25F; - float zPos = facing.getStepZ() * 0.76F + worldPosition.getZ() + 0.25F; - - float ySpd = facing.getStepY() * 0.1F + 0.2F + 0.1F * GTValues.RNG.nextFloat(); - float temp = GTValues.RNG.nextFloat() * 2 * (float) Math.PI; - float xSpd = (float) Math.sin(temp) * 0.1F; - float zSpd = (float) Math.cos(temp) * 0.1F; - - ((ServerLevel) level).sendParticles(ParticleTypes.SMOKE, - xPos + GTValues.RNG.nextFloat() * 0.5F, - yPos + GTValues.RNG.nextFloat() * 0.5F, - zPos + GTValues.RNG.nextFloat() * 0.5F, - 0, - xSpd, ySpd, zSpd, 1); - } - } - - public static void onBlockEntityRegister(BlockEntityType cableBlockEntityBlockEntityType) {} - - ////////////////////////////////////// - // ******* Interaction *******// - ////////////////////////////////////// - - @Override - public ResourceTexture getPipeTexture(boolean isBlock) { - return isBlock ? GuiTextures.TOOL_WIRE_CONNECT : GuiTextures.TOOL_WIRE_BLOCK; - } - - @Override - public GTToolType getPipeTuneTool() { - return GTToolType.WIRE_CUTTER; - } - - @Override - public ManagedFieldHolder getFieldHolder() { - return MANAGED_FIELD_HOLDER; - } - - @Override - public @NotNull List getDataInfo(PortableScannerBehavior.DisplayMode mode) { - List list = new ArrayList<>(); - - if (mode == PortableScannerBehavior.DisplayMode.SHOW_ALL || - mode == PortableScannerBehavior.DisplayMode.SHOW_ELECTRICAL_INFO) { - list.add(Component.translatable("behavior.portable_scanner.eu_per_sec", - Component.translatable(FormattingUtil.formatNumbers(getAverageVoltage())) - .withStyle(ChatFormatting.RED))); - list.add(Component.translatable("behavior.portable_scanner.amp_per_sec", - Component.translatable(FormattingUtil.formatNumbers(getAverageAmperage())) - .withStyle(ChatFormatting.RED))); - } - - return list; - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/blockentity/DuctPipeBlockEntity.java b/src/main/java/com/gregtechceu/gtceu/common/blockentity/DuctPipeBlockEntity.java deleted file mode 100644 index d2e27a6984..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/blockentity/DuctPipeBlockEntity.java +++ /dev/null @@ -1,149 +0,0 @@ -package com.gregtechceu.gtceu.common.blockentity; - -import com.gregtechceu.gtceu.api.blockentity.PipeBlockEntity; -import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; -import com.gregtechceu.gtceu.api.capability.IHazardParticleContainer; -import com.gregtechceu.gtceu.api.capability.forge.GTCapability; -import com.gregtechceu.gtceu.api.data.medicalcondition.MedicalCondition; -import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; -import com.gregtechceu.gtceu.api.machine.feature.IEnvironmentalHazardCleaner; -import com.gregtechceu.gtceu.api.machine.feature.IEnvironmentalHazardEmitter; -import com.gregtechceu.gtceu.common.pipelike.duct.*; -import com.gregtechceu.gtceu.utils.GTUtil; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.block.entity.BlockEntityType; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraftforge.common.capabilities.Capability; -import net.minecraftforge.common.util.LazyOptional; - -import lombok.Getter; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.ref.WeakReference; -import java.util.EnumMap; - -public class DuctPipeBlockEntity extends PipeBlockEntity { - - @Getter - protected final EnumMap handlers = new EnumMap<>(Direction.class); - // the DuctNetHandler can only be created on the server, so we have an empty placeholder for the client - public final IHazardParticleContainer clientCapability = new DefaultDuctContainer(); - private WeakReference currentPipeNet = new WeakReference<>(null); - @Getter - protected DuctNetHandler defaultHandler; - - protected DuctPipeBlockEntity(BlockEntityType type, BlockPos pos, BlockState blockState) { - super(type, pos, blockState); - } - - public static DuctPipeBlockEntity create(BlockEntityType type, BlockPos pos, BlockState blockState) { - return new DuctPipeBlockEntity(type, pos, blockState); - } - - public static void onBlockEntityRegister(BlockEntityType ductBlockEntityBlockEntityType) {} - - @Override - public @NotNull LazyOptional getCapability(@NotNull Capability cap, @Nullable Direction side) { - if (cap == GTCapability.CAPABILITY_HAZARD_CONTAINER) { - if (getLevel().isClientSide()) - return GTCapability.CAPABILITY_HAZARD_CONTAINER.orEmpty(cap, LazyOptional.of(() -> clientCapability)); - - if (handlers.isEmpty()) { - initHandlers(); - } - checkNetwork(); - return GTCapability.CAPABILITY_HAZARD_CONTAINER.orEmpty(cap, - LazyOptional.of(() -> handlers.getOrDefault(side, defaultHandler))); - } else if (cap == GTCapability.CAPABILITY_COVERABLE) { - return GTCapability.CAPABILITY_COVERABLE.orEmpty(cap, LazyOptional.of(this::getCoverContainer)); - } else if (cap == GTCapability.CAPABILITY_TOOLABLE) { - return GTCapability.CAPABILITY_TOOLABLE.orEmpty(cap, LazyOptional.of(() -> this)); - } - return super.getCapability(cap, side); - } - - @Override - public boolean canHaveBlockedFaces() { - return false; - } - - public void initHandlers() { - DuctPipeNet net = getDuctPipeNet(); - if (net == null) return; - for (Direction facing : GTUtil.DIRECTIONS) { - handlers.put(facing, new DuctNetHandler(net, this, facing)); - } - defaultHandler = new DuctNetHandler(net, this, null); - } - - public void checkNetwork() { - if (defaultHandler != null) { - DuctPipeNet current = getDuctPipeNet(); - if (defaultHandler.getNet() != current) { - defaultHandler.updateNetwork(current); - for (DuctNetHandler handler : handlers.values()) { - handler.updateNetwork(current); - } - } - } - } - - public DuctPipeNet getDuctPipeNet() { - if (level == null || level.isClientSide) { - return null; - } - DuctPipeNet currentPipeNet = this.currentPipeNet.get(); - if (currentPipeNet != null && currentPipeNet.isValid() && currentPipeNet.containsNode(getPipePos())) { - return currentPipeNet; - } - LevelDuctPipeNet worldNet = (LevelDuctPipeNet) getPipeBlock().getWorldPipeNet((ServerLevel) getPipeLevel()); - currentPipeNet = worldNet.getNetFromPos(getPipePos()); - if (currentPipeNet != null) { - this.currentPipeNet = new WeakReference<>(currentPipeNet); - } - return currentPipeNet; - } - - @Override - public boolean canAttachTo(Direction side) { - if (level != null) { - if (level.getBlockEntity(getBlockPos().relative(side)) instanceof DuctPipeBlockEntity) { - return false; - } - BlockPos relative = getBlockPos().relative(side); - return GTCapabilityHelper.getHazardContainer(level, relative, side.getOpposite()) != - null || - (level.getBlockEntity(relative) instanceof IMachineBlockEntity machineBlockEntity && - (machineBlockEntity.getMetaMachine() instanceof IEnvironmentalHazardCleaner || - machineBlockEntity.getMetaMachine() instanceof IEnvironmentalHazardEmitter)); - } - return false; - } - - private static class DefaultDuctContainer implements IHazardParticleContainer { - - @Override - public boolean inputsHazard(Direction side, MedicalCondition condition) { - return false; - } - - @Override - public float changeHazard(MedicalCondition condition, float differenceAmount) { - return 0; - } - - @Override - public float getHazardStored(MedicalCondition condition) { - return 0; - } - - @Override - public float getHazardCapacity(MedicalCondition condition) { - return 0; - } - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/blockentity/FluidPipeBlockEntity.java b/src/main/java/com/gregtechceu/gtceu/common/blockentity/FluidPipeBlockEntity.java deleted file mode 100644 index ebd205d8d5..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/blockentity/FluidPipeBlockEntity.java +++ /dev/null @@ -1,581 +0,0 @@ -package com.gregtechceu.gtceu.common.blockentity; - -import com.gregtechceu.gtceu.api.GTValues; -import com.gregtechceu.gtceu.api.blockentity.PipeBlockEntity; -import com.gregtechceu.gtceu.api.capability.ICoverable; -import com.gregtechceu.gtceu.api.capability.forge.GTCapability; -import com.gregtechceu.gtceu.api.capability.recipe.IO; -import com.gregtechceu.gtceu.api.cover.CoverBehavior; -import com.gregtechceu.gtceu.api.data.chemical.material.properties.FluidPipeProperties; -import com.gregtechceu.gtceu.api.fluids.FluidConstants; -import com.gregtechceu.gtceu.api.fluids.FluidState; -import com.gregtechceu.gtceu.api.fluids.GTFluid; -import com.gregtechceu.gtceu.api.fluids.attribute.FluidAttribute; -import com.gregtechceu.gtceu.api.machine.TickableSubscription; -import com.gregtechceu.gtceu.api.machine.feature.IDataInfoProvider; -import com.gregtechceu.gtceu.api.misc.IOFluidHandlerList; -import com.gregtechceu.gtceu.api.transfer.fluid.CustomFluidTank; -import com.gregtechceu.gtceu.api.transfer.fluid.IFluidHandlerModifiable; -import com.gregtechceu.gtceu.common.cover.FluidFilterCover; -import com.gregtechceu.gtceu.common.cover.PumpCover; -import com.gregtechceu.gtceu.common.cover.data.ManualIOMode; -import com.gregtechceu.gtceu.common.item.PortableScannerBehavior; -import com.gregtechceu.gtceu.common.pipelike.fluidpipe.FluidPipeType; -import com.gregtechceu.gtceu.common.pipelike.fluidpipe.PipeTankList; -import com.gregtechceu.gtceu.utils.EntityDamageUtil; -import com.gregtechceu.gtceu.utils.FormattingUtil; -import com.gregtechceu.gtceu.utils.GTTransferUtils; -import com.gregtechceu.gtceu.utils.GTUtil; - -import net.minecraft.ChatFormatting; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.core.particles.ParticleOptions; -import net.minecraft.core.particles.ParticleTypes; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.Tag; -import net.minecraft.network.chat.Component; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.sounds.SoundEvents; -import net.minecraft.sounds.SoundSource; -import net.minecraft.util.Mth; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.entity.BlockEntityType; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.material.Fluid; -import net.minecraft.world.level.material.Fluids; -import net.minecraft.world.phys.AABB; -import net.minecraftforge.common.capabilities.Capability; -import net.minecraftforge.common.capabilities.ForgeCapabilities; -import net.minecraftforge.common.util.LazyOptional; -import net.minecraftforge.fluids.FluidStack; -import net.minecraftforge.fluids.capability.IFluidHandler; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.EnumMap; -import java.util.List; -import java.util.function.Predicate; - -public class FluidPipeBlockEntity extends PipeBlockEntity - implements IDataInfoProvider { - - public static final int FREQUENCY = 5; - - public byte lastReceivedFrom = 0, oldLastReceivedFrom = 0; - private PipeTankList pipeTankList; - private final EnumMap tankLists = new EnumMap<>(Direction.class); - private CustomFluidTank[] fluidTanks; - private long timer = 0L; - private final int offset = GTValues.RNG.nextInt(20); - - private TickableSubscription updateSubs; - - public FluidPipeBlockEntity(BlockEntityType type, BlockPos pos, BlockState blockState) { - super(type, pos, blockState); - } - - public static void onBlockEntityRegister(BlockEntityType fluidPipeBlockEntityBlockEntityType) {} - - public long getOffsetTimer() { - return timer + offset; - } - - @Override - public void onLoad() { - super.onLoad(); - if (updateSubs == null) { - updateSubs = this.subscribeServerTick(this::update); - } - } - - @Override - public void onChunkUnloaded() { - super.onChunkUnloaded(); - if (updateSubs != null) { - this.unsubscribe(updateSubs); - updateSubs = null; - } - } - - @Override - public boolean canAttachTo(Direction side) { - if (level != null) { - if (level.getBlockEntity(getBlockPos().relative(side)) instanceof FluidPipeBlockEntity) { - return false; - } - return GTTransferUtils.hasAdjacentFluidHandler(level, getBlockPos(), side); - } - return false; - } - - private Predicate getFluidCapFilter(@Nullable Direction side, IO io) { - if (side != null) { - var cover = getCoverContainer().getCoverAtSide(side); - if (cover instanceof FluidFilterCover filterCover && filterCover.getFilterMode().filters(io)) { - return filterCover.getFluidFilter(); - } - } - return fluid -> true; - } - - @NotNull - @Override - public LazyOptional getCapability(Capability capability, @Nullable Direction facing) { - if (capability == ForgeCapabilities.FLUID_HANDLER) { - if (facing != null && isConnected(facing)) { - PipeTankList tankList = getTankList(facing); - if (tankList == null) - return LazyOptional.empty(); - - IOFluidHandlerList list = new IOFluidHandlerList(List.of(tankList), IO.BOTH, - getFluidCapFilter(facing, IO.IN), getFluidCapFilter(facing, IO.OUT)); - return ForgeCapabilities.FLUID_HANDLER.orEmpty(capability, - LazyOptional.of(() -> list)); - } - } else if (capability == GTCapability.CAPABILITY_COVERABLE) { - return GTCapability.CAPABILITY_COVERABLE.orEmpty(capability, LazyOptional.of(this::getCoverContainer)); - } else if (capability == GTCapability.CAPABILITY_TOOLABLE) { - return GTCapability.CAPABILITY_TOOLABLE.orEmpty(capability, LazyOptional.of(() -> this)); - } - return super.getCapability(capability, facing); - } - - public int getCapacityPerTank() { - return getNodeData().getThroughput() * 20; - } - - public void update() { - timer++; - if (!level.isClientSide && getOffsetTimer() % FREQUENCY == 0) { - lastReceivedFrom &= 63; - if (lastReceivedFrom == 63) { - lastReceivedFrom = 0; - } - - boolean shouldDistribute = (oldLastReceivedFrom == lastReceivedFrom); - int tanks = getNodeData().getChannels(); - for (int i = 0, j = GTValues.RNG.nextInt(tanks); i < tanks; i++) { - int index = (i + j) % tanks; - CustomFluidTank tank = getFluidTanks()[index]; - FluidStack fluid = tank.getFluid(); - if (fluid.isEmpty() || fluid.getFluid() == Fluids.EMPTY) - continue; - if (fluid.getAmount() <= 0) { - tank.setFluid(FluidStack.EMPTY); - continue; - } - - if (shouldDistribute) { - distributeFluid(index, tank, fluid); - lastReceivedFrom = 0; - } - } - oldLastReceivedFrom = lastReceivedFrom; - } - } - - private void distributeFluid(int channel, CustomFluidTank tank, FluidStack fluid) { - // Tank, From, Amount to receive - List tanks = new ArrayList<>(); - int amount = fluid.getAmount(); - - FluidStack maxFluid = fluid.copy(); - double availableCapacity = 0; - - for (byte i = 0, j = (byte) GTValues.RNG.nextInt(6); i < 6; i++) { - // Get a list of tanks accepting fluids, and what side they're on - byte side = (byte) ((i + j) % 6); - Direction facing = GTUtil.DIRECTIONS[side]; - - if (!isConnected(facing) || (lastReceivedFrom & (1 << side)) != 0) { - continue; - } - - BlockEntity neighbor = getNeighbor(facing); - if (neighbor == null) continue; - IFluidHandler fluidHandler = neighbor.getCapability(ForgeCapabilities.FLUID_HANDLER, facing.getOpposite()) - .resolve().orElse(null); - if (fluidHandler == null) continue; - - IFluidHandlerModifiable pipeTank = tank; - CoverBehavior cover = getCoverContainer().getCoverAtSide(facing); - - // pipeTank should only be determined by the cover attached to the actual pipe - if (cover != null) { - pipeTank = cover.getFluidHandlerCap(pipeTank); - // Shutter covers return null capability when active, so check here to prevent NPE - if (pipeTank == null || checkForPumpCover(cover)) continue; - } else { - ICoverable coverable = neighbor.getCapability(GTCapability.CAPABILITY_COVERABLE, facing.getOpposite()) - .resolve().orElse(null); - if (coverable != null) { - cover = coverable.getCoverAtSide(facing.getOpposite()); - if (checkForPumpCover(cover)) continue; - } - } - - FluidStack drainable = pipeTank.drain(maxFluid, IFluidHandler.FluidAction.SIMULATE); - if (drainable.isEmpty() || drainable.getAmount() <= 0) { - continue; - } - - int filled = Math.min(fluidHandler.fill(maxFluid, IFluidHandler.FluidAction.SIMULATE), - drainable.getAmount()); - - if (filled > 0) { - tanks.add(new FluidTransaction(fluidHandler, pipeTank, filled)); - availableCapacity += filled; - } - maxFluid.setAmount(amount); // Because some mods do actually modify input fluid stack - } - - if (availableCapacity <= 0) - return; - - // How much of this fluid is available for distribution? - final double maxAmount = Math.min(getCapacityPerTank() / 2, fluid.getAmount()); - - // Now distribute - for (FluidTransaction transaction : tanks) { - if (availableCapacity > maxAmount) { - // Distribute fluids based on percentage available space at destination - transaction.amount = Mth.floor(transaction.amount * maxAmount / availableCapacity); - } - if (transaction.amount == 0) { - if (tank.getFluidAmount() <= 0) break; // If there is no more stored fluid, stop transferring to prevent - // dupes - transaction.amount = 1; // If the percent is not enough to give at least 1L, try to give 1L - } else if (transaction.amount < 0) { - continue; - } - - FluidStack toInsert = fluid.copy(); - if (toInsert.isEmpty() || toInsert.getFluid() == Fluids.EMPTY) continue; - toInsert.setAmount(transaction.amount); - - int inserted = transaction.target.fill(toInsert, IFluidHandler.FluidAction.EXECUTE); - if (inserted > 0) { - transaction.pipeTank.drain(inserted, IFluidHandler.FluidAction.EXECUTE); - } - } - } - - private boolean checkForPumpCover(@Nullable CoverBehavior cover) { - if (cover instanceof PumpCover coverPump) { - int pipeThroughput = getNodeData().getThroughput() * 20; - if (coverPump.getTransferRate() > pipeThroughput) { - coverPump.setTransferRate(pipeThroughput); - } - return coverPump.getManualIOMode() == ManualIOMode.DISABLED; - } - return false; - } - - public void checkAndDestroy(@NotNull FluidStack stack) { - Fluid fluid = stack.getFluid(); - FluidPipeProperties prop = getNodeData(); - - boolean burning = prop.getMaxFluidTemperature() < fluid.getFluidType().getTemperature(stack); - boolean leaking = !prop.isGasProof() && fluid.getFluidType().getDensity(stack) < 0; - boolean shattering = !prop.isCryoProof() && - fluid.getFluidType().getTemperature(stack) < FluidConstants.CRYOGENIC_FLUID_THRESHOLD; - boolean corroding = false; - boolean melting = false; - - if (fluid instanceof GTFluid attributedFluid) { - FluidState state = attributedFluid.getState(); - if (!prop.canContain(state)) { - leaking = state == FluidState.GAS; - melting = state == FluidState.PLASMA; - } - - // carrying plasmas which are too hot when plasma proof does not burn pipes - if (burning && state == FluidState.PLASMA && prop.canContain(FluidState.PLASMA)) { - burning = false; - } - - for (FluidAttribute attribute : attributedFluid.getAttributes()) { - if (!prop.canContain(attribute)) { - // corrodes if the pipe can't handle the attribute, even if it's not an acid - corroding = true; - } - } - } - - if (burning || leaking || corroding || shattering || melting) { - destroyPipe(stack, burning, leaking, corroding, shattering, melting); - } - } - - public void destroyPipe(FluidStack stack, boolean isBurning, boolean isLeaking, boolean isCorroding, - boolean isShattering, boolean isMelting) { - // prevent the sound from spamming when filled from anything not a pipe - if (getOffsetTimer() % 10 == 0) { - level.playSound(null, this.getPipePos(), SoundEvents.LAVA_EXTINGUISH, SoundSource.BLOCKS, 1.0F, 1.0F); - } - - if (isLeaking) { - FluidPipeBlockEntity.spawnParticles(level, worldPosition, Direction.UP, ParticleTypes.SMOKE, - 7 + GTValues.RNG.nextInt(2)); - - // voids 10% - stack.setAmount(Math.max(0, stack.getAmount() * 9 / 10)); - - // apply heat damage in area surrounding the pipe - if (getOffsetTimer() % 20 == 0) { - List entities = getPipeLevel().getEntitiesOfClass(LivingEntity.class, - new AABB(getPipePos()).inflate(2)); - for (LivingEntity entityLivingBase : entities) { - EntityDamageUtil.applyTemperatureDamage(entityLivingBase, - stack.getFluid().getFluidType().getTemperature(stack), - 2.0F, 10); - } - } - - // chance to do a small explosion - if (GTValues.RNG.nextInt(isBurning ? 3 : 7) == 0) { - this.doExplosion(1.0f + GTValues.RNG.nextFloat()); - } - } - - if (isCorroding) { - FluidPipeBlockEntity.spawnParticles(getPipeLevel(), getPipePos(), Direction.UP, ParticleTypes.CRIT, - 3 + GTValues.RNG.nextInt(2)); - - // voids 25% - stack.setAmount(Math.max(0, stack.getAmount() * 3 / 4)); - - // apply chemical damage in area surrounding the pipe - if (getOffsetTimer() % 20 == 0) { - List entities = getPipeLevel().getEntitiesOfClass(LivingEntity.class, - new AABB(getPipePos()).inflate(1)); - for (LivingEntity entityLivingBase : entities) { - EntityDamageUtil.applyChemicalDamage(entityLivingBase, 2); - } - } - - // 1/10 chance to void everything and destroy the pipe - if (GTValues.RNG.nextInt(10) == 0) { - stack.setAmount(0); - level.removeBlock(getPipePos(), false); - } - } - - if (isBurning || isMelting) { - FluidPipeBlockEntity.spawnParticles(level, getBlockPos(), Direction.UP, ParticleTypes.FLAME, - (isMelting ? 7 : 3) + GTValues.RNG.nextInt(2)); - - // voids 75% - stack.setAmount(Math.max(0, stack.getAmount() / 4)); - - // 1/4 chance to burn everything around it - if (GTValues.RNG.nextInt(4) == 0) { - FluidPipeBlockEntity.setNeighboursToFire(level, getBlockPos()); - } - - // apply heat damage in area surrounding the pipe - if (isMelting && getOffsetTimer() % 20 == 0) { - List entities = getPipeLevel().getEntitiesOfClass(LivingEntity.class, - new AABB(getPipePos()).inflate(2)); - for (LivingEntity entityLivingBase : entities) { - EntityDamageUtil.applyTemperatureDamage(entityLivingBase, - stack.getFluid().getFluidType().getTemperature(stack), - 2.0F, 10); - } - } - - // 1/10 chance to void everything and burn the pipe - if (GTValues.RNG.nextInt(10) == 0) { - stack.setAmount(0); - level.setBlockAndUpdate(getBlockPos(), Blocks.FIRE.defaultBlockState()); - } - } - - if (isShattering) { - FluidPipeBlockEntity.spawnParticles(level, getBlockPos(), Direction.UP, ParticleTypes.CLOUD, - 3 + GTValues.RNG.nextInt(2)); - - // voids 75% - stack.setAmount(Math.max(0, stack.getAmount() / 4)); - - // apply frost damage in area surrounding the pipe - if (getOffsetTimer() % 20 == 0) { - List entities = getPipeLevel().getEntitiesOfClass(LivingEntity.class, - new AABB(getPipePos()).inflate(2)); - for (LivingEntity entityLivingBase : entities) { - EntityDamageUtil.applyTemperatureDamage(entityLivingBase, - stack.getFluid().getFluidType().getTemperature(stack), - 2.0F, 10); - } - } - - // 1/10 chance to void everything and freeze the pipe - if (GTValues.RNG.nextInt(10) == 0) { - stack.setAmount(0); - level.removeBlock(getBlockPos(), false); - } - } - } - - public void receivedFrom(Direction facing) { - if (facing != null) { - lastReceivedFrom |= (1 << facing.ordinal()); - } - } - - public FluidStack getContainedFluid(int channel) { - if (channel < 0 || channel >= getFluidTanks().length) return null; - return getFluidTanks()[channel].getFluid(); - } - - private void createTanksList() { - fluidTanks = new CustomFluidTank[getNodeData().getChannels()]; - for (int i = 0; i < getNodeData().getChannels(); i++) { - fluidTanks[i] = new CustomFluidTank(getCapacityPerTank()); - } - pipeTankList = new PipeTankList(this, null, fluidTanks); - for (Direction facing : GTUtil.DIRECTIONS) { - tankLists.put(facing, new PipeTankList(this, facing, fluidTanks)); - } - } - - public PipeTankList getTankList() { - if (pipeTankList == null || fluidTanks == null) { - createTanksList(); - } - return pipeTankList; - } - - public PipeTankList getTankList(Direction facing) { - if (tankLists.isEmpty() || fluidTanks == null) { - createTanksList(); - } - return tankLists.getOrDefault(facing, pipeTankList); - } - - public CustomFluidTank[] getFluidTanks() { - if (pipeTankList == null || fluidTanks == null) { - createTanksList(); - } - return fluidTanks; - } - - public FluidStack[] getContainedFluids() { - FluidStack[] fluids = new FluidStack[getFluidTanks().length]; - for (int i = 0; i < fluids.length; i++) { - fluids[i] = fluidTanks[i].getFluid(); - } - return fluids; - } - - @Override - public void saveCustomPersistedData(CompoundTag tag, boolean forDrop) { - super.saveCustomPersistedData(tag, forDrop); - ListTag list = new ListTag(); - for (int i = 0; i < getFluidTanks().length; i++) { - FluidStack stack1 = getContainedFluid(i); - CompoundTag fluidTag = new CompoundTag(); - if (stack1 == null || stack1.getAmount() <= 0) - fluidTag.putBoolean("isNull", true); - else - stack1.writeToNBT(fluidTag); - list.add(fluidTag); - } - tag.put("Fluids", list); - } - - @Override - public void loadCustomPersistedData(CompoundTag nbt) { - super.loadCustomPersistedData(nbt); - ListTag list = nbt.getList("Fluids", Tag.TAG_COMPOUND); - createTanksList(); - for (int i = 0; i < list.size(); i++) { - CompoundTag tag = list.getCompound(i); - if (!tag.getBoolean("isNull")) { - fluidTanks[i].setFluid(FluidStack.loadFluidStackFromNBT(tag)); - } - } - } - - public static void spawnParticles(Level worldIn, BlockPos pos, Direction direction, ParticleOptions particleType, - int particleCount) { - if (worldIn instanceof ServerLevel serverLevel) { - serverLevel.sendParticles(particleType, - pos.getX() + 0.5, - pos.getY() + 0.5, - pos.getZ() + 0.5, - particleCount, - direction.getStepX() * 0.2 + GTValues.RNG.nextDouble() * 0.1, - direction.getStepY() * 0.2 + GTValues.RNG.nextDouble() * 0.1, - direction.getStepZ() * 0.2 + GTValues.RNG.nextDouble() * 0.1, - 0.1); - } - } - - public static void setNeighboursToFire(Level world, BlockPos selfPos) { - for (Direction side : GTUtil.DIRECTIONS) { - if (!GTValues.RNG.nextBoolean()) continue; - BlockPos blockPos = selfPos.relative(side); - BlockState blockState = world.getBlockState(blockPos); - if (world.isEmptyBlock(blockPos) || - blockState.isFlammable(world, blockPos, side.getOpposite())) { - world.setBlockAndUpdate(blockPos, Blocks.FIRE.defaultBlockState()); - } - } - } - - @Override - public @NotNull List getDataInfo(PortableScannerBehavior.DisplayMode mode) { - List list = new ArrayList<>(); - - if (mode == PortableScannerBehavior.DisplayMode.SHOW_ALL || - mode == PortableScannerBehavior.DisplayMode.SHOW_MACHINE_INFO) { - FluidStack[] fluids = getContainedFluids(); - if (fluids != null) { - boolean allTanksEmpty = true; - for (int i = 0; i < fluids.length; i++) { - if (fluids[i] != null) { - if (fluids[i].getFluid() == null || fluids[i].isEmpty()) { - continue; - } - - allTanksEmpty = false; - list.add(Component.translatable("behavior.portable_scanner.tank", i, - Component.translatable(FormattingUtil.formatNumbers(fluids[i].getAmount())) - .withStyle(ChatFormatting.GREEN), - Component.translatable(FormattingUtil.formatNumbers(getCapacityPerTank())) - .withStyle(ChatFormatting.YELLOW), - fluids[i].getDisplayName().copy() - .withStyle(ChatFormatting.GOLD))); - } - } - - if (allTanksEmpty) { - list.add(Component.translatable("behavior.portable_scanner.tanks_empty")); - } - } - } - - return list; - } - - private static class FluidTransaction { - - public final IFluidHandler target; - public final IFluidHandler pipeTank; - public int amount; - - private FluidTransaction(IFluidHandler target, IFluidHandler pipeTank, int amount) { - this.target = target; - this.pipeTank = pipeTank; - this.amount = amount; - } - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/blockentity/ItemPipeBlockEntity.java b/src/main/java/com/gregtechceu/gtceu/common/blockentity/ItemPipeBlockEntity.java deleted file mode 100644 index 2236621a13..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/blockentity/ItemPipeBlockEntity.java +++ /dev/null @@ -1,194 +0,0 @@ -package com.gregtechceu.gtceu.common.blockentity; - -import com.gregtechceu.gtceu.api.blockentity.PipeBlockEntity; -import com.gregtechceu.gtceu.api.capability.forge.GTCapability; -import com.gregtechceu.gtceu.api.cover.CoverBehavior; -import com.gregtechceu.gtceu.api.data.chemical.material.properties.ItemPipeProperties; -import com.gregtechceu.gtceu.common.block.ItemPipeBlock; -import com.gregtechceu.gtceu.common.pipelike.item.ItemNetHandler; -import com.gregtechceu.gtceu.common.pipelike.item.ItemPipeNet; -import com.gregtechceu.gtceu.common.pipelike.item.ItemPipeType; -import com.gregtechceu.gtceu.utils.FacingPos; -import com.gregtechceu.gtceu.utils.GTTransferUtils; -import com.gregtechceu.gtceu.utils.GTUtil; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.entity.BlockEntityType; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraftforge.common.capabilities.Capability; -import net.minecraftforge.common.capabilities.ForgeCapabilities; -import net.minecraftforge.common.util.LazyOptional; -import net.minecraftforge.items.IItemHandlerModifiable; -import net.minecraftforge.items.ItemStackHandler; - -import it.unimi.dsi.fastutil.objects.Object2IntMap; -import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; -import lombok.Getter; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.ref.WeakReference; -import java.util.EnumMap; - -public class ItemPipeBlockEntity extends PipeBlockEntity { - - protected WeakReference currentItemPipeNet = new WeakReference<>(null); - - @Getter - private final EnumMap handlers = new EnumMap<>(Direction.class); - @Getter - private final Object2IntMap transferred = new Object2IntOpenHashMap<>(); - @Getter - private ItemNetHandler defaultHandler; - // the ItemNetHandler can only be created on the server so we have a empty placeholder for the client - private final IItemHandlerModifiable clientCapability = new ItemStackHandler(0); - - private int transferredItems = 0; - private long timer = 0; - - public ItemPipeBlockEntity(BlockEntityType type, BlockPos pos, BlockState blockState) { - super(type, pos, blockState); - } - - public static ItemPipeBlockEntity create(BlockEntityType type, BlockPos pos, BlockState blockState) { - return new ItemPipeBlockEntity(type, pos, blockState); - } - - public long getLevelTime() { - return hasLevel() ? getLevel().getGameTime() : 0L; - } - - public static void onBlockEntityRegister(BlockEntityType itemPipeBlockEntityBlockEntityType) {} - - @Override - public @NotNull LazyOptional getCapability(@NotNull Capability cap, @Nullable Direction side) { - if (cap == ForgeCapabilities.ITEM_HANDLER) { - Level world = getLevel(); - if (world.isClientSide()) - return LazyOptional.empty(); - - if (side != null && isConnected(side)) { - ensureHandlersInitialized(); - checkNetwork(); - if (this.currentItemPipeNet.get() == null) return LazyOptional.empty(); - return ForgeCapabilities.ITEM_HANDLER.orEmpty(cap, - LazyOptional.of(() -> getHandler(side, true))); - } - } else if (cap == GTCapability.CAPABILITY_COVERABLE) { - return GTCapability.CAPABILITY_COVERABLE.orEmpty(cap, LazyOptional.of(this::getCoverContainer)); - } else if (cap == GTCapability.CAPABILITY_TOOLABLE) { - return GTCapability.CAPABILITY_TOOLABLE.orEmpty(cap, LazyOptional.of(() -> this)); - } - return super.getCapability(cap, side); - } - - private void ensureHandlersInitialized() { - if (getHandlers().isEmpty()) - initHandlers(); - } - - public void initHandlers() { - ItemPipeNet net = getItemPipeNet(); - if (net == null) { - return; - } - for (Direction facing : GTUtil.DIRECTIONS) { - handlers.put(facing, new ItemNetHandler(net, this, facing)); - } - defaultHandler = new ItemNetHandler(net, this, null); - } - - public void checkNetwork() { - if (defaultHandler != null) { - ItemPipeNet current = getItemPipeNet(); - if (defaultHandler.getNet() != current) { - defaultHandler.updateNetwork(current); - for (ItemNetHandler handler : handlers.values()) { - handler.updateNetwork(current); - } - } - } - } - - @Override - public boolean canAttachTo(Direction side) { - if (level == null) return false; - if (level.getBlockEntity(getBlockPos().relative(side)) instanceof ItemPipeBlockEntity) { - return false; - } - return GTTransferUtils.hasAdjacentItemHandler(level, getBlockPos(), side); - } - - @Nullable - public ItemPipeNet getItemPipeNet() { - if (level instanceof ServerLevel serverLevel && - getBlockState().getBlock() instanceof ItemPipeBlock itemPipeBlock) { - ItemPipeNet currentItemPipeNet = this.currentItemPipeNet.get(); - if (currentItemPipeNet != null && currentItemPipeNet.isValid() && - currentItemPipeNet.containsNode(getBlockPos())) - return currentItemPipeNet; // return current net if it is still valid - currentItemPipeNet = itemPipeBlock.getWorldPipeNet(serverLevel).getNetFromPos(getBlockPos()); - if (currentItemPipeNet != null) { - this.currentItemPipeNet = new WeakReference<>(currentItemPipeNet); - } - } - return this.currentItemPipeNet.get(); - } - - public void resetTransferred() { - transferred.clear(); - } - - /** - * every time the transferred variable is accessed this method should be called - * if 20 ticks passed since the last access it will reset it - * this method is equal to - * - * @code { - * if (++time % 20 == 0) { - * this.transferredItems = 0; - * } - * } - *

- * if it was in a ticking TileEntity - */ - private void updateTransferredState() { - long currentTime = getLevelTime(); - long dif = currentTime - this.timer; - if (dif >= 20 || dif < 0) { - this.transferredItems = 0; - this.timer = currentTime; - } - } - - public void addTransferredItems(int amount) { - updateTransferredState(); - this.transferredItems += amount; - } - - public int getTransferredItems() { - updateTransferredState(); - return this.transferredItems; - } - - @Override - public void onChunkUnloaded() { - super.onChunkUnloaded(); - this.handlers.clear(); - } - - public IItemHandlerModifiable getHandler(@Nullable Direction side, boolean useCoverCapability) { - ensureHandlersInitialized(); - checkNetwork(); - if (this.currentItemPipeNet.get() == null) return null; - - ItemNetHandler handler = getHandlers().getOrDefault(side, getDefaultHandler()); - if (!useCoverCapability || side == null) return handler; - - CoverBehavior cover = getCoverContainer().getCoverAtSide(side); - return cover != null ? cover.getItemHandlerCap(handler) : handler; - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/blockentity/LaserPipeBlockEntity.java b/src/main/java/com/gregtechceu/gtceu/common/blockentity/LaserPipeBlockEntity.java deleted file mode 100644 index 0d35e2cd4b..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/blockentity/LaserPipeBlockEntity.java +++ /dev/null @@ -1,234 +0,0 @@ -package com.gregtechceu.gtceu.common.blockentity; - -import com.gregtechceu.gtceu.api.blockentity.PipeBlockEntity; -import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; -import com.gregtechceu.gtceu.api.capability.ILaserContainer; -import com.gregtechceu.gtceu.api.capability.forge.GTCapability; -import com.gregtechceu.gtceu.api.item.tool.GTToolType; -import com.gregtechceu.gtceu.api.pipenet.IPipeNode; -import com.gregtechceu.gtceu.common.pipelike.laser.*; -import com.gregtechceu.gtceu.utils.GTUtil; -import com.gregtechceu.gtceu.utils.TaskHandler; - -import com.lowdragmc.lowdraglib.syncdata.annotation.DescSynced; -import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; -import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.entity.BlockEntityType; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraftforge.common.capabilities.Capability; -import net.minecraftforge.common.util.LazyOptional; - -import lombok.Getter; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.ref.WeakReference; -import java.util.EnumMap; - -public class LaserPipeBlockEntity extends PipeBlockEntity { - - public static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder(LaserPipeBlockEntity.class, - PipeBlockEntity.MANAGED_FIELD_HOLDER); - - @Getter - protected final EnumMap handlers = new EnumMap<>(Direction.class); - // the LaserNetHandler can only be created on the server, so we have an empty placeholder for the client - public final ILaserContainer clientCapability = new DefaultLaserContainer(); - private WeakReference currentPipeNet = new WeakReference<>(null); - @Getter - protected LaserNetHandler defaultHandler; - - private int ticksActive = 0; - private int activeDuration = 0; - @Getter - @Persisted - @DescSynced - private boolean active = false; - - protected LaserPipeBlockEntity(BlockEntityType type, BlockPos pos, BlockState blockState) { - super(type, pos, blockState); - } - - public static LaserPipeBlockEntity create(BlockEntityType type, BlockPos pos, BlockState blockState) { - return new LaserPipeBlockEntity(type, pos, blockState); - } - - public static void onBlockEntityRegister(BlockEntityType cableBlockEntityBlockEntityType) {} - - @Override - public @NotNull LazyOptional getCapability(@NotNull Capability cap, @Nullable Direction side) { - if (cap == GTCapability.CAPABILITY_LASER) { - if (getLevel().isClientSide()) - return GTCapability.CAPABILITY_LASER.orEmpty(cap, LazyOptional.of(() -> clientCapability)); - - if (handlers.isEmpty()) { - initHandlers(); - } - checkNetwork(); - return GTCapability.CAPABILITY_LASER.orEmpty(cap, - LazyOptional.of(() -> handlers.getOrDefault(side, defaultHandler))); - } else if (cap == GTCapability.CAPABILITY_COVERABLE) { - return GTCapability.CAPABILITY_COVERABLE.orEmpty(cap, LazyOptional.of(this::getCoverContainer)); - } else if (cap == GTCapability.CAPABILITY_TOOLABLE) { - return GTCapability.CAPABILITY_TOOLABLE.orEmpty(cap, LazyOptional.of(() -> this)); - } - return super.getCapability(cap, side); - } - - @Override - public boolean canHaveBlockedFaces() { - return false; - } - - public void initHandlers() { - LaserPipeNet net = getLaserPipeNet(); - if (net == null) return; - for (Direction facing : GTUtil.DIRECTIONS) { - handlers.put(facing, new LaserNetHandler(net, this, facing)); - } - defaultHandler = new LaserNetHandler(net, this, null); - } - - public void checkNetwork() { - if (defaultHandler != null) { - LaserPipeNet current = getLaserPipeNet(); - if (defaultHandler.getNet() != current) { - defaultHandler.updateNetwork(current); - for (LaserNetHandler handler : handlers.values()) { - handler.updateNetwork(current); - } - } - } - } - - public LaserPipeNet getLaserPipeNet() { - if (level == null || level.isClientSide) { - return null; - } - LaserPipeNet currentPipeNet = this.currentPipeNet.get(); - if (currentPipeNet != null && currentPipeNet.isValid() && currentPipeNet.containsNode(getPipePos())) { - return currentPipeNet; - } - LevelLaserPipeNet worldNet = (LevelLaserPipeNet) getPipeBlock().getWorldPipeNet((ServerLevel) getPipeLevel()); - currentPipeNet = worldNet.getNetFromPos(getPipePos()); - if (currentPipeNet != null) { - this.currentPipeNet = new WeakReference<>(currentPipeNet); - } - return currentPipeNet; - } - - /** - * @param active if the pipe should become active - * @param duration how long the pipe should be active for - */ - public void setActive(boolean active, int duration) { - if (this.active != active) { - this.active = active; - notifyBlockUpdate(); - setChanged(); - if (active && duration != this.activeDuration) { - TaskHandler.enqueueServerTask((ServerLevel) getLevel(), this::queueDisconnect, 0); - } - } - - this.activeDuration = duration; - if (duration > 0 && active) { - this.ticksActive = 0; - } - } - - public boolean queueDisconnect() { - if (++this.ticksActive % activeDuration == 0) { - this.ticksActive = 0; - setActive(false, -1); - return false; - } - return true; - } - - @Override - public boolean canAttachTo(Direction side) { - if (level != null) { - if (level.getBlockEntity(getBlockPos().relative(side)) instanceof LaserPipeBlockEntity) { - return false; - } - return GTCapabilityHelper.getLaser(level, getBlockPos().relative(side), side.getOpposite()) != null; - } - return false; - } - - @Override - public void setConnection(Direction side, boolean connected, boolean fromNeighbor) { - if (!getLevel().isClientSide && connected && !fromNeighbor) { - int connections = getConnections(); - // block connection if any side other than the requested side and its opposite side are already connected. - connections &= ~(1 << side.ordinal()); - connections &= ~(1 << side.getOpposite().ordinal()); - if (connections != 0) return; - - // check the same for the targeted pipe - BlockEntity tile = getLevel().getBlockEntity(getBlockPos().relative(side)); - if (tile instanceof IPipeNode pipeTile && - pipeTile.getPipeType().getClass() == this.getPipeType().getClass()) { - connections = pipeTile.getConnections(); - connections &= ~(1 << side.ordinal()); - connections &= ~(1 << side.getOpposite().ordinal()); - if (connections != 0) return; - } - } - super.setConnection(side, connected, fromNeighbor); - } - - @Override - public GTToolType getPipeTuneTool() { - return GTToolType.WIRE_CUTTER; - } - - @Override - public ManagedFieldHolder getFieldHolder() { - return MANAGED_FIELD_HOLDER; - } - - private static class DefaultLaserContainer implements ILaserContainer { - - @Override - public long acceptEnergyFromNetwork(Direction side, long voltage, long amperage) { - return 0; - } - - @Override - public boolean inputsEnergy(Direction side) { - return false; - } - - @Override - public long changeEnergy(long differenceAmount) { - return 0; - } - - @Override - public long getEnergyStored() { - return 0; - } - - @Override - public long getEnergyCapacity() { - return 0; - } - - @Override - public long getInputAmperage() { - return 0; - } - - @Override - public long getInputVoltage() { - return 0; - } - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/blockentity/OpticalPipeBlockEntity.java b/src/main/java/com/gregtechceu/gtceu/common/blockentity/OpticalPipeBlockEntity.java deleted file mode 100644 index d366aaea2e..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/blockentity/OpticalPipeBlockEntity.java +++ /dev/null @@ -1,222 +0,0 @@ -package com.gregtechceu.gtceu.common.blockentity; - -import com.gregtechceu.gtceu.api.blockentity.PipeBlockEntity; -import com.gregtechceu.gtceu.api.capability.IDataAccessHatch; -import com.gregtechceu.gtceu.api.capability.IOpticalComputationProvider; -import com.gregtechceu.gtceu.api.capability.forge.GTCapability; -import com.gregtechceu.gtceu.api.item.tool.GTToolType; -import com.gregtechceu.gtceu.api.pipenet.IPipeNode; -import com.gregtechceu.gtceu.api.recipe.GTRecipe; -import com.gregtechceu.gtceu.common.pipelike.optical.*; -import com.gregtechceu.gtceu.utils.GTUtil; -import com.gregtechceu.gtceu.utils.TaskHandler; - -import com.lowdragmc.lowdraglib.syncdata.annotation.DescSynced; -import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; -import com.lowdragmc.lowdraglib.syncdata.annotation.RequireRerender; -import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.entity.BlockEntityType; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraftforge.common.capabilities.Capability; -import net.minecraftforge.common.util.LazyOptional; - -import lombok.Getter; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.ref.WeakReference; -import java.util.Collection; -import java.util.EnumMap; - -public class OpticalPipeBlockEntity extends PipeBlockEntity { - - public static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder(OpticalPipeBlockEntity.class, - PipeBlockEntity.MANAGED_FIELD_HOLDER); - - private final EnumMap handlers = new EnumMap<>(Direction.class); - // the OpticalNetHandler can only be created on the server, so we have an empty placeholder for the client - private final IDataAccessHatch clientDataHandler = new DefaultDataHandler(); - private final IOpticalComputationProvider clientComputationHandler = new DefaultComputationHandler(); - private WeakReference currentPipeNet = new WeakReference<>(null); - private OpticalNetHandler defaultHandler; - - @Getter - @Persisted - @DescSynced - @RequireRerender - private boolean isActive; - - public OpticalPipeBlockEntity(BlockEntityType type, BlockPos pos, BlockState blockState) { - super(type, pos, blockState); - } - - @Override - public boolean canHaveBlockedFaces() { - return false; - } - - private void initHandlers() { - OpticalPipeNet net = getOpticalPipeNet(); - if (net == null) return; - for (Direction facing : GTUtil.DIRECTIONS) { - handlers.put(facing, new OpticalNetHandler(net, this, facing)); - } - defaultHandler = new OpticalNetHandler(net, this, null); - } - - @Override - public LazyOptional getCapability(Capability capability, @Nullable Direction facing) { - if (capability == GTCapability.CAPABILITY_DATA_ACCESS) { - if (level.isClientSide) { - return GTCapability.CAPABILITY_DATA_ACCESS.orEmpty(capability, - LazyOptional.of(() -> clientDataHandler)); - } - - if (handlers.isEmpty()) initHandlers(); - - checkNetwork(); - return GTCapability.CAPABILITY_DATA_ACCESS.orEmpty(capability, - LazyOptional.of(() -> handlers.getOrDefault(facing, defaultHandler))); - } - - if (capability == GTCapability.CAPABILITY_COMPUTATION_PROVIDER) { - if (level.isClientSide) { - return GTCapability.CAPABILITY_COMPUTATION_PROVIDER.orEmpty(capability, - LazyOptional.of(() -> clientComputationHandler)); - } - - if (handlers.isEmpty()) initHandlers(); - - checkNetwork(); - return GTCapability.CAPABILITY_COMPUTATION_PROVIDER.orEmpty(capability, - LazyOptional.of(() -> handlers.getOrDefault(facing, defaultHandler))); - } - - if (capability == GTCapability.CAPABILITY_COVERABLE) { - return GTCapability.CAPABILITY_COVERABLE.orEmpty(capability, LazyOptional.of(this::getCoverContainer)); - } - - return super.getCapability(capability, facing); - } - - public void checkNetwork() { - if (defaultHandler != null) { - OpticalPipeNet current = getOpticalPipeNet(); - if (defaultHandler.getNet() != current) { - defaultHandler.updateNetwork(current); - for (OpticalNetHandler handler : handlers.values()) { - handler.updateNetwork(current); - } - } - } - } - - public OpticalPipeNet getOpticalPipeNet() { - if (level == null || level.isClientSide) - return null; - OpticalPipeNet currentPipeNet = this.currentPipeNet.get(); - if (currentPipeNet != null && currentPipeNet.isValid() && currentPipeNet.containsNode(getPipePos())) - return currentPipeNet; // if current net is valid and does contain position, return it - LevelOpticalPipeNet worldNet = (LevelOpticalPipeNet) getPipeBlock() - .getWorldPipeNet((ServerLevel) getPipeLevel()); - currentPipeNet = worldNet.getNetFromPos(getPipePos()); - if (currentPipeNet != null) { - this.currentPipeNet = new WeakReference<>(currentPipeNet); - } - return currentPipeNet; - } - - @Override - public boolean canAttachTo(Direction side) { - return false; - } - - @Override - public void setConnection(Direction side, boolean connected, boolean fromNeighbor) { - if (!getLevel().isClientSide && connected && !fromNeighbor) { - // never allow more than two connections total - if (getNumConnections() >= 2) return; - - // also check the other pipe - BlockEntity tile = getLevel().getBlockEntity(getPipePos().relative(side)); - if (tile instanceof IPipeNode pipeTile && - pipeTile.getPipeType().getClass() == this.getPipeType().getClass()) { - if (pipeTile.getNumConnections() >= 2) return; - } - } - super.setConnection(side, connected, fromNeighbor); - } - - /** - * @param active if the pipe should become active - * @param duration how long the pipe should be active for - */ - public void setActive(boolean active, int duration) { - boolean stateChanged = false; - if (this.isActive && !active) { - this.isActive = false; - stateChanged = true; - } else if (!this.isActive && active) { - this.isActive = true; - stateChanged = true; - TaskHandler.enqueueServerTask((ServerLevel) getLevel(), () -> setActive(false, -1), duration); - } - - if (stateChanged) { - notifyBlockUpdate(); - setChanged(); - } - } - - @Override - public void onChunkUnloaded() { - super.onChunkUnloaded(); - this.handlers.clear(); - } - - @Override - public GTToolType getPipeTuneTool() { - return GTToolType.WIRE_CUTTER; - } - - @Override - public ManagedFieldHolder getFieldHolder() { - return MANAGED_FIELD_HOLDER; - } - - private static class DefaultDataHandler implements IDataAccessHatch { - - @Override - public boolean isRecipeAvailable(@NotNull GTRecipe recipe, @NotNull Collection seen) { - return false; - } - - @Override - public boolean isCreative() { - return false; - } - } - - private static class DefaultComputationHandler implements IOpticalComputationProvider { - - @Override - public int requestCWUt(int cwut, boolean simulate, @NotNull Collection seen) { - return 0; - } - - @Override - public int getMaxCWUt(@NotNull Collection seen) { - return 0; - } - - @Override - public boolean canBridge(@NotNull Collection seen) { - return false; - } - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/cover/ComputerMonitorCover.java b/src/main/java/com/gregtechceu/gtceu/common/cover/ComputerMonitorCover.java index 86b0837504..02a8040630 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/cover/ComputerMonitorCover.java +++ b/src/main/java/com/gregtechceu/gtceu/common/cover/ComputerMonitorCover.java @@ -3,6 +3,7 @@ import com.gregtechceu.gtceu.api.capability.ICoverable; import com.gregtechceu.gtceu.api.cover.CoverBehavior; import com.gregtechceu.gtceu.api.cover.CoverDefinition; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRenderer; import net.minecraft.core.Direction; @@ -18,4 +19,9 @@ public boolean canPipePassThrough() { } // No implementation here, this cover is just for decorative purposes + + @Override + protected CoverRenderer buildRenderer() { + return null; + } } diff --git a/src/main/java/com/gregtechceu/gtceu/common/cover/ConveyorCover.java b/src/main/java/com/gregtechceu/gtceu/common/cover/ConveyorCover.java index a96cdac30b..3c354ffb2d 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/cover/ConveyorCover.java +++ b/src/main/java/com/gregtechceu/gtceu/common/cover/ConveyorCover.java @@ -1,25 +1,43 @@ package com.gregtechceu.gtceu.common.cover; +import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.GTValues; import com.gregtechceu.gtceu.api.capability.IControllable; import com.gregtechceu.gtceu.api.capability.ICoverable; +import com.gregtechceu.gtceu.api.capability.forge.GTCapability; import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.cover.CoverBehavior; import com.gregtechceu.gtceu.api.cover.CoverDefinition; import com.gregtechceu.gtceu.api.cover.IUICover; +import com.gregtechceu.gtceu.api.cover.filter.CoverWithItemFilter; import com.gregtechceu.gtceu.api.cover.filter.FilterHandler; import com.gregtechceu.gtceu.api.cover.filter.FilterHandlers; import com.gregtechceu.gtceu.api.cover.filter.ItemFilter; +import com.gregtechceu.gtceu.api.graphnet.GraphNetUtility; +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.NodeExposingCapabilities; +import com.gregtechceu.gtceu.api.graphnet.predicate.test.ItemTestObject; +import com.gregtechceu.gtceu.api.graphnet.traverse.EdgeDirection; +import com.gregtechceu.gtceu.api.graphnet.traverse.EdgeSelector; +import com.gregtechceu.gtceu.api.graphnet.traverse.NetClosestIterator; +import com.gregtechceu.gtceu.api.graphnet.traverse.ResilientNetClosestIterator; import com.gregtechceu.gtceu.api.gui.GuiTextures; import com.gregtechceu.gtceu.api.gui.widget.EnumSelectorWidget; import com.gregtechceu.gtceu.api.gui.widget.IntInputWidget; import com.gregtechceu.gtceu.api.machine.ConditionalSubscriptionHandler; import com.gregtechceu.gtceu.api.transfer.item.ItemHandlerDelegate; -import com.gregtechceu.gtceu.common.blockentity.ItemPipeBlockEntity; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRenderer; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRendererBuilder; import com.gregtechceu.gtceu.common.cover.data.DistributionMode; +import com.gregtechceu.gtceu.common.cover.data.FilterMode; import com.gregtechceu.gtceu.common.cover.data.ManualIOMode; +import com.gregtechceu.gtceu.common.cover.filter.MatchResult; +import com.gregtechceu.gtceu.common.cover.filter.MergabilityInfo; +import com.gregtechceu.gtceu.common.pipelike.net.item.*; import com.gregtechceu.gtceu.utils.GTTransferUtils; +import com.gregtechceu.gtceu.utils.GTUtil; import com.gregtechceu.gtceu.utils.ItemStackHashStrategy; +import com.gregtechceu.gtceu.utils.function.BiIntConsumer; import com.lowdragmc.lowdraglib.gui.texture.GuiTextureGroup; import com.lowdragmc.lowdraglib.gui.widget.LabelWidget; @@ -36,23 +54,34 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.network.chat.Component; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.ForgeCapabilities; +import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.items.IItemHandler; -import net.minecraftforge.items.IItemHandlerModifiable; -import net.minecraftforge.items.ItemHandlerHelper; +import it.unimi.dsi.fastutil.ints.Int2IntArrayMap; import it.unimi.dsi.fastutil.ints.Int2IntFunction; +import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap; -import lombok.AllArgsConstructor; +import it.unimi.dsi.fastutil.objects.*; import lombok.Getter; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.function.IntUnaryOperator; +import java.util.function.Predicate; import javax.annotation.ParametersAreNonnullByDefault; @@ -63,7 +92,7 @@ */ @ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault -public class ConveyorCover extends CoverBehavior implements IUICover, IControllable { +public class ConveyorCover extends CoverBehavior implements IUICover, IControllable, CoverWithItemFilter { public static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder(ConveyorCover.class, CoverBehavior.MANAGED_FIELD_HOLDER); @@ -93,8 +122,14 @@ public class ConveyorCover extends CoverBehavior implements IUICover, IControlla @Getter protected boolean isWorkingEnabled = true; protected int itemsLeftToTransferLastSecond; + private CoverableItemHandlerWrapper itemHandlerWrapper; private Widget ioModeSwitch; + protected final ObjectLinkedOpenHashSet extractionRoundRobinCache = new ObjectLinkedOpenHashSet<>(); + protected final ObjectLinkedOpenHashSet insertionRoundRobinCache = new ObjectLinkedOpenHashSet<>(); + + protected @Nullable CoverRenderer rendererInverted; + @Persisted @DescSynced @Getter @@ -109,7 +144,7 @@ public ConveyorCover(CoverDefinition definition, ICoverable coverHolder, Directi this.transferRate = maxItemTransferRate; this.itemsLeftToTransferLastSecond = transferRate; this.io = IO.OUT; - this.distributionMode = DistributionMode.INSERT_FIRST; + this.distributionMode = DistributionMode.FLOOD; subscriptionHandler = new ConditionalSubscriptionHandler(coverHolder, this::update, this::isSubscriptionActive); filterHandler = FilterHandlers.item(this) @@ -126,7 +161,7 @@ protected boolean isSubscriptionActive() { return isWorkingEnabled() && getAdjacentItemHandler() != null; } - protected @Nullable IItemHandlerModifiable getOwnItemHandler() { + protected @Nullable IItemHandler getOwnItemHandler() { return coverHolder.getItemHandlerCap(attachedSide, false); } @@ -143,11 +178,6 @@ public ManagedFieldHolder getFieldHolder() { return MANAGED_FIELD_HOLDER; } - @Override - public boolean canAttach() { - return getOwnItemHandler() != null; - } - public void setTransferRate(int transferRate) { if (transferRate <= maxItemTransferRate) { this.transferRate = transferRate; @@ -164,6 +194,10 @@ public void setIo(IO io) { public void setDistributionMode(DistributionMode distributionMode) { this.distributionMode = distributionMode; + this.extractionRoundRobinCache.clear(); + this.extractionRoundRobinCache.trim(16); + this.insertionRoundRobinCache.clear(); + this.insertionRoundRobinCache.trim(16); coverHolder.markDirty(); } @@ -172,6 +206,11 @@ protected void setManualIOMode(ManualIOMode manualIOMode) { coverHolder.markDirty(); } + @Override + public FilterMode getFilterMode() { + return FilterMode.FILTER_BOTH; + } + @Override public void onLoad() { super.onLoad(); @@ -212,299 +251,637 @@ public void setWorkingEnabled(boolean isWorkingAllowed) { protected void update() { long timer = coverHolder.getOffsetTimer(); - if (timer % 5 == 0) { - if (itemsLeftToTransferLastSecond > 0) { - var adjacent = getAdjacentItemHandler(); - var self = getOwnItemHandler(); - - if (adjacent != null && self != null) { - int totalTransferred = switch (io) { - case IN -> doTransferItems(adjacent, self, itemsLeftToTransferLastSecond); - case OUT -> doTransferItems(self, adjacent, itemsLeftToTransferLastSecond); - default -> 0; - }; - this.itemsLeftToTransferLastSecond -= totalTransferred; + if (timer % 5 == 0 && isWorkingEnabled && getItemsLeftToTransfer() > 0) { + Direction side = attachedSide; + BlockEntity tileEntity = coverHolder.getNeighbor(side); + IItemHandler itemHandler = tileEntity == null ? null : + tileEntity.getCapability(ForgeCapabilities.ITEM_HANDLER, side.getOpposite()).resolve().orElse(null); + IItemHandler myItemHandler = coverHolder.getCapability(ForgeCapabilities.ITEM_HANDLER, side).resolve() + .orElse(null); + if (itemHandler != null && myItemHandler != null) { + if (io == IO.OUT) { + performTransferOnUpdate(myItemHandler, itemHandler); + } else { + performTransferOnUpdate(itemHandler, myItemHandler); } } - if (timer % 20 == 0) { - this.itemsLeftToTransferLastSecond = transferRate; - } - subscriptionHandler.updateSubscription(); + } + if (timer % 20 == 0) { + refreshBuffer(transferRate); } } - protected int doTransferItems(IItemHandler sourceInventory, IItemHandler targetInventory, int maxTransferAmount) { - return moveInventoryItems(sourceInventory, targetInventory, maxTransferAmount); + protected int getItemsLeftToTransfer() { + return itemsLeftToTransferLastSecond; } - protected int moveInventoryItems(IItemHandler sourceInventory, IItemHandler targetInventory, - int maxTransferAmount) { - ItemFilter filter = filterHandler.getFilter(); - int itemsLeftToTransfer = maxTransferAmount; + protected void reportItemsTransfer(int transferred) { + this.itemsLeftToTransferLastSecond -= transferred; + } - for (int srcIndex = 0; srcIndex < sourceInventory.getSlots(); srcIndex++) { - ItemStack sourceStack = sourceInventory.extractItem(srcIndex, itemsLeftToTransfer, true); - if (sourceStack.isEmpty()) { - continue; - } + protected void refreshBuffer(int transferRate) { + this.itemsLeftToTransferLastSecond = transferRate; + } - if (!filter.test(sourceStack)) { - continue; + protected void performTransferOnUpdate(@NotNull IItemHandler sourceHandler, @NotNull IItemHandler destHandler) { + reportItemsTransfer(performTransfer(sourceHandler, destHandler, false, i -> 0, + i -> getItemsLeftToTransfer(), null)); + } + + /** + * Performs transfer + * + * @param sourceHandler the handler to pull from + * @param destHandler the handler to push to + * @param byFilterSlot whether to perform the transfer by filter slot. + * @param minTransfer the minimum allowed transfer amount, when given a filter slot. If no filter exists or not + * transferring by slot, a filter slot of -1 will be passed in. + * @param maxTransfer the maximum allowed transfer amount, when given a filter slot. If no filter exists or not + * transferring by slot, a filter slot of -1 will be passed in. + * @param transferReport where transfer is reported; a is the filter slot, b is the amount of transfer. + * Each filter slot will report its transfer before the next slot is calculated. + * @return how much was transferred in total. + */ + protected int performTransfer(@NotNull IItemHandler sourceHandler, @NotNull IItemHandler destHandler, + boolean byFilterSlot, @NotNull IntUnaryOperator minTransfer, + @NotNull IntUnaryOperator maxTransfer, @Nullable BiIntConsumer transferReport) { + var filter = this.getFilterHandler(); + byFilterSlot = byFilterSlot && filter.isFilterPresent(); // can't be by filter slot if there is no filter + Int2IntArrayMap containedByFilterSlot = new Int2IntArrayMap(); + Int2ObjectArrayMap> filterSlotToMergability = new Int2ObjectArrayMap<>(); + for (int i = 0; i < sourceHandler.getSlots(); i++) { + ItemStack stack = sourceHandler.getStackInSlot(i); + int extracted = stack.getCount(); + if (extracted == 0) continue; + MatchResult match = null; + if (!filter.isFilterPresent() || (match = filter.getFilter().match(stack)).isMatched()) { + int filterSlot = -1; + if (byFilterSlot) { + filterSlot = match.getFilterIndex(); + } + containedByFilterSlot.merge(filterSlot, extracted, Integer::sum); + final int handlerSlot = i; + filterSlotToMergability.compute(filterSlot, (k, v) -> { + if (v == null) v = new MergabilityInfo<>(); + v.add(handlerSlot, new ItemTestObject(stack), extracted); + return v; + }); } - - ItemStack remainder = ItemHandlerHelper.insertItem(targetInventory, sourceStack, true); - int amountToInsert = sourceStack.getCount() - remainder.getCount(); - - if (amountToInsert > 0) { - sourceStack = sourceInventory.extractItem(srcIndex, amountToInsert, false); - if (!sourceStack.isEmpty()) { - ItemHandlerHelper.insertItem(targetInventory, sourceStack, false); - itemsLeftToTransfer -= sourceStack.getCount(); - - if (itemsLeftToTransfer == 0) { - break; + } + var iter = containedByFilterSlot.int2IntEntrySet().fastIterator(); + int totalTransfer = 0; + while (iter.hasNext()) { + var next = iter.next(); + int filterSlot = next.getIntKey(); + int min = Math.max(minTransfer.applyAsInt(filterSlot), 1); + int max = maxTransfer.applyAsInt(filterSlot); + if (max < min) continue; + int slotTransfer = 0; + if (next.getIntValue() >= min) { + MergabilityInfo mergabilityInfo = filterSlotToMergability.get(filterSlot); + MergabilityInfo.Merge merge = mergabilityInfo.getLargestMerge(); + // since we can't guarantee the transferability of multiple stack types while just simulating, + // if the largest merge is not large enough we have to give up. + if (merge.getCount() >= min) { + int transfer = Math.min(merge.getCount(), max); + transfer = doInsert(destHandler, merge.getTestObject(), transfer, true); + if (transfer < min) continue; + transfer = doExtract(sourceHandler, merge.getTestObject(), transfer, true); + if (transfer < min) continue; + doExtract(sourceHandler, merge.getTestObject(), transfer, false); + doInsert(destHandler, merge.getTestObject(), transfer, false); + int remaining = max - transfer; + slotTransfer += transfer; + if (remaining <= 0) continue; + for (MergabilityInfo.Merge otherMerge : mergabilityInfo + .getNonLargestMerges(merge)) { + transfer = Math.min(otherMerge.getCount(), remaining); + transfer = doInsert(destHandler, otherMerge.getTestObject(), transfer, true); + if (transfer < min) continue; + transfer = doExtract(sourceHandler, otherMerge.getTestObject(), transfer, true); + if (transfer < min) continue; + doExtract(sourceHandler, otherMerge.getTestObject(), transfer, false); + doInsert(destHandler, otherMerge.getTestObject(), transfer, false); + remaining -= transfer; + slotTransfer += transfer; + if (remaining <= 0) break; } } } + if (transferReport != null) transferReport.accept(filterSlot, slotTransfer); + totalTransfer += slotTransfer; } - return maxTransferAmount - itemsLeftToTransfer; - } - - protected static boolean moveInventoryItemsExact(IItemHandler sourceInventory, IItemHandler targetInventory, - TypeItemInfo itemInfo) { - // first, compute how much can we extract in reality from the machine, - // because totalCount is based on what getStackInSlot returns, which may differ from what - // extractItem() will return - ItemStack resultStack = itemInfo.itemStack.copy(); - int totalExtractedCount = 0; - int itemsLeftToExtract = itemInfo.totalCount; - - for (int i = 0; i < itemInfo.slots.size(); i++) { - int slotIndex = itemInfo.slots.getInt(i); - ItemStack extractedStack = sourceInventory.extractItem(slotIndex, itemsLeftToExtract, true); - if (!extractedStack.isEmpty() && - ItemStack.isSameItemSameTags(resultStack, extractedStack)) { - totalExtractedCount += extractedStack.getCount(); - itemsLeftToExtract -= extractedStack.getCount(); - } - if (itemsLeftToExtract == 0) { - break; + return totalTransfer; + } + + protected ObjectLinkedOpenHashSet getRoundRobinCache(boolean extract, boolean simulate) { + ObjectLinkedOpenHashSet set = extract ? extractionRoundRobinCache : insertionRoundRobinCache; + return simulate ? set.clone() : set; + } + + protected int doExtract(@NotNull IItemHandler handler, ItemTestObject testObject, int count, boolean simulate) { + ItemCapabilityObject cap; + if (distributionMode == DistributionMode.FLOOD || (cap = ItemCapabilityObject.instanceOf(handler)) == null) + return simpleExtract(handler, testObject, count, simulate); + NetNode origin = cap.getNode(); + Predicate filter = GraphNetUtility.standardEdgeBlacklist(testObject); + // if you find yourself here because you added a new distribution mode and now it won't compile, + // good luck. + return switch (distributionMode) { + case ROUND_ROBIN -> { + ItemNetworkView view = cap.getNetworkView(); + Iterator iter = view.handler().getBackingHandlers().iterator(); + ObjectLinkedOpenHashSet cache = getRoundRobinCache(true, simulate); + Set backlog = new ObjectOpenHashSet<>(); + Object2IntOpenHashMap flows = new Object2IntOpenHashMap<>(); + int available = count; + while (available > 0) { + if (!cache.isEmpty() && backlog.remove(cache.first())) { + IItemHandler candidate = cache.first(); + NetNode linked = view.handlerNetNodeBiMap().get(candidate); + if (linked == null) { + cache.removeFirst(); + continue; + } else { + cache.addAndMoveToLast(candidate); + } + available = rrExtract(testObject, simulate, origin, filter, flows, available, candidate, + linked); + continue; + } + if (iter.hasNext()) { + IItemHandler candidate = iter.next(); + boolean frontOfCache = !cache.isEmpty() && cache.first() == candidate; + if (frontOfCache || !cache.contains(candidate)) { + NetNode linked = view.handlerNetNodeBiMap().get(candidate); + if (linked == null) { + if (frontOfCache) cache.removeFirst(); + continue; + } else { + cache.addAndMoveToLast(candidate); + } + available = rrExtract(testObject, simulate, origin, filter, flows, available, candidate, + linked); + } else { + backlog.add(candidate); + } + } else if (backlog.isEmpty()) { + // we have finished the iterator and backlog + break; + } else { + if (!cache.isEmpty()) { + if (view.handler().getBackingHandlers().contains(cache.first())) + break; // we've already visited the next node in the cache + else { + // the network view does not contain the node in the front of the cache, so yeet it. + cache.removeFirst(); + } + } else { + break; // cache is empty and iterator is empty, something is weird, just exit. + } + } + } + while (iter.hasNext()) { + cache.add(iter.next()); + } + if (!simulate) { + for (var entry : flows.object2IntEntrySet()) { + ItemCapabilityObject.reportFlow(entry.getKey(), entry.getIntValue(), testObject); + } + } + yield count - available; } - } - // if amount of items extracted is not equal to the amount of items we - // wanted to extract, abort item extraction - if (totalExtractedCount != itemInfo.totalCount) { - return false; - } - // adjust size of the result stack accordingly - resultStack.setCount(totalExtractedCount); - - // now, see how much we can insert into destination inventory - // if we can't insert as much as itemInfo requires, and remainder is empty, abort, abort - ItemStack remainder = ItemHandlerHelper.insertItem(targetInventory, resultStack, true); - if (!remainder.isEmpty()) { - return false; - } - - // otherwise, perform real insertion and then remove items from the source inventory - ItemHandlerHelper.insertItem(targetInventory, resultStack, false); - - // perform real extraction of the items from the source inventory now - itemsLeftToExtract = itemInfo.totalCount; - for (int i = 0; i < itemInfo.slots.size(); i++) { - int slotIndex = itemInfo.slots.getInt(i); - ItemStack extractedStack = sourceInventory.extractItem(slotIndex, itemsLeftToExtract, false); - if (!extractedStack.isEmpty() && - ItemStack.isSameItemSameTags(resultStack, extractedStack)) { - itemsLeftToExtract -= extractedStack.getCount(); + case EQUALIZED -> { + NetClosestIterator gather = new NetClosestIterator(origin, + EdgeSelector.filtered(EdgeDirection.INCOMING, filter)); + Object2ObjectOpenHashMap candidates = new Object2ObjectOpenHashMap<>(); + while (gather.hasNext()) { + NetNode node = gather.next(); + if (node instanceof NodeExposingCapabilities exposer) { + IItemHandler h = exposer.getProvider().getCapability( + ForgeCapabilities.ITEM_HANDLER, exposer.exposedFacing()) + .resolve().orElse(null); + if (h != null && ItemCapabilityObject.instanceOf(h) == null) { + candidates.put(node, h); + } + } + } + int largestMin = count / candidates.size(); + if (largestMin <= 0) yield 0; + for (IItemHandler value : candidates.values()) { + largestMin = Math.min(largestMin, simpleExtract(value, testObject, largestMin, true)); + if (largestMin <= 0) yield 0; + } + // binary search for largest scale that doesn't exceed flow limits + Int2ObjectArrayMap> flows = new Int2ObjectArrayMap<>(); + largestMin = GTUtil.binarySearchInt(0, largestMin, l -> { + if (flows.containsKey(l) && flows.get(l) == null) return false; + ResilientNetClosestIterator forwardFrontier = new ResilientNetClosestIterator(origin, + EdgeSelector.filtered(EdgeDirection.INCOMING, filter)); + Object2IntOpenHashMap localFlows = new Object2IntOpenHashMap<>(); + for (NetNode node : candidates.keySet()) { + ResilientNetClosestIterator backwardFrontier = new ResilientNetClosestIterator(node, + EdgeSelector.filtered(EdgeDirection.OUTGOING, filter)); + if (GraphNetUtility.p2pWalk(simulate, l, + n -> ItemCapabilityObject.getFlowLimit(n, testObject) - localFlows.getInt(n), + (n, i) -> localFlows.put(n, localFlows.getInt(n) + i), + forwardFrontier, backwardFrontier) < l) + return false; + } + flows.put(l, localFlows); + return true; + }, false); + if (largestMin <= 0 || flows.get(largestMin) == null) yield 0; + if (!simulate) { + for (IItemHandler value : candidates.values()) { + simpleExtract(value, testObject, largestMin, false); + } + for (var e : flows.get(largestMin).object2IntEntrySet()) { + ItemCapabilityObject.reportFlow(e.getKey(), e.getIntValue(), testObject); + } + } + yield largestMin * candidates.size(); } - if (itemsLeftToExtract == 0) { - break; + case FLOOD -> 0; // how are you here? + }; + } + + protected int rrExtract(ItemTestObject testObject, boolean simulate, NetNode origin, Predicate filter, + Object2IntOpenHashMap flows, int available, IItemHandler candidate, + NetNode linked) { + int accepted = simpleExtract(candidate, testObject, available, true); + if (accepted > 0) { + ResilientNetClosestIterator forwardFrontier = new ResilientNetClosestIterator(origin, + EdgeSelector.filtered(EdgeDirection.INCOMING, filter)); + ResilientNetClosestIterator backwardFrontier = new ResilientNetClosestIterator(linked, + EdgeSelector.filtered(EdgeDirection.OUTGOING, filter)); + accepted = GraphNetUtility.p2pWalk(simulate, accepted, + n -> ItemCapabilityObject.getFlowLimit(n, testObject) - flows.getInt(n), + (n, i) -> flows.put(n, flows.getInt(n) + i), + forwardFrontier, backwardFrontier); + if (accepted > 0) { + available -= accepted; + if (!simulate) simpleExtract(candidate, testObject, accepted, false); } } - return true; + return available; } - protected int moveInventoryItems(IItemHandler sourceInventory, IItemHandler targetInventory, - Map itemInfos, int maxTransferAmount) { - ItemFilter filter = filterHandler.getFilter(); - int itemsLeftToTransfer = maxTransferAmount; - - for (int i = 0; i < sourceInventory.getSlots(); i++) { - ItemStack itemStack = sourceInventory.getStackInSlot(i); - if (itemStack.isEmpty() || !filter.test(itemStack) || !itemInfos.containsKey(itemStack)) { - continue; + protected int simpleExtract(@NotNull IItemHandler handler, ItemTestObject testObject, int count, + boolean simulate) { + int available = 0; + for (int i = 0; i < handler.getSlots(); i++) { + ItemStack slot = handler.getStackInSlot(i); + if (testObject.test(slot)) { + available += handler.extractItem(i, count - available, simulate).getCount(); + if (available == count) return count; } - - GroupItemInfo itemInfo = itemInfos.get(itemStack); - - ItemStack extractedStack = sourceInventory.extractItem(i, - Math.min(itemInfo.totalCount, itemsLeftToTransfer), true); - - ItemStack remainderStack = ItemHandlerHelper.insertItemStacked(targetInventory, extractedStack, true); - int amountToInsert = extractedStack.getCount() - remainderStack.getCount(); - - if (amountToInsert > 0) { - extractedStack = sourceInventory.extractItem(i, amountToInsert, false); - - if (!extractedStack.isEmpty()) { - - ItemHandlerHelper.insertItemStacked(targetInventory, extractedStack, false); - itemsLeftToTransfer -= extractedStack.getCount(); - itemInfo.totalCount -= extractedStack.getCount(); - - if (itemInfo.totalCount == 0) { - itemInfos.remove(itemStack); - if (itemInfos.isEmpty()) { - break; + } + return available; + } + + protected int doInsert(@NotNull IItemHandler handler, ItemTestObject testObject, final int count, + boolean simulate) { + ItemCapabilityObject cap; + if (distributionMode == DistributionMode.FLOOD || (cap = ItemCapabilityObject.instanceOf(handler)) == null) + return simpleInsert(handler, testObject, count, simulate); + NetNode origin = cap.getNode(); + Predicate filter = GraphNetUtility.standardEdgeBlacklist(testObject); + // if you find yourself here because you added a new distribution mode and now it won't compile, + // good luck. + return switch (distributionMode) { + case ROUND_ROBIN -> { + ItemNetworkView view = cap.getNetworkView(); + Iterator iter = view.handler().getBackingHandlers().iterator(); + ObjectLinkedOpenHashSet cache = getRoundRobinCache(false, simulate); + Set backlog = new ObjectOpenHashSet<>(); + Object2IntOpenHashMap flows = new Object2IntOpenHashMap<>(); + int available = count; + while (available > 0) { + if (!cache.isEmpty() && backlog.remove(cache.first())) { + IItemHandler candidate = cache.first(); + NetNode linked = view.handlerNetNodeBiMap().get(candidate); + if (linked == null) { + cache.removeFirst(); + continue; + } else { + cache.addAndMoveToLast(candidate); } + available = rrInsert(testObject, simulate, origin, filter, flows, available, candidate, linked); + continue; } - if (itemsLeftToTransfer == 0) { + if (iter.hasNext()) { + IItemHandler candidate = iter.next(); + boolean frontOfCache = !cache.isEmpty() && cache.first() == candidate; + if (frontOfCache || !cache.contains(candidate)) { + NetNode linked = view.handlerNetNodeBiMap().get(candidate); + if (linked == null) { + if (frontOfCache) cache.removeFirst(); + continue; + } else { + cache.addAndMoveToLast(candidate); + } + available = rrInsert(testObject, simulate, origin, filter, flows, available, candidate, + linked); + } else { + backlog.add(candidate); + } + } else if (backlog.isEmpty()) { + // we have finished the iterator and backlog break; + } else { + if (!cache.isEmpty()) { + if (view.handler().getBackingHandlers().contains(cache.first())) + break; // we've already visited the next node in the cache + else { + // the network view does not contain the node in the front of the cache, so yeet it. + cache.removeFirst(); + } + } else { + break; // cache is empty and iterator is empty, something is weird, just exit. + } + } + } + while (iter.hasNext()) { + cache.add(iter.next()); + } + if (!simulate) { + for (var entry : flows.object2IntEntrySet()) { + ItemCapabilityObject.reportFlow(entry.getKey(), entry.getIntValue(), testObject); + } + } + yield count - available; + } + case EQUALIZED -> { + NetClosestIterator gather = new NetClosestIterator(origin, + EdgeSelector.filtered(EdgeDirection.OUTGOING, filter)); + Object2ObjectOpenHashMap candidates = new Object2ObjectOpenHashMap<>(); + while (gather.hasNext()) { + NetNode node = gather.next(); + if (node instanceof NodeExposingCapabilities exposer) { + IItemHandler h = exposer.getProvider().getCapability( + ForgeCapabilities.ITEM_HANDLER, exposer.exposedFacing()) + .resolve().orElse(null); + if (h != null && ItemCapabilityObject.instanceOf(h) == null) { + candidates.put(node, h); + } } } + int largestMin = count / candidates.size(); + if (largestMin <= 0) yield 0; + for (IItemHandler value : candidates.values()) { + largestMin = Math.min(largestMin, simpleInsert(value, testObject, largestMin, true)); + if (largestMin <= 0) yield 0; + } + // binary search for largest scale that doesn't exceed flow limits + Int2ObjectArrayMap> flows = new Int2ObjectArrayMap<>(); + largestMin = GTUtil.binarySearchInt(0, largestMin, l -> { + if (flows.containsKey(l) && flows.get(l) == null) return false; + ResilientNetClosestIterator forwardFrontier = new ResilientNetClosestIterator(origin, + EdgeSelector.filtered(EdgeDirection.OUTGOING, filter)); + Object2IntOpenHashMap localFlows = new Object2IntOpenHashMap<>(); + for (NetNode node : candidates.keySet()) { + ResilientNetClosestIterator backwardFrontier = new ResilientNetClosestIterator(node, + EdgeSelector.filtered(EdgeDirection.INCOMING, filter)); + if (GraphNetUtility.p2pWalk(simulate, l, + n -> ItemCapabilityObject.getFlowLimit(n, testObject) - localFlows.getInt(n), + (n, i) -> localFlows.put(n, localFlows.getInt(n) + i), + forwardFrontier, backwardFrontier) < l) + return false; + } + flows.put(l, localFlows); + return true; + }, false); + if (largestMin <= 0 || flows.get(largestMin) == null) yield 0; + if (!simulate) { + for (IItemHandler value : candidates.values()) { + simpleInsert(value, testObject, largestMin, false); + } + for (var e : flows.get(largestMin).object2IntEntrySet()) { + ItemCapabilityObject.reportFlow(e.getKey(), e.getIntValue(), testObject); + } + } + yield largestMin * candidates.size(); + } + case FLOOD -> 0; // how are you here? + }; + } + + protected int rrInsert(ItemTestObject testObject, boolean simulate, NetNode origin, Predicate filter, + Object2IntOpenHashMap flows, int available, IItemHandler candidate, + NetNode linked) { + int accepted = simpleInsert(candidate, testObject, available, true); + if (accepted > 0) { + ResilientNetClosestIterator forwardFrontier = new ResilientNetClosestIterator(origin, + EdgeSelector.filtered(EdgeDirection.OUTGOING, filter)); + ResilientNetClosestIterator backwardFrontier = new ResilientNetClosestIterator(linked, + EdgeSelector.filtered(EdgeDirection.INCOMING, filter)); + accepted = GraphNetUtility.p2pWalk(simulate, accepted, + n -> ItemCapabilityObject.getFlowLimit(n, testObject) - flows.getInt(n), + (n, i) -> flows.put(n, flows.getInt(n) + i), + forwardFrontier, backwardFrontier); + if (accepted > 0) { + available -= accepted; + if (!simulate) simpleInsert(candidate, testObject, accepted, false); } } - return maxTransferAmount - itemsLeftToTransfer; + return available; } - @NotNull - protected Map countInventoryItemsByType(@NotNull IItemHandler inventory) { - ItemFilter filter = filterHandler.getFilter(); - Map result = new Object2ObjectOpenCustomHashMap<>( - ItemStackHashStrategy.comparingAllButCount()); + protected int simpleInsert(@NotNull IItemHandler handler, ItemTestObject testObject, int count, + boolean simulate) { + int available = count; + for (int i = 0; i < handler.getSlots(); i++) { + ItemStack toInsert = testObject.recombine(Math.min(available, handler.getSlotLimit(i))); + available -= toInsert.getCount() - handler.insertItem(i, toInsert, simulate).getCount(); + if (available <= 0) return count; + } + return count - available; + } - for (int srcIndex = 0; srcIndex < inventory.getSlots(); srcIndex++) { - ItemStack itemStack = inventory.getStackInSlot(srcIndex); - if (itemStack.isEmpty() || !filter.test(itemStack)) { - continue; - } + protected static class TypeItemInfo { - var itemInfo = result.computeIfAbsent(itemStack, s -> new TypeItemInfo(s, new IntArrayList(), 0)); + public final ItemStack itemStack; + public final int filterSlot; + public final IntList slots; + public int totalCount; - itemInfo.totalCount += itemStack.getCount(); - itemInfo.slots.add(srcIndex); + public TypeItemInfo(ItemStack itemStack, int filterSlot, IntList slots, int totalCount) { + this.itemStack = itemStack; + this.filterSlot = filterSlot; + this.slots = slots; + this.totalCount = totalCount; } - - return result; } @NotNull - protected Map countInventoryItemsByMatchSlot(@NotNull IItemHandler inventory) { - ItemFilter filter = filterHandler.getFilter(); - Map result = new Object2ObjectOpenCustomHashMap<>( + protected Map countInventoryItemsByType(@NotNull IItemHandler inventory) { + Map result = new Object2ObjectOpenCustomHashMap<>( ItemStackHashStrategy.comparingAllButCount()); - for (int srcIndex = 0; srcIndex < inventory.getSlots(); srcIndex++) { ItemStack itemStack = inventory.getStackInSlot(srcIndex); - if (itemStack.isEmpty() || !filter.test(itemStack)) { + if (itemStack.isEmpty()) { continue; } - var itemInfo = result.computeIfAbsent(itemStack, s -> new GroupItemInfo(s, 0)); - - itemInfo.totalCount += itemStack.getCount(); + var matchResult = getFilterHandler().getFilter().match(itemStack); + if (!matchResult.isMatched()) continue; + + if (!result.containsKey(itemStack)) { + TypeItemInfo itemInfo = new TypeItemInfo(itemStack.copy(), matchResult.getFilterIndex(), + new IntArrayList(), 0); + itemInfo.totalCount += itemStack.getCount(); + itemInfo.slots.add(srcIndex); + result.put(itemStack.copy(), itemInfo); + } else { + TypeItemInfo itemInfo = result.get(itemStack); + itemInfo.totalCount += itemStack.getCount(); + itemInfo.slots.add(srcIndex); + } } return result; } - @AllArgsConstructor - protected static class TypeItemInfo { + @Override + public boolean canAttach(@NotNull ICoverable coverable, @NotNull Direction side) { + return coverable.getCapability(ForgeCapabilities.ITEM_HANDLER, attachedSide).isPresent(); + } - public final ItemStack itemStack; - public final IntList slots; - public int totalCount; + @Override + public InteractionResult onScrewdriverClick(Player playerIn, InteractionHand hand, BlockHitResult hitResult) { + if (!coverHolder.getLevel().isClientSide) { + createUI(playerIn); + } + return InteractionResult.SUCCESS; } - @AllArgsConstructor - protected static class GroupItemInfo { + @Override + public LazyOptional getCapability(@NotNull Capability capability, LazyOptional defaultValue) { + if (capability == ForgeCapabilities.ITEM_HANDLER) { + if (!defaultValue.isPresent()) { + return LazyOptional.empty(); + } + IItemHandler delegate = defaultValue.cast().resolve().orElse(null); + if (itemHandlerWrapper == null || itemHandlerWrapper.delegate != delegate) { + this.itemHandlerWrapper = new CoverableItemHandlerWrapper(delegate); + } + return ForgeCapabilities.ITEM_HANDLER.orEmpty(capability, LazyOptional.of(() -> itemHandlerWrapper)); + } + if (capability == GTCapability.CAPABILITY_CONTROLLABLE) { + return GTCapability.CAPABILITY_CONTROLLABLE.orEmpty(capability, LazyOptional.of(() -> this)); + } + return defaultValue; + } - public final ItemStack itemStack; - public int totalCount; + @NotNull + protected String getUITitle() { + return "cover.conveyor.title"; + } + + protected void buildAdditionalUI(WidgetGroup group) { + // Do nothing in the base implementation. This is intended to be overridden by subclasses. + } + + protected void configureFilter() { + // Do nothing in the base implementation. This is intended to be overridden by subclasses. } - ////////////////////////////////////// - // *********** GUI ***********// - ////////////////////////////////////// @Override public Widget createUIWidget() { final var group = new WidgetGroup(0, 0, 176, 137); group.addWidget(new LabelWidget(10, 5, Component.translatable(getUITitle(), GTValues.VN[tier]).getString())); - group.addWidget(new IntInputWidget(10, 20, 156, 20, () -> this.transferRate, this::setTransferRate) - .setMin(1).setMax(maxItemTransferRate)); + if (createThroughputRow()) { + group.addWidget(new IntInputWidget(10, 20, 156, 20, () -> this.transferRate, this::setTransferRate) + .setMin(1).setMax(maxItemTransferRate)); + } - ioModeSwitch = new SwitchWidget(10, 45, 20, 20, - (clickData, value) -> { - setIo(value ? IO.IN : IO.OUT); - ioModeSwitch.setHoverTooltips( + if (createConveyorModeRow()) { + ioModeSwitch = new SwitchWidget(10, 45, 20, 20, + (clickData, value) -> { + setIo(value ? IO.IN : IO.OUT); + ioModeSwitch.setHoverTooltips( + LocalizationUtils.format("cover.conveyor.mode", LocalizationUtils.format(io.tooltip))); + }) + .setTexture( + new GuiTextureGroup(GuiTextures.VANILLA_BUTTON, IO.OUT.icon), + new GuiTextureGroup(GuiTextures.VANILLA_BUTTON, IO.IN.icon)) + .setPressed(io == IO.IN) + .setHoverTooltips( LocalizationUtils.format("cover.conveyor.mode", LocalizationUtils.format(io.tooltip))); - }) - .setTexture( - new GuiTextureGroup(GuiTextures.VANILLA_BUTTON, IO.OUT.icon), - new GuiTextureGroup(GuiTextures.VANILLA_BUTTON, IO.IN.icon)) - .setPressed(io == IO.IN) - .setHoverTooltips( - LocalizationUtils.format("cover.conveyor.mode", LocalizationUtils.format(io.tooltip))); - group.addWidget(ioModeSwitch); - - if (shouldDisplayDistributionMode()) { + group.addWidget(ioModeSwitch); + } + + if (createDistributionModeRow()) { group.addWidget(new EnumSelectorWidget<>(146, 67, 20, 20, DistributionMode.VALUES, distributionMode, this::setDistributionMode)); } - group.addWidget(new EnumSelectorWidget<>(146, 107, 20, 20, - ManualIOMode.VALUES, manualIOMode, this::setManualIOMode) - .setHoverTooltips("cover.universal.manual_import_export.mode.description")); + if (createManualIOModeRow()) { + group.addWidget(new EnumSelectorWidget<>(146, 107, 20, 20, + ManualIOMode.VALUES, manualIOMode, this::setManualIOMode) + .setHoverTooltips("cover.universal.manual_import_export.mode.description")); + } - group.addWidget(filterHandler.createFilterSlotUI(125, 108)); - group.addWidget(filterHandler.createFilterConfigUI(10, 72, 156, 60)); + if (createFilterRow()) { + group.addWidget(filterHandler.createFilterSlotUI(125, 108)); + group.addWidget(filterHandler.createFilterConfigUI(10, 72, 156, 60)); + } buildAdditionalUI(group); return group; } - private boolean shouldDisplayDistributionMode() { - return coverHolder.getLevel().getBlockEntity(coverHolder.getPos()) instanceof ItemPipeBlockEntity || - coverHolder.getLevel() - .getBlockEntity(coverHolder.getPos().relative(attachedSide)) instanceof ItemPipeBlockEntity; + protected boolean createThroughputRow() { + return true; } - @NotNull - protected String getUITitle() { - return "cover.conveyor.title"; + protected boolean createFilterRow() { + return true; } - protected void buildAdditionalUI(WidgetGroup group) { - // Do nothing in the base implementation. This is intended to be overridden by subclasses. + protected boolean createManualIOModeRow() { + return true; } - protected void configureFilter() { - // Do nothing in the base implementation. This is intended to be overridden by subclasses. + protected boolean createConveyorModeRow() { + return true; } - ///////////////////////////////////// - // *** CAPABILITY OVERRIDE ***// - ///////////////////////////////////// + protected boolean createDistributionModeRow() { + return true; + } - private CoverableItemHandlerWrapper itemHandlerWrapper; + protected int getMaxStackSize() { + return 1; + } - @Nullable @Override - public IItemHandlerModifiable getItemHandlerCap(@Nullable IItemHandlerModifiable defaultValue) { - if (defaultValue == null) { - return null; + public @NotNull CoverRenderer getRenderer() { + if (io == IO.OUT) { + if (renderer == null) renderer = buildRenderer(); + return renderer; + } else { + if (rendererInverted == null) rendererInverted = buildRendererInverted(); + return rendererInverted; } - if (itemHandlerWrapper == null || itemHandlerWrapper.delegate != defaultValue) { - this.itemHandlerWrapper = new CoverableItemHandlerWrapper(defaultValue); - } - return itemHandlerWrapper; + } + + @Override + protected CoverRenderer buildRenderer() { + return new CoverRendererBuilder(GTCEu.id("block/cover/overlay_conveyor"), + GTCEu.id("block/cover/overlay_conveyor_emissive")).build(); + } + + protected CoverRenderer buildRendererInverted() { + return new CoverRendererBuilder(GTCEu.id("block/cover/overlay_conveyor_inverted"), + GTCEu.id("block/cover/overlay_conveyor_inverted_emissive")).build(); } private class CoverableItemHandlerWrapper extends ItemHandlerDelegate { - public CoverableItemHandlerWrapper(IItemHandlerModifiable delegate) { + public CoverableItemHandlerWrapper(IItemHandler delegate) { super(delegate); } diff --git a/src/main/java/com/gregtechceu/gtceu/common/cover/CoverSolarPanel.java b/src/main/java/com/gregtechceu/gtceu/common/cover/CoverSolarPanel.java index 62c622a8e9..3467cad2b5 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/cover/CoverSolarPanel.java +++ b/src/main/java/com/gregtechceu/gtceu/common/cover/CoverSolarPanel.java @@ -1,18 +1,23 @@ package com.gregtechceu.gtceu.common.cover; +import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.GTValues; import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; import com.gregtechceu.gtceu.api.capability.ICoverable; import com.gregtechceu.gtceu.api.capability.IEnergyContainer; +import com.gregtechceu.gtceu.api.capability.forge.GTCapability; import com.gregtechceu.gtceu.api.cover.CoverBehavior; import com.gregtechceu.gtceu.api.cover.CoverDefinition; import com.gregtechceu.gtceu.api.machine.TickableSubscription; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRenderer; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRendererBuilder; import com.gregtechceu.gtceu.utils.GTUtil; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.level.Level; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class CoverSolarPanel extends CoverBehavior { @@ -40,8 +45,13 @@ public void onRemoved() { } @Override - public boolean canAttach() { - return attachedSide == Direction.UP && getEnergyContainer() != null; + protected CoverRenderer buildRenderer() { + return new CoverRendererBuilder(GTCEu.id("block/cover/overlay_solar_panel"), null).build(); + } + + @Override + public boolean canAttach(@NotNull ICoverable coverable, @NotNull Direction side) { + return side == Direction.UP && coverable.getCapability(GTCapability.CAPABILITY_ENERGY_CONTAINER).isPresent(); } protected void update() { @@ -50,7 +60,7 @@ protected void update() { if (GTUtil.canSeeSunClearly(level, blockPos)) { IEnergyContainer energyContainer = getEnergyContainer(); if (energyContainer != null) { - energyContainer.acceptEnergyFromNetwork(null, EUt, 1); + energyContainer.acceptEnergyFromNetwork(null, EUt, 1, false); } } } diff --git a/src/main/java/com/gregtechceu/gtceu/common/cover/FacadeCover.java b/src/main/java/com/gregtechceu/gtceu/common/cover/FacadeCover.java index db5a45e0fb..fe0fd44e0f 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/cover/FacadeCover.java +++ b/src/main/java/com/gregtechceu/gtceu/common/cover/FacadeCover.java @@ -3,6 +3,8 @@ import com.gregtechceu.gtceu.api.capability.ICoverable; import com.gregtechceu.gtceu.api.cover.CoverBehavior; import com.gregtechceu.gtceu.api.cover.CoverDefinition; +import com.gregtechceu.gtceu.client.renderer.cover.FacadeCoverRenderer; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRenderer; import com.gregtechceu.gtceu.common.item.FacadeItemBehaviour; import com.lowdragmc.lowdraglib.syncdata.annotation.DescSynced; @@ -68,6 +70,16 @@ public boolean canPipePassThrough() { return false; } + @Override + public boolean forcePipeRenderConnection() { + return false; + } + + @Override + protected CoverRenderer buildRenderer() { + return FacadeCoverRenderer.createRenderer(coverHolder.getLevel(), coverHolder.getPos(), facadeState); + } + @Nullable public BlockState getAppearance(BlockState sourceState, BlockPos sourcePos) { return facadeState; diff --git a/src/main/java/com/gregtechceu/gtceu/common/cover/FluidFilterCover.java b/src/main/java/com/gregtechceu/gtceu/common/cover/FluidFilterCover.java index 8dd355d749..28a7b9891b 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/cover/FluidFilterCover.java +++ b/src/main/java/com/gregtechceu/gtceu/common/cover/FluidFilterCover.java @@ -1,13 +1,19 @@ package com.gregtechceu.gtceu.common.cover; +import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.capability.ICoverable; import com.gregtechceu.gtceu.api.cover.CoverBehavior; import com.gregtechceu.gtceu.api.cover.CoverDefinition; import com.gregtechceu.gtceu.api.cover.IUICover; +import com.gregtechceu.gtceu.api.cover.filter.CoverWithFluidFilter; +import com.gregtechceu.gtceu.api.cover.filter.FilterHandler; +import com.gregtechceu.gtceu.api.cover.filter.FilterHandlers; import com.gregtechceu.gtceu.api.cover.filter.FluidFilter; import com.gregtechceu.gtceu.api.gui.widget.EnumSelectorWidget; import com.gregtechceu.gtceu.api.transfer.fluid.FluidHandlerDelegate; import com.gregtechceu.gtceu.api.transfer.fluid.IFluidHandlerModifiable; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRenderer; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRendererBuilder; import com.gregtechceu.gtceu.common.cover.data.FilterMode; import com.gregtechceu.gtceu.common.cover.data.ManualIOMode; @@ -20,10 +26,12 @@ import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.core.Direction; +import net.minecraftforge.common.capabilities.ForgeCapabilities; import net.minecraftforge.fluids.FluidStack; import lombok.Getter; import lombok.Setter; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.annotation.ParametersAreNonnullByDefault; @@ -35,11 +43,11 @@ */ @ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault -public class FluidFilterCover extends CoverBehavior implements IUICover { +public class FluidFilterCover extends CoverBehavior implements IUICover, CoverWithFluidFilter { public static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder(FluidFilterCover.class, CoverBehavior.MANAGED_FIELD_HOLDER); - protected FluidFilter fluidFilter; + protected FilterHandler filterHandler; @Persisted @DescSynced @Getter @@ -51,6 +59,7 @@ public class FluidFilterCover extends CoverBehavior implements IUICover { public FluidFilterCover(CoverDefinition definition, ICoverable coverHolder, Direction attachedSide) { super(definition, coverHolder, attachedSide); + filterHandler = FilterHandlers.fluid(this); } public void setFilterMode(FilterMode filterMode) { @@ -59,15 +68,25 @@ public void setFilterMode(FilterMode filterMode) { } @Override - public boolean canAttach() { - return coverHolder.getFluidHandlerCap(attachedSide, false) != null; + public boolean canAttach(@NotNull ICoverable coverable, @NotNull Direction side) { + return coverable.getCapability(ForgeCapabilities.FLUID_HANDLER).isPresent(); } - public FluidFilter getFluidFilter() { - if (fluidFilter == null) { - fluidFilter = FluidFilter.loadFilter(attachItem); + @Override + protected CoverRenderer buildRenderer() { + return new CoverRendererBuilder(GTCEu.id("block/cover/overlay_fluid_filter"), null).build(); + } + + public @NotNull FilterHandler getFilterHandler() { + if (!filterHandler.isFilterPresent()) { + filterHandler.loadFilter(attachItem); } - return fluidFilter; + return filterHandler; + } + + @Override + public ManualIOMode getManualIOMode() { + return ManualIOMode.FILTERED; } @Override @@ -90,7 +109,7 @@ public Widget createUIWidget() { group.addWidget(new EnumSelectorWidget<>(35, 25, 18, 18, FilterMode.VALUES, filterMode, this::setFilterMode)); group.addWidget(new EnumSelectorWidget<>(35, 45, 18, 18, ManualIOMode.VALUES, allowFlow, this::setAllowFlow)); - group.addWidget(getFluidFilter().openConfigurator(62, 25)); + group.addWidget(getFilterHandler().getFilter().openConfigurator(62, 25)); return group; } @@ -109,7 +128,7 @@ public FilteredFluidHandlerWrapper(IFluidHandlerModifiable delegate) { public int fill(FluidStack resource, FluidAction action) { if ((filterMode == FilterMode.FILTER_EXTRACT) && allowFlow == ManualIOMode.UNFILTERED) return super.fill(resource, action); - if (filterMode != FilterMode.FILTER_EXTRACT && getFluidFilter().test(resource)) + if (filterMode != FilterMode.FILTER_EXTRACT && getFilterHandler().test(resource)) return super.fill(resource, action); return 0; } @@ -118,7 +137,7 @@ public int fill(FluidStack resource, FluidAction action) { public FluidStack drain(FluidStack resource, FluidAction action) { if ((filterMode == FilterMode.FILTER_INSERT) && allowFlow == ManualIOMode.UNFILTERED) return super.drain(resource, action); - if (filterMode != FilterMode.FILTER_INSERT && getFluidFilter().test(resource)) + if (filterMode != FilterMode.FILTER_INSERT && getFilterHandler().test(resource)) return super.drain(resource, action); return FluidStack.EMPTY; } diff --git a/src/main/java/com/gregtechceu/gtceu/common/cover/FluidRegulatorCover.java b/src/main/java/com/gregtechceu/gtceu/common/cover/FluidRegulatorCover.java index 24bc986589..6576c54e6b 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/cover/FluidRegulatorCover.java +++ b/src/main/java/com/gregtechceu/gtceu/common/cover/FluidRegulatorCover.java @@ -2,12 +2,13 @@ import com.gregtechceu.gtceu.api.capability.ICoverable; import com.gregtechceu.gtceu.api.cover.CoverDefinition; +import com.gregtechceu.gtceu.api.cover.filter.FilterHandler; import com.gregtechceu.gtceu.api.cover.filter.FluidFilter; import com.gregtechceu.gtceu.api.cover.filter.SimpleFluidFilter; +import com.gregtechceu.gtceu.api.graphnet.predicate.test.FluidTestObject; import com.gregtechceu.gtceu.api.gui.widget.EnumSelectorWidget; import com.gregtechceu.gtceu.api.gui.widget.IntInputWidget; import com.gregtechceu.gtceu.api.gui.widget.NumberInputWidget; -import com.gregtechceu.gtceu.api.transfer.fluid.IFluidHandlerModifiable; import com.gregtechceu.gtceu.common.cover.data.BucketMode; import com.gregtechceu.gtceu.common.cover.data.TransferMode; @@ -20,12 +21,11 @@ import net.minecraft.core.Direction; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.capability.IFluidHandler; -import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction; import lombok.Getter; import org.jetbrains.annotations.NotNull; -import java.util.Map; +import java.util.function.IntUnaryOperator; import javax.annotation.ParametersAreNonnullByDefault; @@ -42,19 +42,14 @@ public class FluidRegulatorCover extends PumpCover { @DescSynced @Getter private TransferMode transferMode = TransferMode.TRANSFER_ANY; - - @Persisted - @DescSynced - @Getter - private BucketMode transferBucketMode = BucketMode.MILLI_BUCKET; + protected boolean noTransferDueToMinimum = false; @Persisted @DescSynced @Getter protected int globalTransferLimit; - protected int fluidTransferBuffered = 0; private NumberInputWidget transferSizeInput; - private EnumSelectorWidget transferBucketModeInput; + private EnumSelectorWidget bucketModeInput; public FluidRegulatorCover(CoverDefinition definition, ICoverable coverHolder, Direction attachedSide, int tier, int maxTransferRate) { @@ -75,122 +70,56 @@ public ManagedFieldHolder getFieldHolder() { ////////////////////////////////////// @Override - protected int doTransferFluidsInternal(IFluidHandlerModifiable source, IFluidHandlerModifiable destination, - int platformTransferLimit) { - return switch (transferMode) { - case TRANSFER_ANY -> transferAny(source, destination, platformTransferLimit); - case TRANSFER_EXACT -> transferExact(source, destination, platformTransferLimit); - case KEEP_EXACT -> keepExact(source, destination, platformTransferLimit); - }; - } - - private int transferExact(IFluidHandler source, IFluidHandler destination, int platformTransferLimit) { - int fluidLeftToTransfer = platformTransferLimit; - - for (int slot = 0; slot < source.getTanks(); slot++) { - if (fluidLeftToTransfer <= 0) - break; - - FluidStack sourceFluid = source.getFluidInTank(slot).copy(); - int supplyAmount = getFilteredFluidAmount(sourceFluid); - - // If the remaining transferrable amount in this operation is not enough to transfer the full stack size, - // the remaining amount for this operation will be buffered and added to the next operation's maximum. - if (fluidLeftToTransfer + fluidTransferBuffered < supplyAmount) { - this.fluidTransferBuffered += fluidLeftToTransfer; - fluidLeftToTransfer = 0; - break; + protected void refreshBuffer(int transferRate) { + if (this.transferMode == TransferMode.TRANSFER_EXACT && noTransferDueToMinimum) { + FluidFilter filter = this.getFilterHandler().getFilter(); + if (filter != FluidFilter.EMPTY) { + this.noTransferDueToMinimum = false; + this.mBLeftToTransferLastSecond += transferRate; + int max = filter.getMaxTransferSize(); + if (this.mBLeftToTransferLastSecond > max) { + this.mBLeftToTransferLastSecond = max; + } + return; } - - if (sourceFluid.isEmpty() || supplyAmount <= 0) - continue; - - sourceFluid.setAmount(supplyAmount); - FluidStack drained = source.drain(sourceFluid, FluidAction.SIMULATE); - - if (drained.isEmpty() || drained.getAmount() < supplyAmount) - continue; - - int insertableAmount = destination.fill(drained.copy(), FluidAction.SIMULATE); - if (insertableAmount <= 0) - continue; - - drained.setAmount(insertableAmount); - drained = source.drain(drained, FluidAction.EXECUTE); - - if (!drained.isEmpty()) { - destination.fill(drained, FluidAction.EXECUTE); - fluidLeftToTransfer -= (drained.getAmount() - fluidTransferBuffered); - } - - fluidTransferBuffered = 0; } - - return platformTransferLimit - fluidLeftToTransfer; + super.refreshBuffer(transferRate); } - private int keepExact(IFluidHandlerModifiable source, IFluidHandlerModifiable destination, - int platformTransferLimit) { - int fluidLeftToTransfer = platformTransferLimit; - - final Map sourceAmounts = enumerateDistinctFluids(source, TransferDirection.EXTRACT); - final Map destinationAmounts = enumerateDistinctFluids(destination, - TransferDirection.INSERT); - - for (FluidStack fluidStack : sourceAmounts.keySet()) { - if (fluidLeftToTransfer <= 0) - break; - - int amountToKeep = getFilteredFluidAmount(fluidStack); - int amountInDest = destinationAmounts.getOrDefault(fluidStack, 0); - if (amountInDest >= amountToKeep) - continue; - - FluidStack fluidToMove = fluidStack.copy(); - fluidToMove.setAmount(Math.min(fluidLeftToTransfer, amountToKeep - amountInDest)); - if (fluidToMove.getAmount() <= 0) - continue; - - FluidStack drained = source.drain(fluidToMove, FluidAction.SIMULATE); - int fillableAmount = destination.fill(drained, FluidAction.SIMULATE); - if (fillableAmount <= 0) - continue; - - fluidToMove.setAmount(Math.min(fluidToMove.getAmount(), fillableAmount)); - - drained = source.drain(fluidToMove, FluidAction.EXECUTE); - int movedAmount = destination.fill(drained, FluidAction.EXECUTE); - - fluidLeftToTransfer -= movedAmount; + @Override + protected void performTransferOnUpdate(@NotNull IFluidHandler sourceHandler, @NotNull IFluidHandler destHandler) { + if (transferMode != TransferMode.TRANSFER_EXACT) { + super.performTransferOnUpdate(sourceHandler, destHandler); + return; } - - return platformTransferLimit - fluidLeftToTransfer; + FilterHandler filter = this.getFilterHandler(); + if (filter == null) return; + IntUnaryOperator maxflow = s -> { + int limit = filter.getFilter().getTransferLimit(s); + if (getFluidsLeftToTransfer() < limit) { + noTransferDueToMinimum = true; + return 0; + } else return limit; + }; + performTransfer(sourceHandler, destHandler, true, maxflow, maxflow, (a, b) -> reportFluidsTransfer(b)); } - private void setTransferBucketMode(BucketMode transferBucketMode) { - var oldMultiplier = this.transferBucketMode.multiplier; - var newMultiplier = transferBucketMode.multiplier; - - this.transferBucketMode = transferBucketMode; - - if (transferSizeInput == null) return; - - if (oldMultiplier > newMultiplier) { - transferSizeInput.setValue(getCurrentBucketModeTransferSize()); - } - this.transferSizeInput.setMax(MAX_STACK_SIZE / this.transferBucketMode.multiplier); - if (newMultiplier > oldMultiplier) { - transferSizeInput.setValue(getCurrentBucketModeTransferSize()); + @Override + protected int simpleInsert(@NotNull IFluidHandler destHandler, FluidTestObject testObject, int count, + boolean simulate) { + if (transferMode == TransferMode.KEEP_EXACT) { + assert getFilterHandler().isFilterPresent(); + int kept = getFilterHandler().getFilter().getTransferLimit(testObject.recombine()); + count = Math.min(count, kept - computeContained(destHandler, testObject)); } + return super.simpleInsert(destHandler, testObject, count, simulate); } - private void setTransferMode(TransferMode transferMode) { - this.transferMode = transferMode; - - configureTransferSizeInput(); - - if (!this.isRemote()) { - configureFilter(); + public void setTransferMode(TransferMode transferMode) { + if (this.transferMode != transferMode) { + this.transferMode = transferMode; + this.coverHolder.markDirty(); + this.getFilterHandler().getFilter().setMaxTransferSize(this.transferMode.maxStackSize); } } @@ -203,14 +132,6 @@ protected void configureFilter() { configureTransferSizeInput(); } - private int getFilteredFluidAmount(FluidStack fluidStack) { - if (!filterHandler.isFilterPresent()) - return globalTransferLimit; - - FluidFilter filter = filterHandler.getFilter(); - return (filter.supportsAmounts() ? filter.testFluidAmount(fluidStack) : globalTransferLimit); - } - /////////////////////////// // ***** GUI ******// /////////////////////////// @@ -231,26 +152,27 @@ protected void buildAdditionalUI(WidgetGroup group) { configureTransferSizeInput(); group.addWidget(this.transferSizeInput); - this.transferBucketModeInput = new EnumSelectorWidget<>(121, 45, 20, 20, BucketMode.values(), - transferBucketMode, this::setTransferBucketMode); - group.addWidget(this.transferBucketModeInput); + this.bucketModeInput = new EnumSelectorWidget<>(121, 45, 20, 20, BucketMode.values(), + bucketMode, this::setBucketMode); + group.addWidget(this.bucketModeInput); } private int getCurrentBucketModeTransferSize() { - return this.globalTransferLimit / this.transferBucketMode.multiplier; + return this.getFilterHandler().getFilter().getMaxTransferSize() / this.bucketMode.multiplier; } - private void setCurrentBucketModeTransferSize(int transferSize) { - this.globalTransferLimit = Math.min(Math.max(transferSize * this.transferBucketMode.multiplier, 0), - MAX_STACK_SIZE); + private void setCurrentBucketModeTransferSize(long transferSize) { + this.getFilterHandler().getFilter() + .setMaxTransferSize((int) Math.min(Math.max(transferSize * this.bucketMode.multiplier, 0), + MAX_STACK_SIZE)); } private void configureTransferSizeInput() { - if (this.transferSizeInput == null || transferBucketModeInput == null) + if (this.transferSizeInput == null || bucketModeInput == null) return; this.transferSizeInput.setVisible(shouldShowTransferSize()); - this.transferBucketModeInput.setVisible(shouldShowTransferSize()); + this.bucketModeInput.setVisible(shouldShowTransferSize()); } private boolean shouldShowTransferSize() { @@ -262,4 +184,15 @@ private boolean shouldShowTransferSize() { return !this.filterHandler.getFilter().supportsAmounts(); } + + protected int computeContained(@NotNull IFluidHandler handler, @NotNull FluidTestObject testObject) { + int found = 0; + for (int i = 0; i < handler.getTanks(); ++i) { + FluidStack contained = handler.getFluidInTank(i); + if (testObject.test(contained)) { + found += contained.getAmount(); + } + } + return found; + } } diff --git a/src/main/java/com/gregtechceu/gtceu/common/cover/InfiniteWaterCover.java b/src/main/java/com/gregtechceu/gtceu/common/cover/InfiniteWaterCover.java index 73e47ca738..d0d29e0f1c 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/cover/InfiniteWaterCover.java +++ b/src/main/java/com/gregtechceu/gtceu/common/cover/InfiniteWaterCover.java @@ -1,18 +1,24 @@ package com.gregtechceu.gtceu.common.cover; +import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.capability.ICoverable; import com.gregtechceu.gtceu.api.cover.CoverBehavior; import com.gregtechceu.gtceu.api.cover.CoverDefinition; import com.gregtechceu.gtceu.api.machine.TickableSubscription; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRenderer; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRendererBuilder; import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.core.Direction; import net.minecraft.world.level.material.Fluids; +import net.minecraftforge.common.capabilities.ForgeCapabilities; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.FluidType; import net.minecraftforge.fluids.FluidUtil; import net.minecraftforge.fluids.capability.IFluidHandler; +import org.jetbrains.annotations.NotNull; + import javax.annotation.ParametersAreNonnullByDefault; /** @@ -31,8 +37,14 @@ public InfiniteWaterCover(CoverDefinition definition, ICoverable coverHolder, Di } @Override - public boolean canAttach() { - return FluidUtil.getFluidHandler(coverHolder.getLevel(), coverHolder.getPos(), attachedSide).isPresent(); + protected CoverRenderer buildRenderer() { + return new CoverRendererBuilder(GTCEu.id("block/cover/overlay_infinite_water"), + GTCEu.id("block/cover/overlay_infinite_water_emissive")).build(); + } + + @Override + public boolean canAttach(@NotNull ICoverable coverable, @NotNull Direction side) { + return coverable.getCapability(ForgeCapabilities.FLUID_HANDLER).isPresent(); } @Override diff --git a/src/main/java/com/gregtechceu/gtceu/common/cover/ItemFilterCover.java b/src/main/java/com/gregtechceu/gtceu/common/cover/ItemFilterCover.java index 15a26b35d9..e587715099 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/cover/ItemFilterCover.java +++ b/src/main/java/com/gregtechceu/gtceu/common/cover/ItemFilterCover.java @@ -1,15 +1,17 @@ package com.gregtechceu.gtceu.common.cover; +import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.capability.ICoverable; import com.gregtechceu.gtceu.api.cover.CoverBehavior; import com.gregtechceu.gtceu.api.cover.CoverDefinition; import com.gregtechceu.gtceu.api.cover.IUICover; -import com.gregtechceu.gtceu.api.cover.filter.ItemFilter; -import com.gregtechceu.gtceu.api.cover.filter.SmartItemFilter; +import com.gregtechceu.gtceu.api.cover.filter.*; import com.gregtechceu.gtceu.api.gui.widget.EnumSelectorWidget; import com.gregtechceu.gtceu.api.machine.MachineCoverContainer; import com.gregtechceu.gtceu.api.machine.MetaMachine; import com.gregtechceu.gtceu.api.transfer.item.ItemHandlerDelegate; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRenderer; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRendererBuilder; import com.gregtechceu.gtceu.common.cover.data.FilterMode; import com.gregtechceu.gtceu.common.cover.data.ManualIOMode; @@ -22,9 +24,9 @@ import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.core.Direction; -import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.item.ItemStack; -import net.minecraftforge.items.IItemHandlerModifiable; +import net.minecraftforge.common.capabilities.ForgeCapabilities; +import net.minecraftforge.items.IItemHandler; import lombok.Getter; import lombok.Setter; @@ -40,12 +42,12 @@ */ @ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault -public class ItemFilterCover extends CoverBehavior implements IUICover { +public class ItemFilterCover extends CoverBehavior implements IUICover, CoverWithItemFilter { public static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder(ItemFilterCover.class, CoverBehavior.MANAGED_FIELD_HOLDER); - protected ItemFilter itemFilter; + protected FilterHandler filterHandler; @Persisted @DescSynced @Getter @@ -57,17 +59,25 @@ public class ItemFilterCover extends CoverBehavior implements IUICover { public ItemFilterCover(CoverDefinition definition, ICoverable coverHolder, Direction attachedSide) { super(definition, coverHolder, attachedSide); + filterHandler = FilterHandlers.item(this); } - public ItemFilter getItemFilter() { - if (itemFilter == null) { - itemFilter = ItemFilter.loadFilter(attachItem); - if (itemFilter instanceof SmartItemFilter smart && coverHolder instanceof MachineCoverContainer mcc) { + @Override + protected CoverRenderer buildRenderer() { + return new CoverRendererBuilder(GTCEu.id("block/cover/overlay_item_filter"), null).build(); + } + + @Override + public @NotNull FilterHandler getFilterHandler() { + if (!filterHandler.isFilterPresent()) { + filterHandler.loadFilter(attachItem); + if (filterHandler.getFilter() instanceof SmartItemFilter smart && + coverHolder instanceof MachineCoverContainer mcc) { var machine = MetaMachine.getMachine(mcc.getLevel(), mcc.getPos()); if (machine != null) smart.setModeFromMachine(machine.getDefinition().getName()); } } - return itemFilter; + return filterHandler; } public void setFilterMode(FilterMode filterMode) { @@ -76,12 +86,17 @@ public void setFilterMode(FilterMode filterMode) { } @Override - public boolean canAttach() { - return coverHolder.getItemHandlerCap(attachedSide, false) != null; + public ManualIOMode getManualIOMode() { + return ManualIOMode.FILTERED; + } + + @Override + public boolean canAttach(@NotNull ICoverable coverable, @NotNull Direction side) { + return coverable.getCapability(ForgeCapabilities.ITEM_HANDLER).isPresent(); } @Override - public @Nullable IItemHandlerModifiable getItemHandlerCap(IItemHandlerModifiable defaultValue) { + public @Nullable IItemHandler getItemHandlerCap(IItemHandler defaultValue) { if (defaultValue == null) { return null; } @@ -91,11 +106,6 @@ public boolean canAttach() { return itemFilterWrapper; } - @Override - public void onAttached(ItemStack itemStack, ServerPlayer player) { - super.onAttached(itemStack, player); - } - @Override public Widget createUIWidget() { final var group = new WidgetGroup(0, 0, 178, 85); @@ -103,7 +113,7 @@ public Widget createUIWidget() { group.addWidget(new EnumSelectorWidget<>(35, 25, 18, 18, FilterMode.VALUES, filterMode, this::setFilterMode)); group.addWidget(new EnumSelectorWidget<>(35, 45, 18, 18, ManualIOMode.VALUES, allowFlow, this::setAllowFlow)); - group.addWidget(getItemFilter().openConfigurator(62, 25)); + group.addWidget(getFilterHandler().getFilter().openConfigurator(62, 25)); return group; } @@ -114,7 +124,7 @@ public ManagedFieldHolder getFieldHolder() { private class FilteredItemHandlerWrapper extends ItemHandlerDelegate { - public FilteredItemHandlerWrapper(IItemHandlerModifiable delegate) { + public FilteredItemHandlerWrapper(IItemHandler delegate) { super(delegate); } @@ -122,7 +132,7 @@ public FilteredItemHandlerWrapper(IItemHandlerModifiable delegate) { public @NotNull ItemStack insertItem(int slot, @NotNull ItemStack stack, boolean simulate) { if ((filterMode == FilterMode.FILTER_EXTRACT) && allowFlow == ManualIOMode.UNFILTERED) return super.insertItem(slot, stack, simulate); - if (filterMode != FilterMode.FILTER_EXTRACT && getItemFilter().test(stack)) { + if (filterMode != FilterMode.FILTER_EXTRACT && getFilterHandler().test(stack)) { return super.insertItem(slot, stack, simulate); } return stack; @@ -135,7 +145,7 @@ public FilteredItemHandlerWrapper(IItemHandlerModifiable delegate) { return super.extractItem(slot, amount, false); } - if (filterMode != FilterMode.FILTER_INSERT && getItemFilter().test(result)) { + if (filterMode != FilterMode.FILTER_INSERT && getFilterHandler().test(result)) { return super.extractItem(slot, amount, false); } return ItemStack.EMPTY; diff --git a/src/main/java/com/gregtechceu/gtceu/common/cover/MachineControllerCover.java b/src/main/java/com/gregtechceu/gtceu/common/cover/MachineControllerCover.java index 53402975bd..d313a980c7 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/cover/MachineControllerCover.java +++ b/src/main/java/com/gregtechceu/gtceu/common/cover/MachineControllerCover.java @@ -1,8 +1,10 @@ package com.gregtechceu.gtceu.common.cover; +import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; import com.gregtechceu.gtceu.api.capability.IControllable; import com.gregtechceu.gtceu.api.capability.ICoverable; +import com.gregtechceu.gtceu.api.capability.forge.GTCapability; import com.gregtechceu.gtceu.api.cover.CoverBehavior; import com.gregtechceu.gtceu.api.cover.CoverDefinition; import com.gregtechceu.gtceu.api.cover.IUICover; @@ -11,6 +13,8 @@ import com.gregtechceu.gtceu.api.gui.widget.PhantomSlotWidget; import com.gregtechceu.gtceu.api.gui.widget.ToggleButtonWidget; import com.gregtechceu.gtceu.api.transfer.item.CustomItemStackHandler; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRenderer; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRendererBuilder; import com.gregtechceu.gtceu.common.cover.data.ControllerMode; import com.gregtechceu.gtceu.data.lang.LangHandler; @@ -31,10 +35,10 @@ import net.minecraft.world.inventory.ClickType; import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; import lombok.Getter; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Arrays; @@ -76,8 +80,13 @@ public MachineControllerCover(CoverDefinition definition, ICoverable coverHolder } @Override - public boolean canAttach() { - return !getAllowedModes().isEmpty(); + protected CoverRenderer buildRenderer() { + return new CoverRendererBuilder(GTCEu.id("block/cover/overlay_controller"), null).build(); + } + + @Override + public boolean canAttach(@NotNull ICoverable coverable, @NotNull Direction side) { + return !getAllowedModes(coverable, side).isEmpty(); } @Override @@ -99,6 +108,11 @@ public boolean canConnectRedstone() { return true; } + @Override + public void onRedstoneInputSignalChange(int newSignalStrength) { + updateInput(); + } + @Override public void onNeighborChanged(Block block, BlockPos fromPos, boolean isMoving) { super.onNeighborChanged(block, fromPos, isMoving); @@ -186,11 +200,15 @@ public List getAllowedModes() { .collect(Collectors.toList()); } - private int getInputSignal() { - Level level = coverHolder.getLevel(); - BlockPos sourcePos = coverHolder.getPos().relative(attachedSide); + public static List getAllowedModes(ICoverable coverable, Direction attachedSide) { + return Arrays.stream(ControllerMode.values()) + .filter(mode -> mode.side != attachedSide) + .filter(mode -> coverable.getCapability(GTCapability.CAPABILITY_CONTROLLABLE, mode.side).isPresent()) + .collect(Collectors.toList()); + } - return level.getSignal(sourcePos, attachedSide); + private int getInputSignal() { + return coverHolder.getInputRedstoneSignal(attachedSide, true); } ////////////////////////////////////// diff --git a/src/main/java/com/gregtechceu/gtceu/common/cover/PumpCover.java b/src/main/java/com/gregtechceu/gtceu/common/cover/PumpCover.java index f5f40bb7c0..5408941c73 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/cover/PumpCover.java +++ b/src/main/java/com/gregtechceu/gtceu/common/cover/PumpCover.java @@ -1,5 +1,6 @@ package com.gregtechceu.gtceu.common.cover; +import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.GTValues; import com.gregtechceu.gtceu.api.capability.IControllable; import com.gregtechceu.gtceu.api.capability.ICoverable; @@ -7,20 +8,35 @@ import com.gregtechceu.gtceu.api.cover.CoverBehavior; import com.gregtechceu.gtceu.api.cover.CoverDefinition; import com.gregtechceu.gtceu.api.cover.IUICover; +import com.gregtechceu.gtceu.api.cover.filter.CoverWithFluidFilter; import com.gregtechceu.gtceu.api.cover.filter.FilterHandler; import com.gregtechceu.gtceu.api.cover.filter.FilterHandlers; import com.gregtechceu.gtceu.api.cover.filter.FluidFilter; +import com.gregtechceu.gtceu.api.graphnet.GraphNetUtility; +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.NodeExposingCapabilities; +import com.gregtechceu.gtceu.api.graphnet.predicate.test.FluidTestObject; +import com.gregtechceu.gtceu.api.graphnet.traverse.EdgeDirection; +import com.gregtechceu.gtceu.api.graphnet.traverse.EdgeSelector; +import com.gregtechceu.gtceu.api.graphnet.traverse.NetClosestIterator; +import com.gregtechceu.gtceu.api.graphnet.traverse.ResilientNetClosestIterator; import com.gregtechceu.gtceu.api.gui.widget.EnumSelectorWidget; import com.gregtechceu.gtceu.api.gui.widget.IntInputWidget; import com.gregtechceu.gtceu.api.gui.widget.NumberInputWidget; import com.gregtechceu.gtceu.api.machine.ConditionalSubscriptionHandler; import com.gregtechceu.gtceu.api.transfer.fluid.FluidHandlerDelegate; import com.gregtechceu.gtceu.api.transfer.fluid.IFluidHandlerModifiable; -import com.gregtechceu.gtceu.api.transfer.fluid.ModifiableFluidHandlerWrapper; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRenderer; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRendererBuilder; import com.gregtechceu.gtceu.common.cover.data.BucketMode; +import com.gregtechceu.gtceu.common.cover.data.DistributionMode; +import com.gregtechceu.gtceu.common.cover.data.FilterMode; import com.gregtechceu.gtceu.common.cover.data.ManualIOMode; -import com.gregtechceu.gtceu.utils.FluidStackHashStrategy; +import com.gregtechceu.gtceu.common.cover.filter.MatchResult; +import com.gregtechceu.gtceu.common.pipelike.net.fluid.*; import com.gregtechceu.gtceu.utils.GTTransferUtils; +import com.gregtechceu.gtceu.utils.GTUtil; +import com.gregtechceu.gtceu.utils.function.BiIntConsumer; import com.lowdragmc.lowdraglib.gui.widget.LabelWidget; import com.lowdragmc.lowdraglib.gui.widget.Widget; @@ -36,18 +52,23 @@ import net.minecraft.network.chat.Component; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.Block; +import net.minecraftforge.common.capabilities.ForgeCapabilities; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.capability.IFluidHandler; import it.unimi.dsi.fastutil.ints.Int2IntFunction; -import it.unimi.dsi.fastutil.objects.Object2IntOpenCustomHashMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; +import it.unimi.dsi.fastutil.objects.*; import lombok.Getter; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Arrays; +import java.util.Iterator; import java.util.List; -import java.util.Map; +import java.util.Set; +import java.util.function.IntUnaryOperator; +import java.util.function.Predicate; import javax.annotation.ParametersAreNonnullByDefault; @@ -58,7 +79,7 @@ */ @ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault -public class PumpCover extends CoverBehavior implements IUICover, IControllable { +public class PumpCover extends CoverBehavior implements IUICover, IControllable, CoverWithFluidFilter { public static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder(PumpCover.class, CoverBehavior.MANAGED_FIELD_HOLDER); @@ -80,6 +101,10 @@ public class PumpCover extends CoverBehavior implements IUICover, IControllable @Persisted @DescSynced @Getter + protected DistributionMode distributionMode = DistributionMode.FLOOD; + @Persisted + @DescSynced + @Getter protected BucketMode bucketMode = BucketMode.MILLI_BUCKET; @Persisted @DescSynced @@ -93,10 +118,16 @@ public class PumpCover extends CoverBehavior implements IUICover, IControllable @Persisted @DescSynced + @Getter protected final FilterHandler filterHandler; protected final ConditionalSubscriptionHandler subscriptionHandler; private NumberInputWidget transferRateWidget; + protected final ObjectLinkedOpenHashSet extractionRoundRobinCache = new ObjectLinkedOpenHashSet<>(); + protected final ObjectLinkedOpenHashSet insertionRoundRobinCache = new ObjectLinkedOpenHashSet<>(); + + protected @Nullable CoverRenderer rendererInverted; + public PumpCover(CoverDefinition definition, ICoverable coverHolder, Direction attachedSide, int tier, int maxTransferRate) { super(definition, coverHolder, attachedSide); @@ -121,7 +152,7 @@ protected boolean isSubscriptionActive() { return isWorkingEnabled() && getAdjacentFluidHandler() != null; } - protected @Nullable IFluidHandlerModifiable getOwnFluidHandler() { + protected @Nullable IFluidHandler getOwnFluidHandler() { return coverHolder.getFluidHandlerCap(attachedSide, false); } @@ -140,8 +171,8 @@ public ManagedFieldHolder getFieldHolder() { } @Override - public boolean canAttach() { - return getOwnFluidHandler() != null; + public boolean canAttach(@NotNull ICoverable coverable, @NotNull Direction side) { + return coverable.getCapability(ForgeCapabilities.FLUID_HANDLER, side).isPresent(); } public void setIo(IO io) { @@ -176,6 +207,28 @@ public void onNeighborChanged(Block block, BlockPos fromPos, boolean isMoving) { subscriptionHandler.updateSubscription(); } + @Override + public @NotNull CoverRenderer getRenderer() { + if (io == IO.OUT) { + if (renderer == null) renderer = buildRenderer(); + return renderer; + } else { + if (rendererInverted == null) rendererInverted = buildRendererInverted(); + return rendererInverted; + } + } + + @Override + protected CoverRenderer buildRenderer() { + return new CoverRendererBuilder(GTCEu.id("block/cover/overlay_pump"), + GTCEu.id("block/cover/overlay_pump_emissive")).build(); + } + + protected CoverRenderer buildRendererInverted() { + return new CoverRendererBuilder(GTCEu.id("block/cover/overlay_pump_inverted"), + GTCEu.id("block/cover/overlay_pump_inverted_emissive")).build(); + } + @Override public void setWorkingEnabled(boolean isWorkingAllowed) { if (this.isWorkingEnabled != isWorkingAllowed) { @@ -211,87 +264,433 @@ public void setBucketMode(BucketMode bucketMode) { } } + public void setDistributionMode(DistributionMode distributionMode) { + this.distributionMode = distributionMode; + this.extractionRoundRobinCache.clear(); + this.extractionRoundRobinCache.trim(16); + this.insertionRoundRobinCache.clear(); + this.insertionRoundRobinCache.trim(16); + coverHolder.markDirty(); + } + protected void setManualIOMode(ManualIOMode manualIOMode) { this.manualIOMode = manualIOMode; coverHolder.markDirty(); } - protected void update() { - long timer = coverHolder.getOffsetTimer(); - if (timer % 5 != 0) - return; + @Override + public FilterMode getFilterMode() { + return FilterMode.FILTER_BOTH; + } - if (mBLeftToTransferLastSecond > 0) { - int platformTransferredFluid = doTransferFluids(mBLeftToTransferLastSecond); - this.mBLeftToTransferLastSecond -= platformTransferredFluid; + public void update() { + long timer = coverHolder.getOffsetTimer(); + if (isWorkingEnabled && getFluidsLeftToTransfer() > 0) { + IFluidHandler fluidHandler = getAdjacentFluidHandler(); + IFluidHandler myFluidHandler = getOwnFluidHandler(); + if (myFluidHandler != null && fluidHandler != null) { + if (io == IO.OUT) { + performTransferOnUpdate(myFluidHandler, fluidHandler); + } else { + performTransferOnUpdate(fluidHandler, myFluidHandler); + } + } } - if (timer % 20 == 0) { - this.mBLeftToTransferLastSecond = transferRate * 20; + refreshBuffer(transferRate); } - - subscriptionHandler.updateSubscription(); } - private int doTransferFluids(int platformTransferLimit) { - var adjacent = getAdjacentFluidHandler(); - var adjacentModifiable = adjacent instanceof IFluidHandlerModifiable modifiable ? modifiable : - new ModifiableFluidHandlerWrapper(adjacent); - var ownFluidHandler = getOwnFluidHandler(); - - if (adjacent != null && ownFluidHandler != null) { - return switch (io) { - case IN -> doTransferFluidsInternal(adjacentModifiable, ownFluidHandler, platformTransferLimit); - case OUT -> doTransferFluidsInternal(ownFluidHandler, adjacentModifiable, platformTransferLimit); - default -> 0; - }; - } - return 0; + public int getFluidsLeftToTransfer() { + return mBLeftToTransferLastSecond; } - protected int doTransferFluidsInternal(IFluidHandlerModifiable source, IFluidHandlerModifiable destination, - int platformTransferLimit) { - return transferAny(source, destination, platformTransferLimit); + public void reportFluidsTransfer(int transferred) { + mBLeftToTransferLastSecond -= transferred; } - protected int transferAny(IFluidHandlerModifiable source, IFluidHandlerModifiable destination, - int platformTransferLimit) { - return GTTransferUtils.transferFluidsFiltered(source, destination, filterHandler.getFilter(), - platformTransferLimit); + protected void refreshBuffer(int transferRate) { + this.mBLeftToTransferLastSecond = transferRate; } - protected enum TransferDirection { - INSERT, - EXTRACT + protected void performTransferOnUpdate(@NotNull IFluidHandler sourceHandler, @NotNull IFluidHandler destHandler) { + reportFluidsTransfer(performTransfer(sourceHandler, destHandler, false, i -> 0, + i -> getFluidsLeftToTransfer(), null)); } - protected Map enumerateDistinctFluids(IFluidHandlerModifiable fluidHandler, - TransferDirection direction) { - final Map summedFluids = new Object2IntOpenCustomHashMap<>( - FluidStackHashStrategy.comparingAllButAmount()); - - for (int tank = 0; tank < fluidHandler.getTanks(); tank++) { - if (!canTransfer(fluidHandler, direction, tank)) - continue; + /** + * Performs transfer + * + * @param sourceHandler the handler to pull from + * @param destHandler the handler to push to + * @param byFilterSlot whether to perform the transfer by filter slot. + * @param minTransfer the minimum allowed transfer amount, when given a filter slot. If no filter exists or not + * transferring by slot, a filter slot of -1 will be passed in. + * @param maxTransfer the maximum allowed transfer amount, when given a filter slot. If no filter exists or not + * transferring by slot, a filter slot of -1 will be passed in. + * @param transferReport where transfer is reported; a is the filter slot, b is the amount of transfer. + * Each filter slot will report its transfer before the next slot is calculated. + * @return how much was transferred in total. + */ + protected int performTransfer(@NotNull IFluidHandler sourceHandler, @NotNull IFluidHandler destHandler, + boolean byFilterSlot, @NotNull IntUnaryOperator minTransfer, + @NotNull IntUnaryOperator maxTransfer, @Nullable BiIntConsumer transferReport) { + FilterHandler filter = this.getFilterHandler(); + byFilterSlot = byFilterSlot && filter.isFilterPresent(); // can't be by filter slot if there is no filter + Object2IntOpenHashMap contained = new Object2IntOpenHashMap<>(); + for (int i = 0; i < sourceHandler.getTanks(); i++) { + FluidStack contents = sourceHandler.getFluidInTank(i); + if (!contents.isEmpty()) contained.merge(new FluidTestObject(contents), contents.getAmount(), Integer::sum); + } + var iter = contained.object2IntEntrySet().fastIterator(); + int totalTransfer = 0; + while (iter.hasNext()) { + var content = iter.next(); + MatchResult match = null; + if (!filter.isFilterPresent() || + (match = filter.getFilter().match(content.getKey().recombine(content.getIntValue()))).isMatched()) { + int filterSlot = -1; + if (byFilterSlot) { + // we know it is not null, because if it were byFilterSlot would be false. + assert filter.isFilterPresent(); + filterSlot = match.getFilterIndex(); + } + int min = Math.max(minTransfer.applyAsInt(filterSlot), 1); + int max = maxTransfer.applyAsInt(filterSlot); + if (max < min) continue; + + if (content.getIntValue() < min) continue; + int transfer = Math.min(content.getIntValue(), max); + transfer = doInsert(destHandler, content.getKey(), transfer, true); + if (transfer < min) continue; + transfer = doExtract(sourceHandler, content.getKey(), transfer, true); + if (transfer < min) continue; + doExtract(sourceHandler, content.getKey(), transfer, false); + doInsert(destHandler, content.getKey(), transfer, false); + if (transferReport != null) transferReport.accept(filterSlot, transfer); + totalTransfer += transfer; + } + } + return totalTransfer; + } + + protected ObjectLinkedOpenHashSet getRoundRobinCache(boolean extract, boolean simulate) { + ObjectLinkedOpenHashSet set = extract ? extractionRoundRobinCache : insertionRoundRobinCache; + return simulate ? set.clone() : set; + } + + protected int doExtract(@NotNull IFluidHandler handler, FluidTestObject testObject, int count, + boolean simulate) { + FluidCapabilityObject cap; + if (distributionMode == DistributionMode.FLOOD || (cap = FluidCapabilityObject.instanceOf(handler)) == null) + return simpleExtract(handler, testObject, count, simulate); + NetNode origin = cap.getNode(); + Predicate filter = GraphNetUtility.standardEdgeBlacklist(testObject); + // if you find yourself here because you added a new distribution mode and now it won't compile, + // good luck. + return switch (distributionMode) { + case ROUND_ROBIN -> { + FluidNetworkView view = cap.getNetworkView(); + Iterator iter = view.handler().getBackingHandlers().iterator(); + ObjectLinkedOpenHashSet cache = getRoundRobinCache(true, simulate); + Set backlog = new ObjectOpenHashSet<>(); + Object2IntOpenHashMap flows = new Object2IntOpenHashMap<>(); + int available = count; + while (available > 0) { + if (!cache.isEmpty() && backlog.remove(cache.first())) { + IFluidHandler candidate = cache.first(); + NetNode linked = view.handlerNetNodeBiMap().get(candidate); + if (linked == null) { + cache.removeFirst(); + continue; + } else { + cache.addAndMoveToLast(candidate); + } + available = rrExtract(testObject, simulate, origin, filter, flows, available, candidate, + linked); + continue; + } + if (iter.hasNext()) { + IFluidHandler candidate = iter.next(); + boolean frontOfCache = !cache.isEmpty() && cache.first() == candidate; + if (frontOfCache || !cache.contains(candidate)) { + NetNode linked = view.handlerNetNodeBiMap().get(candidate); + if (linked == null) { + if (frontOfCache) cache.removeFirst(); + continue; + } else { + cache.addAndMoveToLast(candidate); + } + available = rrExtract(testObject, simulate, origin, filter, flows, available, candidate, + linked); + } else { + backlog.add(candidate); + } + } else if (backlog.isEmpty()) { + // we have finished the iterator and backlog + break; + } else { + if (!cache.isEmpty()) { + if (view.handler().getBackingHandlers().contains(cache.first())) + break; // we've already visited the next node in the cache + else { + // the network view does not contain the node in the front of the cache, so yeet it. + cache.removeFirst(); + } + } else { + break; // cache is empty and iterator is empty, something is weird, just exit. + } + } + } + while (iter.hasNext()) { + cache.add(iter.next()); + } + if (!simulate) { + for (var entry : flows.object2IntEntrySet()) { + FluidCapabilityObject.reportFlow(entry.getKey(), entry.getIntValue(), testObject); + } + } + yield count - available; + } + case EQUALIZED -> { + NetClosestIterator gather = new NetClosestIterator(origin, + EdgeSelector.filtered(EdgeDirection.INCOMING, filter)); + Object2ObjectOpenHashMap candidates = new Object2ObjectOpenHashMap<>(); + while (gather.hasNext()) { + NetNode node = gather.next(); + if (node instanceof NodeExposingCapabilities exposer) { + IFluidHandler h = exposer.getProvider().getCapability( + ForgeCapabilities.FLUID_HANDLER, exposer.exposedFacing()) + .resolve().orElse(null); + if (h != null && FluidCapabilityObject.instanceOf(h) == null) { + candidates.put(node, h); + } + } + } + int largestMin = count / candidates.size(); + if (largestMin <= 0) yield 0; + for (IFluidHandler value : candidates.values()) { + largestMin = Math.min(largestMin, simpleExtract(value, testObject, largestMin, true)); + if (largestMin <= 0) yield 0; + } + // binary search for largest scale that doesn't exceed flow limits + Int2ObjectArrayMap> flows = new Int2ObjectArrayMap<>(); + largestMin = GTUtil.binarySearchInt(0, largestMin, l -> { + if (flows.containsKey(l) && flows.get(l) == null) return false; + ResilientNetClosestIterator forwardFrontier = new ResilientNetClosestIterator(origin, + EdgeSelector.filtered(EdgeDirection.INCOMING, filter)); + Object2IntOpenHashMap localFlows = new Object2IntOpenHashMap<>(); + for (NetNode node : candidates.keySet()) { + ResilientNetClosestIterator backwardFrontier = new ResilientNetClosestIterator(node, + EdgeSelector.filtered(EdgeDirection.OUTGOING, filter)); + if (GraphNetUtility.p2pWalk(simulate, l, + n -> FluidCapabilityObject.getFlowLimit(n, testObject) - localFlows.getInt(n), + (n, i) -> localFlows.put(n, localFlows.getInt(n) + i), + forwardFrontier, backwardFrontier) < l) + return false; + } + flows.put(l, localFlows); + return true; + }, false); + if (largestMin <= 0 || flows.get(largestMin) == null) yield 0; + if (!simulate) { + for (IFluidHandler value : candidates.values()) { + simpleExtract(value, testObject, largestMin, false); + } + for (var e : flows.get(largestMin).object2IntEntrySet()) { + FluidCapabilityObject.reportFlow(e.getKey(), e.getIntValue(), testObject); + } + } + yield largestMin * candidates.size(); + } + case FLOOD -> 0; // how are you here? + }; + } - FluidStack fluidStack = fluidHandler.getFluidInTank(tank); - if (fluidStack.isEmpty()) - continue; + protected int rrExtract(FluidTestObject testObject, boolean simulate, NetNode origin, Predicate filter, + Object2IntOpenHashMap flows, int available, IFluidHandler candidate, + NetNode linked) { + int accepted = simpleExtract(candidate, testObject, available, true); + if (accepted > 0) { + ResilientNetClosestIterator forwardFrontier = new ResilientNetClosestIterator(origin, + EdgeSelector.filtered(EdgeDirection.INCOMING, filter)); + ResilientNetClosestIterator backwardFrontier = new ResilientNetClosestIterator(linked, + EdgeSelector.filtered(EdgeDirection.OUTGOING, filter)); + accepted = GraphNetUtility.p2pWalk(simulate, accepted, + n -> FluidCapabilityObject.getFlowLimit(n, testObject) - flows.getInt(n), + (n, i) -> flows.put(n, flows.getInt(n) + i), + forwardFrontier, backwardFrontier); + if (accepted > 0) { + available -= accepted; + if (!simulate) simpleExtract(candidate, testObject, accepted, false); + } + } + return available; + } + + protected int simpleExtract(@NotNull IFluidHandler destHandler, FluidTestObject testObject, int count, + boolean simulate) { + FluidStack ext = destHandler.drain(testObject.recombine(count), + !simulate ? IFluidHandler.FluidAction.SIMULATE : IFluidHandler.FluidAction.EXECUTE); + return ext.isEmpty() ? 0 : ext.getAmount(); + } + + protected int doInsert(@NotNull IFluidHandler handler, FluidTestObject testObject, int count, + boolean simulate) { + FluidCapabilityObject cap; + if (distributionMode == DistributionMode.FLOOD || (cap = FluidCapabilityObject.instanceOf(handler)) == null) + return simpleInsert(handler, testObject, count, simulate); + NetNode origin = cap.getNode(); + Predicate filter = GraphNetUtility.standardEdgeBlacklist(testObject); + // if you find yourself here because you added a new distribution mode and now it won't compile, + // good luck. + return switch (distributionMode) { + case ROUND_ROBIN -> { + FluidNetworkView view = cap.getNetworkView(); + Iterator iter = view.handler().getBackingHandlers().iterator(); + ObjectLinkedOpenHashSet cache = getRoundRobinCache(false, simulate); + Set backlog = new ObjectOpenHashSet<>(); + Object2IntOpenHashMap flows = new Object2IntOpenHashMap<>(); + int available = count; + while (available > 0) { + if (!cache.isEmpty() && backlog.remove(cache.first())) { + IFluidHandler candidate = cache.first(); + NetNode linked = view.handlerNetNodeBiMap().get(candidate); + if (linked == null) { + cache.removeFirst(); + continue; + } else { + cache.addAndMoveToLast(candidate); + } + available = rrInsert(testObject, simulate, origin, filter, flows, available, candidate, linked); + continue; + } + if (iter.hasNext()) { + IFluidHandler candidate = iter.next(); + boolean frontOfCache = !cache.isEmpty() && cache.first() == candidate; + if (frontOfCache || !cache.contains(candidate)) { + NetNode linked = view.handlerNetNodeBiMap().get(candidate); + if (linked == null) { + if (frontOfCache) cache.removeFirst(); + continue; + } else { + cache.addAndMoveToLast(candidate); + } + available = rrInsert(testObject, simulate, origin, filter, flows, available, candidate, + linked); + } else { + backlog.add(candidate); + } + } else if (backlog.isEmpty()) { + // we have finished the iterator and backlog + break; + } else { + if (!cache.isEmpty()) { + if (view.handler().getBackingHandlers().contains(cache.first())) + break; // we've already visited the next node in the cache + else { + // the network view does not contain the node in the front of the cache, so yeet it. + cache.removeFirst(); + } + } else { + break; // cache is empty and iterator is empty, something is weird, just exit. + } + } + } + while (iter.hasNext()) { + cache.add(iter.next()); + } + if (!simulate) { + for (var entry : flows.object2IntEntrySet()) { + FluidCapabilityObject.reportFlow(entry.getKey(), entry.getIntValue(), testObject); + } + } + yield count - available; + } + case EQUALIZED -> { + NetClosestIterator gather = new NetClosestIterator(origin, + EdgeSelector.filtered(EdgeDirection.OUTGOING, filter)); + Object2ObjectOpenHashMap candidates = new Object2ObjectOpenHashMap<>(); + while (gather.hasNext()) { + NetNode node = gather.next(); + if (node instanceof NodeExposingCapabilities exposer) { + IFluidHandler h = exposer.getProvider().getCapability( + ForgeCapabilities.FLUID_HANDLER, exposer.exposedFacing()) + .resolve().orElse(null); + if (h != null && FluidCapabilityObject.instanceOf(h) == null) { + candidates.put(node, h); + } + } + } + int largestMin = count / candidates.size(); + if (largestMin <= 0) yield 0; + for (IFluidHandler value : candidates.values()) { + largestMin = Math.min(largestMin, simpleInsert(value, testObject, largestMin, true)); + if (largestMin <= 0) yield 0; + } + // binary search for largest scale that doesn't exceed flow limits + Int2ObjectArrayMap> flows = new Int2ObjectArrayMap<>(); + largestMin = GTUtil.binarySearchInt(0, largestMin, l -> { + if (flows.containsKey(l) && flows.get(l) == null) return false; + ResilientNetClosestIterator forwardFrontier = new ResilientNetClosestIterator(origin, + EdgeSelector.filtered(EdgeDirection.OUTGOING, filter)); + Object2IntOpenHashMap localFlows = new Object2IntOpenHashMap<>(); + for (NetNode node : candidates.keySet()) { + ResilientNetClosestIterator backwardFrontier = new ResilientNetClosestIterator(node, + EdgeSelector.filtered(EdgeDirection.INCOMING, filter)); + if (GraphNetUtility.p2pWalk(simulate, l, + n -> FluidCapabilityObject.getFlowLimit(n, testObject) - localFlows.getInt(n), + (n, i) -> localFlows.put(n, localFlows.getInt(n) + i), + forwardFrontier, backwardFrontier) < l) + return false; + } + flows.put(l, localFlows); + return true; + }, false); + if (largestMin <= 0 || flows.get(largestMin) == null) yield 0; + if (!simulate) { + for (IFluidHandler value : candidates.values()) { + simpleInsert(value, testObject, largestMin, false); + } + for (var e : flows.get(largestMin).object2IntEntrySet()) { + FluidCapabilityObject.reportFlow(e.getKey(), e.getIntValue(), testObject); + } + } + yield largestMin * candidates.size(); + } + case FLOOD -> 0; // how are you here? + }; + } - summedFluids.putIfAbsent(fluidStack, 0); - summedFluids.computeIfPresent(fluidStack, (stack, totalAmount) -> { - return totalAmount + stack.getAmount(); - }); + protected int rrInsert(FluidTestObject testObject, boolean simulate, NetNode origin, Predicate filter, + Object2IntOpenHashMap flows, int available, IFluidHandler candidate, + NetNode linked) { + int accepted = simpleInsert(candidate, testObject, available, true); + if (accepted > 0) { + ResilientNetClosestIterator forwardFrontier = new ResilientNetClosestIterator(origin, + EdgeSelector.filtered(EdgeDirection.OUTGOING, filter)); + ResilientNetClosestIterator backwardFrontier = new ResilientNetClosestIterator(linked, + EdgeSelector.filtered(EdgeDirection.INCOMING, filter)); + accepted = GraphNetUtility.p2pWalk(simulate, accepted, + n -> FluidCapabilityObject.getFlowLimit(n, testObject) - flows.getInt(n), + (n, i) -> flows.put(n, flows.getInt(n) + i), + forwardFrontier, backwardFrontier); + if (accepted > 0) { + available -= accepted; + if (!simulate) simpleInsert(candidate, testObject, accepted, false); + } } + return available; + } - return summedFluids; + protected int simpleInsert(@NotNull IFluidHandler destHandler, FluidTestObject testObject, int count, + boolean simulate) { + return destHandler.fill(testObject.recombine(count), + simulate ? IFluidHandler.FluidAction.SIMULATE : IFluidHandler.FluidAction.EXECUTE); } - private static boolean canTransfer(IFluidHandlerModifiable fluidHandler, TransferDirection direction, int tank) { - return switch (direction) { - case INSERT -> fluidHandler.supportsFill(tank); - case EXTRACT -> fluidHandler.supportsDrain(tank); - }; + protected boolean checkInputFluid(FluidStack fluidStack) { + return filterHandler.test(fluidStack); } ////////////////////////////////////// diff --git a/src/main/java/com/gregtechceu/gtceu/common/cover/RobotArmCover.java b/src/main/java/com/gregtechceu/gtceu/common/cover/RobotArmCover.java index cfd394a6fe..a6f570362c 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/cover/RobotArmCover.java +++ b/src/main/java/com/gregtechceu/gtceu/common/cover/RobotArmCover.java @@ -1,14 +1,17 @@ package com.gregtechceu.gtceu.common.cover; +import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.capability.ICoverable; -import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.cover.CoverDefinition; +import com.gregtechceu.gtceu.api.cover.filter.FilterHandler; import com.gregtechceu.gtceu.api.cover.filter.ItemFilter; import com.gregtechceu.gtceu.api.cover.filter.SimpleItemFilter; +import com.gregtechceu.gtceu.api.graphnet.predicate.test.ItemTestObject; import com.gregtechceu.gtceu.api.gui.widget.EnumSelectorWidget; import com.gregtechceu.gtceu.api.gui.widget.IntInputWidget; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRenderer; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRendererBuilder; import com.gregtechceu.gtceu.common.cover.data.TransferMode; -import com.gregtechceu.gtceu.common.pipelike.item.ItemNetHandler; import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup; import com.lowdragmc.lowdraglib.syncdata.annotation.DescSynced; @@ -23,8 +26,7 @@ import lombok.Getter; import org.jetbrains.annotations.NotNull; -import java.util.Iterator; -import java.util.Map; +import java.util.function.IntUnaryOperator; import javax.annotation.ParametersAreNonnullByDefault; @@ -39,6 +41,7 @@ public class RobotArmCover extends ConveyorCover { @DescSynced @Getter protected TransferMode transferMode; + protected boolean noTransferDueToMinimum = false; @Persisted @Getter @@ -63,89 +66,69 @@ public ManagedFieldHolder getFieldHolder() { } @Override - protected int doTransferItems(IItemHandler itemHandler, IItemHandler myItemHandler, int maxTransferAmount) { - if (io == IO.OUT && itemHandler instanceof ItemNetHandler && transferMode == TransferMode.KEEP_EXACT) { - return 0; - } - if (io == IO.IN && myItemHandler instanceof ItemNetHandler && transferMode == TransferMode.KEEP_EXACT) { - return 0; - } - return switch (transferMode) { - case TRANSFER_ANY -> moveInventoryItems(itemHandler, myItemHandler, maxTransferAmount); - case TRANSFER_EXACT -> doTransferExact(itemHandler, myItemHandler, maxTransferAmount); - case KEEP_EXACT -> doKeepExact(itemHandler, myItemHandler, maxTransferAmount); - }; + protected CoverRenderer buildRenderer() { + return new CoverRendererBuilder(GTCEu.id("block/cover/overlay_arm"), + GTCEu.id("block/cover/overlay_arm_emissive")).build(); } - protected int doTransferExact(IItemHandler sourceInventory, IItemHandler targetInventory, int maxTransferAmount) { - Map sourceItemAmount = countInventoryItemsByType(sourceInventory); - - Iterator iterator = sourceItemAmount.keySet().iterator(); - while (iterator.hasNext()) { - TypeItemInfo sourceInfo = sourceItemAmount.get(iterator.next()); - int itemAmount = sourceInfo.totalCount; - int itemToMoveAmount = getFilteredItemAmount(sourceInfo.itemStack); + @Override + protected CoverRenderer buildRendererInverted() { + return new CoverRendererBuilder(GTCEu.id("block/cover/overlay_arm"), + GTCEu.id("block/cover/overlay_arm_inverted_emissive")).build(); + } - if (itemAmount >= itemToMoveAmount) { - sourceInfo.totalCount = itemToMoveAmount; - } else { - iterator.remove(); + @Override + protected void refreshBuffer(int transferRate) { + if (this.transferMode == TransferMode.TRANSFER_EXACT && noTransferDueToMinimum) { + if (getFilterHandler().isFilterPresent()) { + this.noTransferDueToMinimum = false; + this.itemsLeftToTransferLastSecond += transferRate; + int max = getFilterHandler().getFilter().getMaxTransferSize(); + if (this.itemsLeftToTransferLastSecond > max) { + this.itemsLeftToTransferLastSecond = max; + } + return; } } + super.refreshBuffer(transferRate); + } - int itemsTransferred = 0; - int maxTotalTransferAmount = maxTransferAmount + itemsTransferBuffered; - boolean notEnoughTransferRate = false; - for (TypeItemInfo itemInfo : sourceItemAmount.values()) { - if (maxTotalTransferAmount >= itemInfo.totalCount) { - boolean result = moveInventoryItemsExact(sourceInventory, targetInventory, itemInfo); - itemsTransferred += result ? itemInfo.totalCount : 0; - maxTotalTransferAmount -= result ? itemInfo.totalCount : 0; - } else { - notEnoughTransferRate = true; - } - } - // if we didn't transfer anything because of too small transfer rate, buffer it - if (itemsTransferred == 0 && notEnoughTransferRate) { - itemsTransferBuffered += maxTransferAmount; - } else { - // otherwise, if transfer succeed, empty transfer buffer value - itemsTransferBuffered = 0; + @Override + protected void performTransferOnUpdate(@NotNull IItemHandler sourceHandler, @NotNull IItemHandler destHandler) { + if (transferMode != TransferMode.TRANSFER_EXACT) { + super.performTransferOnUpdate(sourceHandler, destHandler); + return; } - return Math.min(itemsTransferred, maxTransferAmount); + FilterHandler filter = this.getFilterHandler(); + if (!filter.isFilterPresent()) return; + IntUnaryOperator reqFlow = s -> { + int limit = filter.getFilter().getTransferLimit(s); + if (getItemsLeftToTransfer() < limit) { + noTransferDueToMinimum = true; + return 0; + } else return limit; + }; + performTransfer(sourceHandler, destHandler, true, reqFlow, reqFlow, (a, b) -> reportItemsTransfer(b)); } - protected int doKeepExact(IItemHandler sourceInventory, IItemHandler targetInventory, int maxTransferAmount) { - Map targetItemAmounts = countInventoryItemsByMatchSlot(targetInventory); - Map sourceItemAmounts = countInventoryItemsByMatchSlot(sourceInventory); - - Iterator iterator = sourceItemAmounts.keySet().iterator(); - while (iterator.hasNext()) { - ItemStack filteredItem = iterator.next(); - GroupItemInfo sourceInfo = sourceItemAmounts.get(filteredItem); - int itemToKeepAmount = getFilteredItemAmount(sourceInfo.itemStack); - - int itemAmount = 0; - if (targetItemAmounts.containsKey(filteredItem)) { - GroupItemInfo destItemInfo = targetItemAmounts.get(filteredItem); - itemAmount = destItemInfo.totalCount; - } - if (itemAmount < itemToKeepAmount) { - sourceInfo.totalCount = itemToKeepAmount - itemAmount; - } else { - iterator.remove(); - } + @Override + protected int simpleInsert(@NotNull IItemHandler handler, ItemTestObject testObject, int count, + boolean simulate) { + if (transferMode == TransferMode.KEEP_EXACT) { + assert getFilterHandler().isFilterPresent(); + int kept = getFilterHandler().getFilter().getTransferLimit(testObject.recombine()); + count = Math.min(count, kept - computeContained(handler, testObject)); } - - return moveInventoryItems(sourceInventory, targetInventory, sourceItemAmounts, maxTransferAmount); + return super.simpleInsert(handler, testObject, count, simulate); } - private int getFilteredItemAmount(ItemStack itemStack) { - if (!filterHandler.isFilterPresent()) - return globalTransferLimit; - - ItemFilter filter = filterHandler.getFilter(); - return filter.supportsAmounts() ? filter.testItemCount(itemStack) : globalTransferLimit; + public void setTransferMode(TransferMode transferMode) { + if (this.transferMode != transferMode) { + this.transferMode = transferMode; + this.coverHolder.markDirty(); + configureStackSizeInput(); + this.getFilterHandler().getFilter().setMaxTransferSize(transferMode.maxStackSize); + } } public int getBuffer() { @@ -182,16 +165,6 @@ protected void buildAdditionalUI(WidgetGroup group) { group.addWidget(this.stackSizeInput); } - private void setTransferMode(TransferMode transferMode) { - this.transferMode = transferMode; - - configureStackSizeInput(); - - if (!this.isRemote()) { - configureFilter(); - } - } - @Override protected void configureFilter() { if (filterHandler.getFilter() instanceof SimpleItemFilter filter) { @@ -219,4 +192,15 @@ private boolean shouldShowStackSize() { return !this.filterHandler.getFilter().supportsAmounts(); } + + protected int computeContained(@NotNull IItemHandler handler, @NotNull ItemTestObject testObject) { + int found = 0; + for (int i = 0; i < handler.getSlots(); i++) { + ItemStack contained = handler.getStackInSlot(i); + if (testObject.test(contained)) { + found += contained.getCount(); + } + } + return found; + } } diff --git a/src/main/java/com/gregtechceu/gtceu/common/cover/ShutterCover.java b/src/main/java/com/gregtechceu/gtceu/common/cover/ShutterCover.java index e1be88cf33..7b2408e11a 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/cover/ShutterCover.java +++ b/src/main/java/com/gregtechceu/gtceu/common/cover/ShutterCover.java @@ -1,10 +1,12 @@ package com.gregtechceu.gtceu.common.cover; +import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.capability.IControllable; import com.gregtechceu.gtceu.api.capability.ICoverable; import com.gregtechceu.gtceu.api.cover.CoverBehavior; import com.gregtechceu.gtceu.api.cover.CoverDefinition; -import com.gregtechceu.gtceu.api.transfer.fluid.IFluidHandlerModifiable; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRenderer; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRendererBuilder; import com.lowdragmc.lowdraglib.syncdata.annotation.Persisted; import com.lowdragmc.lowdraglib.syncdata.field.ManagedFieldHolder; @@ -16,12 +18,10 @@ import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.player.Player; import net.minecraft.world.phys.BlockHitResult; -import net.minecraftforge.items.IItemHandlerModifiable; import lombok.Getter; import lombok.Setter; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import javax.annotation.ParametersAreNonnullByDefault; @@ -42,6 +42,11 @@ public ShutterCover(@NotNull CoverDefinition definition, @NotNull ICoverable cov super(definition, coverableView, attachedSide); } + @Override + protected CoverRenderer buildRenderer() { + return new CoverRendererBuilder(GTCEu.id("block/cover/overlay_shutter"), null).build(); + } + @Override public InteractionResult onScrewdriverClick(Player playerIn, InteractionHand hand, BlockHitResult hitResult) { return InteractionResult.FAIL; @@ -49,7 +54,13 @@ public InteractionResult onScrewdriverClick(Player playerIn, InteractionHand han @Override public boolean canPipePassThrough() { - return !workingEnabled; + // isWorkingAllowed restriction is applied during edge predication + return true; + } + + @Override + public boolean forcePipeRenderConnection() { + return false; } @Override @@ -62,16 +73,6 @@ public InteractionResult onSoftMalletClick(Player playerIn, InteractionHand hand return InteractionResult.SUCCESS; } - @Override - public @Nullable IItemHandlerModifiable getItemHandlerCap(IItemHandlerModifiable defaultValue) { - return isWorkingEnabled() ? null : super.getItemHandlerCap(defaultValue); - } - - @Override - public @Nullable IFluidHandlerModifiable getFluidHandlerCap(IFluidHandlerModifiable defaultValue) { - return isWorkingEnabled() ? null : super.getFluidHandlerCap(defaultValue); - } - @Override public ManagedFieldHolder getFieldHolder() { return MANAGED_FIELD_HOLDER; diff --git a/src/main/java/com/gregtechceu/gtceu/common/cover/StorageCover.java b/src/main/java/com/gregtechceu/gtceu/common/cover/StorageCover.java index 6c6a5b640c..fdca35ee47 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/cover/StorageCover.java +++ b/src/main/java/com/gregtechceu/gtceu/common/cover/StorageCover.java @@ -1,5 +1,6 @@ package com.gregtechceu.gtceu.common.cover; +import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.capability.ICoverable; import com.gregtechceu.gtceu.api.cover.CoverBehavior; import com.gregtechceu.gtceu.api.cover.CoverDefinition; @@ -9,6 +10,9 @@ import com.gregtechceu.gtceu.api.gui.widget.SlotWidget; import com.gregtechceu.gtceu.api.machine.MachineCoverContainer; import com.gregtechceu.gtceu.api.transfer.item.CustomItemStackHandler; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRenderer; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRendererBuilder; +import com.gregtechceu.gtceu.utils.GTUtil; import com.lowdragmc.lowdraglib.gui.texture.IGuiTexture; import com.lowdragmc.lowdraglib.gui.widget.LabelWidget; @@ -67,13 +71,18 @@ public List getAdditionalDrops() { } @Override - public boolean canAttach() { + protected CoverRenderer buildRenderer() { + return new CoverRendererBuilder(GTCEu.id("block/cover/overlay_storage"), null).build(); + } + + @Override + public boolean canAttach(@NotNull ICoverable coverable, @NotNull Direction side) { if (!(coverHolder instanceof MachineCoverContainer)) return false; - for (var dir : Direction.values()) { + for (var dir : GTUtil.DIRECTIONS) { if (coverHolder.hasCover(dir) && coverHolder.getCoverAtSide(dir) instanceof StorageCover) return false; } - return super.canAttach(); + return super.canAttach(coverable, side); } @Override diff --git a/src/main/java/com/gregtechceu/gtceu/common/cover/data/DistributionMode.java b/src/main/java/com/gregtechceu/gtceu/common/cover/data/DistributionMode.java index 137f7c5d7d..ab6233fc38 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/cover/data/DistributionMode.java +++ b/src/main/java/com/gregtechceu/gtceu/common/cover/data/DistributionMode.java @@ -7,9 +7,9 @@ public enum DistributionMode implements EnumSelectorWidget.SelectableEnum { - ROUND_ROBIN_GLOBAL("round_robin_global"), - ROUND_ROBIN_PRIO("round_robin_prio"), - INSERT_FIRST("insert_first"); + EQUALIZED("equalized"), + ROUND_ROBIN("round_robin"), + FLOOD("flood"); public static final DistributionMode[] VALUES = values(); private static final float OFFSET = 1.0f / VALUES.length; @@ -22,7 +22,7 @@ public enum DistributionMode implements EnumSelectorWidget.SelectableEnum { @Override public String getTooltip() { - return "cover.conveyor.distribution." + localeName; + return "cover.generic.distribution." + localeName; } @Override diff --git a/src/main/java/com/gregtechceu/gtceu/common/cover/detector/ActivityDetectorCover.java b/src/main/java/com/gregtechceu/gtceu/common/cover/detector/ActivityDetectorCover.java index d10aa75c3f..9561bbce93 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/cover/detector/ActivityDetectorCover.java +++ b/src/main/java/com/gregtechceu/gtceu/common/cover/detector/ActivityDetectorCover.java @@ -1,12 +1,18 @@ package com.gregtechceu.gtceu.common.cover.detector; +import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; import com.gregtechceu.gtceu.api.capability.ICoverable; +import com.gregtechceu.gtceu.api.capability.forge.GTCapability; import com.gregtechceu.gtceu.api.cover.CoverDefinition; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRenderer; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRendererBuilder; import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.core.Direction; +import org.jetbrains.annotations.NotNull; + import javax.annotation.ParametersAreNonnullByDefault; @ParametersAreNonnullByDefault @@ -18,8 +24,14 @@ public ActivityDetectorCover(CoverDefinition definition, ICoverable coverHolder, } @Override - public boolean canAttach() { - return GTCapabilityHelper.getWorkable(coverHolder.getLevel(), coverHolder.getPos(), attachedSide) != null; + protected CoverRenderer buildRenderer() { + return new CoverRendererBuilder(GTCEu.id("block/cover/overlay_activity_detector"), + GTCEu.id("block/cover/overlay_activity_detector_emissive")).build(); + } + + @Override + public boolean canAttach(@NotNull ICoverable coverable, @NotNull Direction side) { + return coverable.getCapability(GTCapability.CAPABILITY_WORKABLE).isPresent(); } @Override diff --git a/src/main/java/com/gregtechceu/gtceu/common/cover/detector/AdvancedActivityDetectorCover.java b/src/main/java/com/gregtechceu/gtceu/common/cover/detector/AdvancedActivityDetectorCover.java index 415ba2eeca..6fcef4b8ee 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/cover/detector/AdvancedActivityDetectorCover.java +++ b/src/main/java/com/gregtechceu/gtceu/common/cover/detector/AdvancedActivityDetectorCover.java @@ -1,8 +1,11 @@ package com.gregtechceu.gtceu.common.cover.detector; +import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; import com.gregtechceu.gtceu.api.capability.ICoverable; import com.gregtechceu.gtceu.api.cover.CoverDefinition; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRenderer; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRendererBuilder; import com.gregtechceu.gtceu.utils.RedstoneUtil; import net.minecraft.MethodsReturnNonnullByDefault; @@ -18,6 +21,12 @@ public AdvancedActivityDetectorCover(CoverDefinition definition, ICoverable cove super(definition, coverHolder, attachedSide); } + @Override + protected CoverRenderer buildRenderer() { + return new CoverRendererBuilder(GTCEu.id("block/cover/overlay_activity_detector_advanced"), + GTCEu.id("block/cover/overlay_activity_detector_advanced_emissive")).build(); + } + @Override protected void update() { if (this.coverHolder.getOffsetTimer() % 20 != 0) diff --git a/src/main/java/com/gregtechceu/gtceu/common/cover/detector/AdvancedEnergyDetectorCover.java b/src/main/java/com/gregtechceu/gtceu/common/cover/detector/AdvancedEnergyDetectorCover.java index 8dc48f889f..4d7d089000 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/cover/detector/AdvancedEnergyDetectorCover.java +++ b/src/main/java/com/gregtechceu/gtceu/common/cover/detector/AdvancedEnergyDetectorCover.java @@ -8,6 +8,8 @@ import com.gregtechceu.gtceu.api.gui.GuiTextures; import com.gregtechceu.gtceu.api.gui.widget.LongInputWidget; import com.gregtechceu.gtceu.api.gui.widget.ToggleButtonWidget; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRenderer; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRendererBuilder; import com.gregtechceu.gtceu.data.lang.LangHandler; import com.gregtechceu.gtceu.utils.GTMath; import com.gregtechceu.gtceu.utils.RedstoneUtil; @@ -67,6 +69,11 @@ public AdvancedEnergyDetectorCover(CoverDefinition definition, ICoverable coverH this.usePercent = true; } + @Override + protected CoverRenderer buildRenderer() { + return new CoverRendererBuilder(GTCEu.id("block/cover/overlay_energy_detector_advanced"), null).build(); + } + @Override protected void update() { if (coverHolder.getOffsetTimer() % 20 != 0) return; diff --git a/src/main/java/com/gregtechceu/gtceu/common/cover/detector/AdvancedFluidDetectorCover.java b/src/main/java/com/gregtechceu/gtceu/common/cover/detector/AdvancedFluidDetectorCover.java index 1705d97799..04bb8fe627 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/cover/detector/AdvancedFluidDetectorCover.java +++ b/src/main/java/com/gregtechceu/gtceu/common/cover/detector/AdvancedFluidDetectorCover.java @@ -1,5 +1,6 @@ package com.gregtechceu.gtceu.common.cover.detector; +import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.capability.ICoverable; import com.gregtechceu.gtceu.api.cover.CoverDefinition; import com.gregtechceu.gtceu.api.cover.IUICover; @@ -9,6 +10,8 @@ import com.gregtechceu.gtceu.api.gui.GuiTextures; import com.gregtechceu.gtceu.api.gui.widget.IntInputWidget; import com.gregtechceu.gtceu.api.gui.widget.ToggleButtonWidget; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRenderer; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRendererBuilder; import com.gregtechceu.gtceu.data.lang.LangHandler; import com.gregtechceu.gtceu.utils.GTMath; import com.gregtechceu.gtceu.utils.RedstoneUtil; @@ -71,6 +74,11 @@ public AdvancedFluidDetectorCover(CoverDefinition definition, ICoverable coverHo filterHandler = FilterHandlers.fluid(this); } + @Override + protected CoverRenderer buildRenderer() { + return new CoverRendererBuilder(GTCEu.id("block/cover/overlay_fluid_detector_advanced"), null).build(); + } + @Override public List getAdditionalDrops() { var list = super.getAdditionalDrops(); diff --git a/src/main/java/com/gregtechceu/gtceu/common/cover/detector/AdvancedItemDetectorCover.java b/src/main/java/com/gregtechceu/gtceu/common/cover/detector/AdvancedItemDetectorCover.java index 84dd663482..857a690d92 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/cover/detector/AdvancedItemDetectorCover.java +++ b/src/main/java/com/gregtechceu/gtceu/common/cover/detector/AdvancedItemDetectorCover.java @@ -1,5 +1,6 @@ package com.gregtechceu.gtceu.common.cover.detector; +import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.capability.ICoverable; import com.gregtechceu.gtceu.api.cover.CoverDefinition; import com.gregtechceu.gtceu.api.cover.IUICover; @@ -9,6 +10,8 @@ import com.gregtechceu.gtceu.api.gui.GuiTextures; import com.gregtechceu.gtceu.api.gui.widget.IntInputWidget; import com.gregtechceu.gtceu.api.gui.widget.ToggleButtonWidget; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRenderer; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRendererBuilder; import com.gregtechceu.gtceu.data.lang.LangHandler; import com.gregtechceu.gtceu.utils.RedstoneUtil; @@ -70,6 +73,11 @@ public AdvancedItemDetectorCover(CoverDefinition definition, ICoverable coverHol filterHandler = FilterHandlers.item(this); } + @Override + protected CoverRenderer buildRenderer() { + return new CoverRendererBuilder(GTCEu.id("block/cover/overlay_item_detector_advanced"), null).build(); + } + @Override public List getAdditionalDrops() { var list = super.getAdditionalDrops(); diff --git a/src/main/java/com/gregtechceu/gtceu/common/cover/detector/EnergyDetectorCover.java b/src/main/java/com/gregtechceu/gtceu/common/cover/detector/EnergyDetectorCover.java index c5334db9f5..6daa7fb0af 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/cover/detector/EnergyDetectorCover.java +++ b/src/main/java/com/gregtechceu/gtceu/common/cover/detector/EnergyDetectorCover.java @@ -1,13 +1,18 @@ package com.gregtechceu.gtceu.common.cover.detector; +import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; import com.gregtechceu.gtceu.api.capability.ICoverable; import com.gregtechceu.gtceu.api.capability.IEnergyInfoProvider; +import com.gregtechceu.gtceu.api.capability.forge.GTCapability; import com.gregtechceu.gtceu.api.cover.CoverDefinition; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRenderer; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRendererBuilder; import com.gregtechceu.gtceu.utils.RedstoneUtil; import net.minecraft.core.Direction; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class EnergyDetectorCover extends DetectorCover { @@ -17,8 +22,13 @@ public EnergyDetectorCover(CoverDefinition definition, ICoverable coverHolder, D } @Override - public boolean canAttach() { - return getEnergyInfoProvider() != null; + protected CoverRenderer buildRenderer() { + return new CoverRendererBuilder(GTCEu.id("block/cover/overlay_energy_detector"), null).build(); + } + + @Override + public boolean canAttach(@NotNull ICoverable coverable, @NotNull Direction side) { + return coverable.getCapability(GTCapability.CAPABILITY_ENERGY_INFO_PROVIDER).isPresent(); } @Override diff --git a/src/main/java/com/gregtechceu/gtceu/common/cover/detector/FluidDetectorCover.java b/src/main/java/com/gregtechceu/gtceu/common/cover/detector/FluidDetectorCover.java index f3d3ec8f2b..61ed274db2 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/cover/detector/FluidDetectorCover.java +++ b/src/main/java/com/gregtechceu/gtceu/common/cover/detector/FluidDetectorCover.java @@ -1,14 +1,20 @@ package com.gregtechceu.gtceu.common.cover.detector; +import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.capability.ICoverable; import com.gregtechceu.gtceu.api.cover.CoverDefinition; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRenderer; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRendererBuilder; import com.gregtechceu.gtceu.utils.RedstoneUtil; import net.minecraft.core.Direction; +import net.minecraftforge.common.capabilities.ForgeCapabilities; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.FluidUtil; import net.minecraftforge.fluids.capability.IFluidHandler; +import org.jetbrains.annotations.NotNull; + public class FluidDetectorCover extends DetectorCover { public FluidDetectorCover(CoverDefinition definition, ICoverable coverHolder, Direction attachedSide) { @@ -16,8 +22,13 @@ public FluidDetectorCover(CoverDefinition definition, ICoverable coverHolder, Di } @Override - public boolean canAttach() { - return getFluidHandler() != null; + protected CoverRenderer buildRenderer() { + return new CoverRendererBuilder(GTCEu.id("block/cover/overlay_fluid_detector"), null).build(); + } + + @Override + public boolean canAttach(@NotNull ICoverable coverable, @NotNull Direction side) { + return coverable.getCapability(ForgeCapabilities.FLUID_HANDLER).isPresent(); } @Override diff --git a/src/main/java/com/gregtechceu/gtceu/common/cover/detector/ItemDetectorCover.java b/src/main/java/com/gregtechceu/gtceu/common/cover/detector/ItemDetectorCover.java index 268fde61ab..fcdff8d11b 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/cover/detector/ItemDetectorCover.java +++ b/src/main/java/com/gregtechceu/gtceu/common/cover/detector/ItemDetectorCover.java @@ -1,13 +1,19 @@ package com.gregtechceu.gtceu.common.cover.detector; +import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.capability.ICoverable; import com.gregtechceu.gtceu.api.cover.CoverDefinition; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRenderer; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRendererBuilder; import com.gregtechceu.gtceu.utils.GTTransferUtils; import com.gregtechceu.gtceu.utils.RedstoneUtil; import net.minecraft.core.Direction; +import net.minecraftforge.common.capabilities.ForgeCapabilities; import net.minecraftforge.items.IItemHandler; +import org.jetbrains.annotations.NotNull; + public class ItemDetectorCover extends DetectorCover { public ItemDetectorCover(CoverDefinition definition, ICoverable coverHolder, Direction attachedSide) { @@ -15,8 +21,13 @@ public ItemDetectorCover(CoverDefinition definition, ICoverable coverHolder, Dir } @Override - public boolean canAttach() { - return getItemHandler() != null; + protected CoverRenderer buildRenderer() { + return new CoverRendererBuilder(GTCEu.id("block/cover/overlay_item_detector"), null).build(); + } + + @Override + public boolean canAttach(@NotNull ICoverable coverable, @NotNull Direction side) { + return coverable.getCapability(ForgeCapabilities.ITEM_HANDLER).isPresent(); } @Override diff --git a/src/main/java/com/gregtechceu/gtceu/common/cover/detector/MaintenanceDetectorCover.java b/src/main/java/com/gregtechceu/gtceu/common/cover/detector/MaintenanceDetectorCover.java index ad5ba24c7c..2149a5391a 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/cover/detector/MaintenanceDetectorCover.java +++ b/src/main/java/com/gregtechceu/gtceu/common/cover/detector/MaintenanceDetectorCover.java @@ -1,13 +1,19 @@ package com.gregtechceu.gtceu.common.cover.detector; +import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; import com.gregtechceu.gtceu.api.capability.ICoverable; +import com.gregtechceu.gtceu.api.capability.forge.GTCapability; import com.gregtechceu.gtceu.api.cover.CoverDefinition; import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMaintenanceMachine; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRenderer; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRendererBuilder; import com.gregtechceu.gtceu.config.ConfigHolder; import net.minecraft.core.Direction; +import org.jetbrains.annotations.NotNull; + public class MaintenanceDetectorCover extends DetectorCover { public MaintenanceDetectorCover(CoverDefinition definition, ICoverable coverHolder, Direction attachedSide) { @@ -15,13 +21,17 @@ public MaintenanceDetectorCover(CoverDefinition definition, ICoverable coverHold } @Override - public boolean canAttach() { + protected CoverRenderer buildRenderer() { + return new CoverRendererBuilder(GTCEu.id("block/cover/overlay_maintenance_detector"), null).build(); + } + + @Override + public boolean canAttach(@NotNull ICoverable coverable, @NotNull Direction side) { if (!ConfigHolder.INSTANCE.machines.enableMaintenance) { return false; } - return GTCapabilityHelper.getMaintenanceMachine(coverHolder.getLevel(), coverHolder.getPos(), attachedSide) != - null; + return coverable.getCapability(GTCapability.CAPABILITY_MAINTENANCE_MACHINE).isPresent(); } @Override diff --git a/src/main/java/com/gregtechceu/gtceu/common/cover/filter/MatchResult.java b/src/main/java/com/gregtechceu/gtceu/common/cover/filter/MatchResult.java new file mode 100644 index 0000000000..bdb9982393 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/cover/filter/MatchResult.java @@ -0,0 +1,46 @@ +package com.gregtechceu.gtceu.common.cover.filter; + +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class MatchResult { + + public static final MatchResult NONE = new MatchResult(false, null, -1); + public static final MatchResult ANY = new MatchResult(true, null, -1); + private final boolean matched; + private final Object matchedObject; + private final int filterIndex; + + private MatchResult(boolean matched, Object matchedObject, int filterIndex) { + this.matched = matched; + this.matchedObject = matchedObject; + this.filterIndex = filterIndex; + } + + public boolean isMatched() { + return matched; + } + + public Object getMatchedObject() { + return matchedObject; + } + + public @NotNull ItemStack getItemStack() { + return matchedObject instanceof ItemStack stack ? stack : ItemStack.EMPTY; + } + + public @Nullable FluidStack getFluidStack() { + return matchedObject instanceof FluidStack stack ? stack : null; + } + + public int getFilterIndex() { + return filterIndex; + } + + public static MatchResult create(boolean matched, Object matchedStack, int filterIndex) { + return new MatchResult(matched, matchedStack, filterIndex); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/cover/filter/MergabilityInfo.java b/src/main/java/com/gregtechceu/gtceu/common/cover/filter/MergabilityInfo.java new file mode 100644 index 0000000000..5ae7f2fdc3 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/cover/filter/MergabilityInfo.java @@ -0,0 +1,61 @@ +package com.gregtechceu.gtceu.common.cover.filter; + +import com.gregtechceu.gtceu.api.graphnet.predicate.test.IPredicateTestObject; + +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Comparator; +import java.util.List; + +public final class MergabilityInfo { + + private final Object2ObjectOpenHashMap mergeMap = new Object2ObjectOpenHashMap<>(); + + public void add(int handlerSlot, T testObject, int count) { + mergeMap.compute(testObject, (k, v) -> { + if (v == null) v = new Merge(k); + v.count += count; + v.handlerSlots.add(handlerSlot); + return v; + }); + } + + public Merge getLargestMerge() { + return mergeMap.values().stream().max(Comparator.comparingInt(Merge::getCount)).orElse(null); + } + + public @NotNull List getNonLargestMerges(@Nullable Merge largestMerge) { + List merges = new ObjectArrayList<>(mergeMap.values()); + merges.remove(largestMerge == null ? getLargestMerge() : largestMerge); + return merges; + } + + public final class Merge { + + private final T testObject; + + private int count = 0; + private final IntList handlerSlots = new IntArrayList(); + + public Merge(T testObject) { + this.testObject = testObject; + } + + public int getCount() { + return count; + } + + public T getTestObject() { + return testObject; + } + + public IntList getHandlerSlots() { + return handlerSlots; + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/cover/voiding/AdvancedFluidVoidingCover.java b/src/main/java/com/gregtechceu/gtceu/common/cover/voiding/AdvancedFluidVoidingCover.java index 6c547b35a4..b862ff7c9e 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/cover/voiding/AdvancedFluidVoidingCover.java +++ b/src/main/java/com/gregtechceu/gtceu/common/cover/voiding/AdvancedFluidVoidingCover.java @@ -1,5 +1,6 @@ package com.gregtechceu.gtceu.common.cover.voiding; +import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.capability.ICoverable; import com.gregtechceu.gtceu.api.cover.CoverDefinition; import com.gregtechceu.gtceu.api.cover.filter.FluidFilter; @@ -7,9 +8,11 @@ import com.gregtechceu.gtceu.api.gui.widget.EnumSelectorWidget; import com.gregtechceu.gtceu.api.gui.widget.IntInputWidget; import com.gregtechceu.gtceu.api.gui.widget.NumberInputWidget; -import com.gregtechceu.gtceu.api.transfer.fluid.IFluidHandlerModifiable; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRenderer; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRendererBuilder; import com.gregtechceu.gtceu.common.cover.data.BucketMode; import com.gregtechceu.gtceu.common.cover.data.VoidingMode; +import com.gregtechceu.gtceu.utils.GTTransferUtils; import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup; import com.lowdragmc.lowdraglib.syncdata.annotation.DescSynced; @@ -18,13 +21,14 @@ import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.core.Direction; +import net.minecraftforge.common.capabilities.ForgeCapabilities; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.capability.IFluidHandler; import lombok.Getter; import org.jetbrains.annotations.NotNull; -import java.util.Map; +import java.util.function.Predicate; import javax.annotation.ParametersAreNonnullByDefault; @@ -53,36 +57,57 @@ public AdvancedFluidVoidingCover(CoverDefinition definition, ICoverable coverHol super(definition, coverHolder, attachedSide); } + @Override + protected CoverRenderer buildRenderer() { + return new CoverRendererBuilder(GTCEu.id("block/cover/overlay_fluid_voiding_advanced"), + GTCEu.id("block/cover/overlay_fluid_voiding_advanced_emissive")).build(); + } + ////////////////////////////////////////////// // *********** COVER LOGIC ***********// ////////////////////////////////////////////// @Override - protected void doVoidFluids() { - IFluidHandlerModifiable fluidHandler = getOwnFluidHandler(); - if (fluidHandler == null) { + protected void doTransferFluids() { + IFluidHandler myFluidHandler = coverHolder.getCapability(ForgeCapabilities.FLUID_HANDLER, + attachedSide).resolve().orElse(null); + if (myFluidHandler == null) { return; } - switch (voidingMode) { - case VOID_ANY -> voidAny(fluidHandler); - case VOID_OVERFLOW -> voidOverflow(fluidHandler); + case VOID_ANY -> GTTransferUtils.transferFluidsFiltered(myFluidHandler, nullFluidTank, + getFilterHandler()::test); + case VOID_OVERFLOW -> voidOverflow(myFluidHandler, getFilterHandler()::test, + this.globalTransferSizeMillibuckets); } + subscriptionHandler.updateSubscription(); } - private void voidOverflow(IFluidHandlerModifiable fluidHandler) { - final Map fluidAmounts = enumerateDistinctFluids(fluidHandler, TransferDirection.EXTRACT); + /** + * Performs one tick worth of Keep Exact behavior. + * + * @param sourceHandler source(s) to move fluids from + * @param fluidFilter a predicate which determines what fluids may be moved + * @param keepAmount the desired amount in milliBuckets of a particular fluid in the destination + */ + protected void voidOverflow(final IFluidHandler sourceHandler, + final Predicate fluidFilter, + int keepAmount) { + if (sourceHandler == null || fluidFilter == null) + return; - for (FluidStack fluidStack : fluidAmounts.keySet()) { - int presentAmount = fluidAmounts.get(fluidStack); - int targetAmount = getFilteredFluidAmount(fluidStack); - if (targetAmount <= 0L || targetAmount > presentAmount) + for (int i = 0; i < sourceHandler.getTanks(); ++i) { + FluidStack sourceFluid = sourceHandler.getFluidInTank(i); + if (this.getFilterHandler().isFilterPresent() && + voidingMode == VoidingMode.VOID_OVERFLOW) { + keepAmount = this.getFilterHandler().getFilter() + .getTransferLimit(sourceFluid, maxFluidTransferRate); + } + if (sourceFluid.isEmpty() || sourceFluid.getAmount() == 0 || + !getFilterHandler().test(sourceFluid)) continue; - - var toDrain = fluidStack.copy(); - toDrain.setAmount(presentAmount - targetAmount); - - fluidHandler.drain(toDrain, IFluidHandler.FluidAction.EXECUTE); + sourceFluid.setAmount(sourceFluid.getAmount() - keepAmount); + sourceHandler.drain(sourceFluid, IFluidHandler.FluidAction.EXECUTE); } } diff --git a/src/main/java/com/gregtechceu/gtceu/common/cover/voiding/AdvancedItemVoidingCover.java b/src/main/java/com/gregtechceu/gtceu/common/cover/voiding/AdvancedItemVoidingCover.java index 282b01b3a9..0df099d94d 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/cover/voiding/AdvancedItemVoidingCover.java +++ b/src/main/java/com/gregtechceu/gtceu/common/cover/voiding/AdvancedItemVoidingCover.java @@ -1,11 +1,14 @@ package com.gregtechceu.gtceu.common.cover.voiding; +import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.capability.ICoverable; import com.gregtechceu.gtceu.api.cover.CoverDefinition; import com.gregtechceu.gtceu.api.cover.filter.ItemFilter; import com.gregtechceu.gtceu.api.cover.filter.SimpleItemFilter; import com.gregtechceu.gtceu.api.gui.widget.EnumSelectorWidget; import com.gregtechceu.gtceu.api.gui.widget.IntInputWidget; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRenderer; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRendererBuilder; import com.gregtechceu.gtceu.common.cover.data.VoidingMode; import com.lowdragmc.lowdraglib.gui.widget.WidgetGroup; @@ -44,6 +47,12 @@ public AdvancedItemVoidingCover(CoverDefinition definition, ICoverable coverHold super(definition, coverHolder, attachedSide); } + @Override + protected CoverRenderer buildRenderer() { + return new CoverRendererBuilder(GTCEu.id("block/cover/overlay_item_voiding_advanced"), + GTCEu.id("block/cover/overlay_item_voiding_advanced_emissive")).build(); + } + ////////////////////////////////////////////// // *********** COVER LOGIC ***********// ////////////////////////////////////////////// diff --git a/src/main/java/com/gregtechceu/gtceu/common/cover/voiding/FluidVoidingCover.java b/src/main/java/com/gregtechceu/gtceu/common/cover/voiding/FluidVoidingCover.java index 13cde836d7..9b034aad3e 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/cover/voiding/FluidVoidingCover.java +++ b/src/main/java/com/gregtechceu/gtceu/common/cover/voiding/FluidVoidingCover.java @@ -1,11 +1,14 @@ package com.gregtechceu.gtceu.common.cover.voiding; +import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.capability.ICoverable; import com.gregtechceu.gtceu.api.cover.CoverDefinition; import com.gregtechceu.gtceu.api.gui.GuiTextures; import com.gregtechceu.gtceu.api.gui.widget.ToggleButtonWidget; -import com.gregtechceu.gtceu.api.transfer.fluid.IFluidHandlerModifiable; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRenderer; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRendererBuilder; import com.gregtechceu.gtceu.common.cover.PumpCover; +import com.gregtechceu.gtceu.utils.GTTransferUtils; import com.lowdragmc.lowdraglib.gui.widget.LabelWidget; import com.lowdragmc.lowdraglib.gui.widget.Widget; @@ -15,20 +18,22 @@ import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.core.Direction; +import net.minecraftforge.common.capabilities.ForgeCapabilities; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.capability.IFluidHandler; +import net.minecraftforge.fluids.capability.templates.FluidTank; import lombok.Getter; import org.jetbrains.annotations.NotNull; -import java.util.Map; - import javax.annotation.ParametersAreNonnullByDefault; @MethodsReturnNonnullByDefault @ParametersAreNonnullByDefault public class FluidVoidingCover extends PumpCover { + protected final NullFluidTank nullFluidTank = new NullFluidTank(); + @Persisted @Getter protected boolean isEnabled = false; @@ -37,6 +42,11 @@ public FluidVoidingCover(CoverDefinition definition, ICoverable coverHolder, Dir super(definition, coverHolder, attachedSide, 0); } + @Override + protected CoverRenderer buildRenderer() { + return new CoverRendererBuilder(GTCEu.id("block/cover/overlay_fluid_voiding"), null).build(); + } + @Override protected boolean isSubscriptionActive() { return isWorkingEnabled() && isEnabled(); @@ -47,34 +57,21 @@ protected boolean isSubscriptionActive() { ////////////////////////////////////////////// @Override - protected void update() { - if (coverHolder.getOffsetTimer() % 5 != 0) - return; - - doVoidFluids(); - subscriptionHandler.updateSubscription(); - } - - protected void doVoidFluids() { - IFluidHandlerModifiable fluidHandler = getOwnFluidHandler(); - if (fluidHandler == null) { - return; + public void update() { + if (isWorkingEnabled && coverHolder.getOffsetTimer() % 20 == 0) { + doTransferFluids(); } - voidAny(fluidHandler); } - void voidAny(IFluidHandlerModifiable fluidHandler) { - final Map fluidAmounts = enumerateDistinctFluids(fluidHandler, TransferDirection.EXTRACT); - - for (FluidStack fluidStack : fluidAmounts.keySet()) { - if (!filterHandler.test(fluidStack)) - continue; - - var toDrain = fluidStack.copy(); - toDrain.setAmount(fluidAmounts.get(fluidStack)); - - fluidHandler.drain(toDrain, IFluidHandler.FluidAction.EXECUTE); + protected void doTransferFluids() { + IFluidHandler myFluidHandler = coverHolder.getCapability(ForgeCapabilities.FLUID_HANDLER, + attachedSide).resolve().orElse(null); + if (myFluidHandler == null) { + return; } + GTTransferUtils.transferFluidsFiltered(myFluidHandler, nullFluidTank, + getFilterHandler()::test); + subscriptionHandler.updateSubscription(); } public void setWorkingEnabled(boolean workingEnabled) { @@ -132,4 +129,19 @@ protected void configureFilter() { public ManagedFieldHolder getFieldHolder() { return MANAGED_FIELD_HOLDER; } + + class NullFluidTank extends FluidTank { + + public NullFluidTank() { + super(Integer.MAX_VALUE); + } + + @Override + public int fill(FluidStack resource, FluidAction action) { + if (FluidVoidingCover.this.filterHandler.test(resource)) { + return resource.getAmount(); + } + return 0; + } + } } diff --git a/src/main/java/com/gregtechceu/gtceu/common/cover/voiding/ItemVoidingCover.java b/src/main/java/com/gregtechceu/gtceu/common/cover/voiding/ItemVoidingCover.java index f6f0a9715a..569066b710 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/cover/voiding/ItemVoidingCover.java +++ b/src/main/java/com/gregtechceu/gtceu/common/cover/voiding/ItemVoidingCover.java @@ -1,5 +1,6 @@ package com.gregtechceu.gtceu.common.cover.voiding; +import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.capability.IControllable; import com.gregtechceu.gtceu.api.capability.ICoverable; import com.gregtechceu.gtceu.api.cover.CoverDefinition; @@ -7,6 +8,8 @@ import com.gregtechceu.gtceu.api.cover.filter.ItemFilter; import com.gregtechceu.gtceu.api.gui.GuiTextures; import com.gregtechceu.gtceu.api.gui.widget.ToggleButtonWidget; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRenderer; +import com.gregtechceu.gtceu.client.renderer.pipe.cover.CoverRendererBuilder; import com.gregtechceu.gtceu.common.cover.ConveyorCover; import com.lowdragmc.lowdraglib.gui.widget.LabelWidget; @@ -42,6 +45,17 @@ protected boolean isSubscriptionActive() { return isWorkingEnabled() && isEnabled(); } + @Override + public @NotNull CoverRenderer getRenderer() { + if (renderer == null) renderer = buildRenderer(); + return renderer; + } + + @Override + protected CoverRenderer buildRenderer() { + return new CoverRendererBuilder(GTCEu.id("block/cover/overlay_item_voiding"), null).build(); + } + ////////////////////////////////////////////// // *********** COVER LOGIC ***********// ////////////////////////////////////////////// diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/GTBedrockFluids.java b/src/main/java/com/gregtechceu/gtceu/common/data/GTBedrockFluids.java index 6ed969bb59..8d83864569 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/GTBedrockFluids.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/GTBedrockFluids.java @@ -30,7 +30,7 @@ public class GTBedrockFluids { // ******** OVERWORLD ********// ////////////////////////////////////// public static BedrockFluidDefinition HEAVY_OIL = create(GTCEu.id("heavy_oil_deposit"), builder -> builder - .fluid(GTMaterials.OilHeavy::getFluid) + .fluid(GTMaterials.HeavyOil::getFluid) .weight(15) .yield(100, 200) .depletionAmount(1) @@ -41,7 +41,7 @@ public class GTBedrockFluids { .dimensions(overworld())); public static BedrockFluidDefinition LIGHT_OIL = create(GTCEu.id("light_oil_deposit"), builder -> builder - .fluid(GTMaterials.OilLight::getFluid) + .fluid(GTMaterials.LightOil::getFluid) .weight(25) .yield(175, 300) .depletionAmount(1) diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/GTBlockEntities.java b/src/main/java/com/gregtechceu/gtceu/common/data/GTBlockEntities.java index 78c2f759bf..90deabf05d 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/GTBlockEntities.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/GTBlockEntities.java @@ -1,11 +1,16 @@ package com.gregtechceu.gtceu.common.data; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.ActivablePipeBlockEntity; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.MaterialPipeBlockEntity; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; import com.gregtechceu.gtceu.common.blockentity.*; import net.minecraft.world.level.block.entity.SignBlockEntity; import com.tterrag.registrate.util.entry.BlockEntityEntry; -import com.tterrag.registrate.util.entry.BlockEntry; +import com.tterrag.registrate.util.nullness.NonNullSupplier; + +import java.util.stream.Stream; import static com.gregtechceu.gtceu.common.registry.GTRegistration.REGISTRATE; @@ -14,44 +19,24 @@ * @date 2023/2/13 * @implNote GTBlockEntities */ +@SuppressWarnings("unchecked") public class GTBlockEntities { - @SuppressWarnings("unchecked") - public static final BlockEntityEntry CABLE = REGISTRATE - .blockEntity("cable", CableBlockEntity::create) - .onRegister(CableBlockEntity::onBlockEntityRegister) - .validBlocks(GTMaterialBlocks.CABLE_BLOCKS.values().toArray(BlockEntry[]::new)) - .register(); - - @SuppressWarnings("unchecked") - public static final BlockEntityEntry FLUID_PIPE = REGISTRATE - .blockEntity("fluid_pipe", FluidPipeBlockEntity::new) - .onRegister(FluidPipeBlockEntity::onBlockEntityRegister) - .validBlocks(GTMaterialBlocks.FLUID_PIPE_BLOCKS.values().toArray(BlockEntry[]::new)) - .register(); - - @SuppressWarnings("unchecked") - public static final BlockEntityEntry ITEM_PIPE = REGISTRATE - .blockEntity("item_pipe", ItemPipeBlockEntity::create) - .onRegister(ItemPipeBlockEntity::onBlockEntityRegister) - .validBlocks(GTMaterialBlocks.ITEM_PIPE_BLOCKS.values().toArray(BlockEntry[]::new)) - .register(); - - public static final BlockEntityEntry LASER_PIPE = REGISTRATE - .blockEntity("laser_pipe", LaserPipeBlockEntity::create) - .onRegister(LaserPipeBlockEntity::onBlockEntityRegister) - .validBlocks(GTBlocks.LASER_PIPES) + public static final BlockEntityEntry PIPE = REGISTRATE + .blockEntity("pipe", PipeBlockEntity::new) + .validBlocks(GTBlocks.DUCT_PIPE_BLOCKS.values().toArray(NonNullSupplier[]::new)) .register(); - public static final BlockEntityEntry OPTICAL_PIPE = REGISTRATE - .blockEntity("optical_pipe", OpticalPipeBlockEntity::new) - .validBlocks(GTBlocks.OPTICAL_PIPES) + public static final BlockEntityEntry ACTIVATABLE_PIPE = REGISTRATE + .blockEntity("activatable_pipe", ActivablePipeBlockEntity::new) + .validBlocks(Stream.of(GTBlocks.OPTICAL_PIPE, GTBlocks.LASER_PIPE).toArray(NonNullSupplier[]::new)) .register(); - public static final BlockEntityEntry DUCT_PIPE = REGISTRATE - .blockEntity("duct_pipe", DuctPipeBlockEntity::create) - .onRegister(DuctPipeBlockEntity::onBlockEntityRegister) - .validBlocks(GTBlocks.DUCT_PIPES) + public static final BlockEntityEntry MATERIAL_PIPE = REGISTRATE + .blockEntity("material_pipe", MaterialPipeBlockEntity::new) + .validBlocks(Stream.concat(GTMaterialBlocks.CABLE_BLOCKS.values().stream(), + GTMaterialBlocks.MATERIAL_PIPE_BLOCKS.values().stream()) + .toArray(NonNullSupplier[]::new)) .register(); public static final BlockEntityEntry GT_SIGN = REGISTRATE diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/GTBlocks.java b/src/main/java/com/gregtechceu/gtceu/common/data/GTBlocks.java index b51a9b26cf..59c4180266 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/GTBlocks.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/GTBlocks.java @@ -12,6 +12,9 @@ import com.gregtechceu.gtceu.api.data.chemical.material.stack.UnificationEntry; import com.gregtechceu.gtceu.api.data.tag.TagPrefix; import com.gregtechceu.gtceu.api.data.tag.TagUtil; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.PipeStructureRegistry; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.block.PipeBlockItem; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.block.PipeMaterialBlock; import com.gregtechceu.gtceu.api.item.*; import com.gregtechceu.gtceu.api.item.tool.GTToolType; import com.gregtechceu.gtceu.api.machine.multiblock.IBatteryData; @@ -19,11 +22,14 @@ import com.gregtechceu.gtceu.common.block.*; import com.gregtechceu.gtceu.common.block.explosive.IndustrialTNTBlock; import com.gregtechceu.gtceu.common.block.explosive.PowderbarrelBlock; -import com.gregtechceu.gtceu.common.pipelike.duct.DuctPipeType; -import com.gregtechceu.gtceu.common.pipelike.fluidpipe.longdistance.LDFluidPipeType; -import com.gregtechceu.gtceu.common.pipelike.item.longdistance.LDItemPipeType; -import com.gregtechceu.gtceu.common.pipelike.laser.LaserPipeType; -import com.gregtechceu.gtceu.common.pipelike.optical.OpticalPipeType; +import com.gregtechceu.gtceu.common.pipelike.block.duct.DuctPipeBlock; +import com.gregtechceu.gtceu.common.pipelike.block.duct.DuctStructure; +import com.gregtechceu.gtceu.common.pipelike.block.laser.LaserPipeBlock; +import com.gregtechceu.gtceu.common.pipelike.block.laser.LaserStructure; +import com.gregtechceu.gtceu.common.pipelike.block.optical.OpticalPipeBlock; +import com.gregtechceu.gtceu.common.pipelike.block.optical.OpticalStructure; +import com.gregtechceu.gtceu.common.pipelike.longdistance.fluid.LDFluidPipeType; +import com.gregtechceu.gtceu.common.pipelike.longdistance.item.LDItemPipeType; import com.gregtechceu.gtceu.core.mixins.BlockPropertiesAccessor; import com.gregtechceu.gtceu.data.recipe.CustomTags; import com.gregtechceu.gtceu.utils.SupplierMemoizer; @@ -96,10 +102,11 @@ public class GTBlocks { ////////////////////////////////////// // ***** Procedural Blocks *****// + ////////////////////////////////////// - public static final BlockEntry[] LASER_PIPES = new BlockEntry[LaserPipeType.values().length]; - public static final BlockEntry[] OPTICAL_PIPES = new BlockEntry[OpticalPipeType.values().length]; - public static final BlockEntry[] DUCT_PIPES = new BlockEntry[DuctPipeType.VALUES.length]; + private static ImmutableMap.Builder> DUCT_PIPE_BLOCKS_BUILDER = ImmutableMap + .builder(); + public static Map> DUCT_PIPE_BLOCKS; ////////////////////////////////////// // Pipes Blocks // @@ -108,86 +115,73 @@ public class GTBlocks { REGISTRATE.creativeModeTab(() -> GTCreativeModeTabs.MATERIAL_PIPE); } - // Laser Pipe Blocks - private static void generateLaserPipeBlocks() { - GTCEu.LOGGER.debug("Generating GTCEu Laser Pipe Blocks..."); - for (int i = 0; i < LaserPipeType.values().length; ++i) { - registerLaserPipeBlock(i); - } - GTCEu.LOGGER.debug("Generating GTCEu Laser Pipe Blocks... Complete!"); - } - - private static void registerLaserPipeBlock(int index) { - var type = LaserPipeType.values()[index]; - var entry = REGISTRATE - .block("%s_laser_pipe".formatted(type.getSerializedName()), (p) -> new LaserPipeBlock(p, type)) - .initialProperties(() -> Blocks.IRON_BLOCK) - .properties(p -> p.dynamicShape().noOcclusion().forceSolidOn()) - .blockstate(NonNullBiConsumer.noop()) - .defaultLoot() - .tag(GTToolType.WIRE_CUTTER.harvestTags.get(0)) - .addLayer(() -> RenderType::cutoutMipped) - .color(() -> LaserPipeBlock::tintedColor) - .item(LaserPipeBlockItem::new) - .model(NonNullBiConsumer.noop()) - .color(() -> LaserPipeBlockItem::tintColor) - .build() - .register(); - LASER_PIPES[index] = entry; - } - - // Optical Pipe Blocks - private static void generateOpticalPipeBlocks() { - GTCEu.LOGGER.debug("Generating GTCEu Optical Pipe Blocks..."); - for (int i = 0; i < OpticalPipeType.values().length; ++i) { - registerOpticalPipeBlock(i); - } - GTCEu.LOGGER.debug("Generating GTCEu Optical Pipe Blocks... Complete!"); - } - - private static void registerOpticalPipeBlock(int index) { - var type = OpticalPipeType.values()[index]; - var entry = REGISTRATE - .block("%s_optical_pipe".formatted(type.getSerializedName()), (p) -> new OpticalPipeBlock(p, type)) - .lang("Optical Fiber Cable") - .initialProperties(() -> Blocks.IRON_BLOCK) - .properties(p -> p.dynamicShape().noOcclusion().forceSolidOn()) - .blockstate(NonNullBiConsumer.noop()) - .defaultLoot() - .tag(GTToolType.WIRE_CUTTER.harvestTags.get(0)) - .addLayer(() -> RenderType::cutoutMipped) - .color(() -> OpticalPipeBlock::tintedColor) - .item(OpticalPipeBlockItem::new) - .model(NonNullBiConsumer.noop()) - .build() - .register(); - OPTICAL_PIPES[index] = entry; - } + public static final BlockEntry LASER_PIPE = REGISTRATE + .block("laser_pipe", (p) -> new LaserPipeBlock(p, LaserStructure.NORMAL)) + .initialProperties(() -> Blocks.IRON_BLOCK) + .properties(p -> p.dynamicShape().noOcclusion().forceSolidOn()) + .blockstate(NonNullBiConsumer.noop()) + .defaultLoot() + .tag(GTToolType.WIRE_CUTTER.harvestTags.get(0)) + .addLayer(() -> RenderType::cutoutMipped) + .color(() -> PipeMaterialBlock::tintedColor) + .item(PipeBlockItem::new) + .model(NonNullBiConsumer.noop()) + .color(() -> () -> (stack, color) -> -1) + .build() + .register(); + public static final BlockEntry LASER_REFLECTOR_PIPE = REGISTRATE + .block("laser_reflector_pipe", (p) -> new LaserPipeBlock(p, LaserStructure.MIRROR)) + .initialProperties(() -> Blocks.IRON_BLOCK) + .properties(p -> p.dynamicShape().noOcclusion().forceSolidOn()) + .blockstate(NonNullBiConsumer.noop()) + .defaultLoot() + .tag(GTToolType.WIRE_CUTTER.harvestTags.get(0)) + .addLayer(() -> RenderType::cutoutMipped) + .color(() -> PipeMaterialBlock::tintedColor) + .item(PipeBlockItem::new) + .model(NonNullBiConsumer.noop()) + .color(() -> () -> (stack, color) -> -1) + .build() + .register(); + public static final BlockEntry OPTICAL_PIPE = REGISTRATE + .block("optical_fiber_cable", (p) -> new OpticalPipeBlock(p, OpticalStructure.INSTANCE)) + .initialProperties(() -> Blocks.IRON_BLOCK) + .properties(p -> p.dynamicShape().noOcclusion().forceSolidOn()) + .blockstate(NonNullBiConsumer.noop()) + .defaultLoot() + .tag(GTToolType.WIRE_CUTTER.harvestTags.get(0)) + .color(() -> PipeMaterialBlock::tintedColor) + .addLayer(() -> RenderType::cutoutMipped) + .item(PipeBlockItem::new) + .model(NonNullBiConsumer.noop()) + .color(() -> () -> (stack, color) -> -1) + .build() + .register(); // Duct Pipe Blocks private static void generateDuctPipeBlocks() { GTCEu.LOGGER.debug("Generating GTCEu Duct Pipe Blocks..."); - for (int i = 0; i < DuctPipeType.VALUES.length; ++i) { - registerDuctPipeBlock(i); + for (var structure : PipeStructureRegistry.getStructures(DuctStructure.class)) { + registerDuctPipeBlock(structure); } + DUCT_PIPE_BLOCKS = DUCT_PIPE_BLOCKS_BUILDER.build(); GTCEu.LOGGER.debug("Generating GTCEu Duct Pipe Blocks... Complete!"); } - private static void registerDuctPipeBlock(int index) { - var type = DuctPipeType.VALUES[index]; + private static void registerDuctPipeBlock(DuctStructure structure) { var entry = REGISTRATE - .block("%s_duct_pipe".formatted(type.getSerializedName()), (p) -> new DuctPipeBlock(p, type)) + .block("%s_duct_pipe".formatted(structure.getSerializedName()), (p) -> new DuctPipeBlock(p, structure)) .initialProperties(() -> Blocks.IRON_BLOCK) .properties(p -> p.dynamicShape().noOcclusion().forceSolidOn()) .blockstate(NonNullBiConsumer.noop()) .defaultLoot() .tag(GTToolType.WRENCH.harvestTags.get(0)) .addLayer(() -> RenderType::cutoutMipped) - .item(DuctPipeBlockItem::new) + .item(PipeBlockItem::new) .model(NonNullBiConsumer.noop()) .build() .register(); - DUCT_PIPES[index] = entry; + DUCT_PIPE_BLOCKS_BUILDER.put(structure, entry); } // Long Distance Item Pipe Blocks @@ -1398,19 +1392,16 @@ public static void init() { // Procedural Pipes/Wires REGISTRATE.creativeModeTab(() -> GTCreativeModeTabs.MATERIAL_PIPE); - GTMaterialBlocks.generateCableBlocks(); // Cable & Wire Blocks - GTMaterialBlocks.generateFluidPipeBlocks(); // Fluid Pipe Blocks - GTMaterialBlocks.generateItemPipeBlocks(); // Item Pipe Blocks - generateLaserPipeBlocks(); // Laser Pipe Blocks - generateOpticalPipeBlocks(); // Optical Pipe Blocks - generateDuctPipeBlocks(); // Duct Pipe Blocks + GTMaterialBlocks.generateCableBlocks(); // Cable & Wire Blocks + GTMaterialBlocks.generateMaterialPipeBlocks(); // Material Pipe Blocks + generateDuctPipeBlocks(); // Duct Pipe Blocks // Remove Builder Tables GTMaterialBlocks.MATERIAL_BLOCKS_BUILDER = null; GTMaterialBlocks.SURFACE_ROCK_BLOCKS_BUILDER = null; GTMaterialBlocks.CABLE_BLOCKS_BUILDER = null; - GTMaterialBlocks.FLUID_PIPE_BLOCKS_BUILDER = null; - GTMaterialBlocks.ITEM_PIPE_BLOCKS_BUILDER = null; + GTMaterialBlocks.MATERIAL_PIPE_BLOCKS_BUILDER = null; + DUCT_PIPE_BLOCKS_BUILDER = null; // GCYM GCYMBlocks.init(); diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/GTCovers.java b/src/main/java/com/gregtechceu/gtceu/common/data/GTCovers.java index 36fb5c432c..2a87047323 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/GTCovers.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/GTCovers.java @@ -7,7 +7,6 @@ import com.gregtechceu.gtceu.api.addon.IGTAddon; import com.gregtechceu.gtceu.api.cover.CoverDefinition; import com.gregtechceu.gtceu.api.registry.GTRegistries; -import com.gregtechceu.gtceu.client.renderer.cover.*; import com.gregtechceu.gtceu.common.cover.*; import com.gregtechceu.gtceu.common.cover.detector.*; import com.gregtechceu.gtceu.common.cover.voiding.AdvancedFluidVoidingCover; @@ -17,8 +16,6 @@ import net.minecraftforge.fml.ModLoader; -import it.unimi.dsi.fastutil.ints.Int2ObjectFunction; - import java.util.Arrays; import java.util.Locale; @@ -38,126 +35,60 @@ public class GTCovers { GTRegistries.COVERS.unfreeze(); } - public final static CoverDefinition FACADE = register( - "facade", FacadeCover::new, - FacadeCoverRenderer.INSTANCE); - - public final static CoverDefinition ITEM_FILTER = register( - "item_filter", ItemFilterCover::new, - new SimpleCoverRenderer(GTCEu.id("block/cover/overlay_item_filter"))); - - public final static CoverDefinition FLUID_FILTER = register( - "fluid_filter", FluidFilterCover::new, - new SimpleCoverRenderer(GTCEu.id("block/cover/overlay_fluid_filter"))); - - public final static CoverDefinition INFINITE_WATER = register( - "infinite_water", InfiniteWaterCover::new, - new SimpleCoverRenderer(GTCEu.id("block/cover/overlay_infinite_water"))); - - public final static CoverDefinition SHUTTER = register( - "shutter", ShutterCover::new, - new SimpleCoverRenderer(GTCEu.id("block/cover/overlay_shutter"))); - public final static CoverDefinition COVER_STORAGE = register( - "storage", StorageCover::new, - new SimpleCoverRenderer(GTCEu.id("block/cover/storage_cover"))); - - public final static CoverDefinition[] CONVEYORS = registerTiered( - "conveyor", ConveyorCover::new, - tier -> ConveyorCoverRenderer.INSTANCE, ALL_TIERS); - - public final static CoverDefinition[] ROBOT_ARMS = registerTiered( - "robot_arm", RobotArmCover::new, - tier -> RobotArmCoverRenderer.INSTANCE, ALL_TIERS); - - public final static CoverDefinition[] PUMPS = registerTiered( - "pump", PumpCover::new, - tier -> PumpCoverRenderer.INSTANCE, ALL_TIERS); - - public final static CoverDefinition[] FLUID_REGULATORS = registerTiered( - "fluid_regulator", FluidRegulatorCover::new, - tier -> FluidRegulatorCoverRenderer.INSTANCE, ALL_TIERS); - - public final static CoverDefinition COMPUTER_MONITOR = register( - "computer_monitor", ComputerMonitorCover::new, - new SimpleCoverRenderer(GTCEu.id("block/cover/overlay_display"))); - - public final static CoverDefinition MACHINE_CONTROLLER = register( - "machine_controller", MachineControllerCover::new, - new SimpleCoverRenderer(GTCEu.id("block/cover/overlay_controller"))); + public final static CoverDefinition FACADE = register("facade", FacadeCover::new); + public final static CoverDefinition ITEM_FILTER = register("item_filter", ItemFilterCover::new); + public final static CoverDefinition FLUID_FILTER = register("fluid_filter", FluidFilterCover::new); + public final static CoverDefinition INFINITE_WATER = register("infinite_water", InfiniteWaterCover::new); + public final static CoverDefinition SHUTTER = register("shutter", ShutterCover::new); + public final static CoverDefinition STORAGE = register("storage", StorageCover::new); + public final static CoverDefinition[] CONVEYORS = registerTiered("conveyor", ConveyorCover::new, ALL_TIERS); + public final static CoverDefinition[] ROBOT_ARMS = registerTiered("robot_arm", RobotArmCover::new, ALL_TIERS); + public final static CoverDefinition[] PUMPS = registerTiered("pump", PumpCover::new, ALL_TIERS); + public final static CoverDefinition[] FLUID_REGULATORS = registerTiered("fluid_regulator", FluidRegulatorCover::new, + ALL_TIERS); + + public final static CoverDefinition COMPUTER_MONITOR = register("computer_monitor", ComputerMonitorCover::new); + public final static CoverDefinition MACHINE_CONTROLLER = register("machine_controller", + MachineControllerCover::new); // Voiding - public final static CoverDefinition ITEM_VOIDING = register( - "item_voiding", ItemVoidingCover::new, - new SimpleCoverRenderer(GTCEu.id("block/cover/overlay_item_voiding"))); - public final static CoverDefinition ITEM_VOIDING_ADVANCED = register( - "item_voiding_advanced", AdvancedItemVoidingCover::new, - new SimpleCoverRenderer(GTCEu.id("block/cover/overlay_item_voiding_advanced"))); - public final static CoverDefinition FLUID_VOIDING = register( - "fluid_voiding", FluidVoidingCover::new, - new SimpleCoverRenderer(GTCEu.id("block/cover/overlay_fluid_voiding"))); - public final static CoverDefinition FLUID_VOIDING_ADVANCED = register( - "fluid_voiding_advanced", AdvancedFluidVoidingCover::new, - new SimpleCoverRenderer(GTCEu.id("block/cover/overlay_fluid_voiding_advanced"))); + public final static CoverDefinition ITEM_VOIDING = register("item_voiding", ItemVoidingCover::new); + public final static CoverDefinition ITEM_VOIDING_ADVANCED = register("item_voiding_advanced", + AdvancedItemVoidingCover::new); + public final static CoverDefinition FLUID_VOIDING = register("fluid_voiding", FluidVoidingCover::new); + public final static CoverDefinition FLUID_VOIDING_ADVANCED = register("fluid_voiding_advanced", + AdvancedFluidVoidingCover::new); // Detectors - public final static CoverDefinition ACTIVITY_DETECTOR = register( - "activity_detector", ActivityDetectorCover::new, - new SimpleCoverRenderer(GTCEu.id("block/cover/overlay_activity_detector"))); - public final static CoverDefinition ACTIVITY_DETECTOR_ADVANCED = register( - "activity_detector_advanced", AdvancedActivityDetectorCover::new, - new SimpleCoverRenderer(GTCEu.id("block/cover/overlay_activity_detector_advanced"))); - public final static CoverDefinition FLUID_DETECTOR = register( - "fluid_detector", FluidDetectorCover::new, - new SimpleCoverRenderer(GTCEu.id("block/cover/overlay_fluid_detector"))); - public final static CoverDefinition FLUID_DETECTOR_ADVANCED = register( - "fluid_detector_advanced", AdvancedFluidDetectorCover::new, - new SimpleCoverRenderer(GTCEu.id("block/cover/overlay_fluid_detector_advanced"))); - public final static CoverDefinition ITEM_DETECTOR = register( - "item_detector", ItemDetectorCover::new, - new SimpleCoverRenderer(GTCEu.id("block/cover/overlay_item_detector"))); - public final static CoverDefinition ITEM_DETECTOR_ADVANCED = register( - "item_detector_advanced", AdvancedItemDetectorCover::new, - new SimpleCoverRenderer(GTCEu.id("block/cover/overlay_item_detector_advanced"))); - public final static CoverDefinition ENERGY_DETECTOR = register( - "energy_detector", EnergyDetectorCover::new, - new SimpleCoverRenderer(GTCEu.id("block/cover/overlay_energy_detector"))); - public final static CoverDefinition ENERGY_DETECTOR_ADVANCED = register( - "energy_detector_advanced", AdvancedEnergyDetectorCover::new, - new SimpleCoverRenderer(GTCEu.id("block/cover/overlay_energy_detector_advanced"))); - public final static CoverDefinition MAINTENANCE_DETECTOR = register( - "maintenance_detector", MaintenanceDetectorCover::new, - new SimpleCoverRenderer(GTCEu.id("block/cover/overlay_maintenance_detector"))); + public final static CoverDefinition ACTIVITY_DETECTOR = register("activity_detector", ActivityDetectorCover::new); + public final static CoverDefinition ACTIVITY_DETECTOR_ADVANCED = register("activity_detector_advanced", + AdvancedActivityDetectorCover::new); + public final static CoverDefinition FLUID_DETECTOR = register("fluid_detector", FluidDetectorCover::new); + public final static CoverDefinition FLUID_DETECTOR_ADVANCED = register("fluid_detector_advanced", + AdvancedFluidDetectorCover::new); + public final static CoverDefinition ITEM_DETECTOR = register("item_detector", ItemDetectorCover::new); + public final static CoverDefinition ITEM_DETECTOR_ADVANCED = register("item_detector_advanced", + AdvancedItemDetectorCover::new); + public final static CoverDefinition ENERGY_DETECTOR = register("energy_detector", EnergyDetectorCover::new); + public final static CoverDefinition ENERGY_DETECTOR_ADVANCED = register("energy_detector_advanced", + AdvancedEnergyDetectorCover::new); + public final static CoverDefinition MAINTENANCE_DETECTOR = register("maintenance_detector", + MaintenanceDetectorCover::new); // Solar Panels - public final static CoverDefinition[] SOLAR_PANEL = registerTiered( - "solar_panel", CoverSolarPanel::new, - tier -> new SimpleCoverRenderer(GTCEu.id("block/cover/overlay_solar_panel")), ALL_TIERS_WITH_ULV); + public final static CoverDefinition[] SOLAR_PANEL = registerTiered("solar_panel", CoverSolarPanel::new, + ALL_TIERS_WITH_ULV); /////////////////////////////////////////////// // *********** UTIL METHODS ***********// /////////////////////////////////////////////// public static CoverDefinition register(String id, CoverDefinition.CoverBehaviourProvider behaviorCreator) { - return register(id, behaviorCreator, new SimpleCoverRenderer(GTCEu.id("block/cover/" + id))); - } - - public static CoverDefinition register(String id, CoverDefinition.CoverBehaviourProvider behaviorCreator, - ICoverRenderer coverRenderer) { - var definition = new CoverDefinition(GTCEu.id(id), behaviorCreator, coverRenderer); + var definition = new CoverDefinition(GTCEu.id(id), behaviorCreator); GTRegistries.COVERS.register(GTCEu.id(id), definition); return definition; } - public static CoverDefinition[] registerTiered(String id, - CoverDefinition.TieredCoverBehaviourProvider behaviorCreator, - Int2ObjectFunction coverRenderer, int... tiers) { - return Arrays.stream(tiers).mapToObj(tier -> { - var name = id + "." + GTValues.VN[tier].toLowerCase(Locale.ROOT); - return register(name, (def, coverable, side) -> behaviorCreator.create(def, coverable, side, tier), - coverRenderer.apply(tier)); - }).toArray(CoverDefinition[]::new); - } - public static CoverDefinition[] registerTiered(String id, CoverDefinition.TieredCoverBehaviourProvider behaviorCreator, int... tiers) { diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/GTCreativeModeTabs.java b/src/main/java/com/gregtechceu/gtceu/common/data/GTCreativeModeTabs.java index 11935bc1e3..351f07d9ca 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/GTCreativeModeTabs.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/GTCreativeModeTabs.java @@ -10,7 +10,6 @@ import com.gregtechceu.gtceu.api.item.tool.GTToolType; import com.gregtechceu.gtceu.api.item.tool.ToolHelper; import com.gregtechceu.gtceu.api.registry.registrate.GTRegistrate; -import com.gregtechceu.gtceu.common.pipelike.cable.Insulation; import net.minecraft.core.NonNullList; import net.minecraft.core.registries.Registries; @@ -50,7 +49,7 @@ public class GTCreativeModeTabs { .register(); public static RegistryEntry MATERIAL_PIPE = REGISTRATE.defaultCreativeTab("material_pipe", builder -> builder.displayItems(new RegistrateDisplayItemsGenerator("material_pipe", REGISTRATE)) - .icon(() -> ChemicalHelper.get(Insulation.WIRE_DOUBLE.getTagPrefix(), GTMaterials.Copper)) + .icon(() -> ChemicalHelper.get(TagPrefix.wireGtDouble, GTMaterials.Copper)) .title(REGISTRATE.addLang("itemGroup", GTCEu.id("material_pipe"), GTCEu.NAME + " Material Pipes")) .build()) .register(); diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/GTItems.java b/src/main/java/com/gregtechceu/gtceu/common/data/GTItems.java index decd93b956..ec4656a6d8 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/GTItems.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/GTItems.java @@ -27,6 +27,7 @@ import com.gregtechceu.gtceu.common.item.armor.*; import com.gregtechceu.gtceu.common.item.tool.behavior.LighterBehavior; import com.gregtechceu.gtceu.common.item.tool.behavior.MetaMachineConfigCopyBehaviour; +import com.gregtechceu.gtceu.common.pipelike.handlers.properties.MaterialFluidProperties; import com.gregtechceu.gtceu.config.ConfigHolder; import com.gregtechceu.gtceu.data.lang.LangHandler; import com.gregtechceu.gtceu.data.recipe.CustomTags; @@ -388,7 +389,7 @@ public Component getItemName(ItemStack stack) { .setData(ProviderType.ITEM_MODEL, NonNullBiConsumer.noop()) .color(() -> GTItems::cellColor) .onRegister(attach( - ThermalFluidStats.create(FluidType.BUCKET_VOLUME, 1800, true, false, false, false, false), + ThermalFluidStats.create(FluidType.BUCKET_VOLUME, 1800, 121, false, false, false, false), new ItemFluidContainer(), cellName())) .register(); public static ItemEntry FLUID_CELL_UNIVERSAL = REGISTRATE @@ -397,7 +398,7 @@ public Component getItemName(ItemStack stack) { .color(() -> GTItems::cellColor) .setData(ProviderType.ITEM_MODEL, NonNullBiConsumer.noop()) .onRegister(attach(cellName(), - ThermalFluidStats.create(FluidType.BUCKET_VOLUME, 1800, true, false, false, false, true), + ThermalFluidStats.create(FluidType.BUCKET_VOLUME, 1800, 121, false, false, false, true), new ItemFluidContainer())) .register(); public static ItemEntry FLUID_CELL_LARGE_STEEL = REGISTRATE @@ -407,8 +408,11 @@ public Component getItemName(ItemStack stack) { .setData(ProviderType.ITEM_MODEL, NonNullBiConsumer.noop()) .onRegister(attach(cellName(), ThermalFluidStats.create(FluidType.BUCKET_VOLUME * 8, - GTMaterials.Steel.getProperty(PropertyKey.FLUID_PIPE).getMaxFluidTemperature(), true, false, - false, false, true), + GTMaterials.Steel.getProperty(PropertyKey.PIPENET_PROPERTIES) + .getProperty(MaterialFluidProperties.KEY).getMaxFluidTemperature(), + GTMaterials.Steel.getProperty(PropertyKey.PIPENET_PROPERTIES) + .getProperty(MaterialFluidProperties.KEY).getMinFluidTemperature(), + false, false, false, true), new ItemFluidContainer())) .onRegister(materialInfo(new ItemMaterialInfo(new MaterialStack(GTMaterials.Steel, GTValues.M * 4)))) .register(); @@ -419,7 +423,10 @@ public Component getItemName(ItemStack stack) { .setData(ProviderType.ITEM_MODEL, NonNullBiConsumer.noop()) .onRegister(attach(cellName(), ThermalFluidStats.create(FluidType.BUCKET_VOLUME * 32, - GTMaterials.Aluminium.getProperty(PropertyKey.FLUID_PIPE).getMaxFluidTemperature(), true, + GTMaterials.Aluminium.getProperty(PropertyKey.PIPENET_PROPERTIES) + .getProperty(MaterialFluidProperties.KEY).getMaxFluidTemperature(), + GTMaterials.Aluminium.getProperty(PropertyKey.PIPENET_PROPERTIES) + .getProperty(MaterialFluidProperties.KEY).getMinFluidTemperature(), false, false, false, true), new ItemFluidContainer())) .onRegister(materialInfo(new ItemMaterialInfo(new MaterialStack(GTMaterials.Aluminium, GTValues.M * 4)))) @@ -431,8 +438,11 @@ public Component getItemName(ItemStack stack) { .setData(ProviderType.ITEM_MODEL, NonNullBiConsumer.noop()) .onRegister(attach(cellName(), ThermalFluidStats.create(FluidType.BUCKET_VOLUME * 64, - GTMaterials.StainlessSteel.getProperty(PropertyKey.FLUID_PIPE).getMaxFluidTemperature(), - true, false, false, false, true), + GTMaterials.StainlessSteel.getProperty(PropertyKey.PIPENET_PROPERTIES) + .getProperty(MaterialFluidProperties.KEY).getMaxFluidTemperature(), + GTMaterials.StainlessSteel.getProperty(PropertyKey.PIPENET_PROPERTIES) + .getProperty(MaterialFluidProperties.KEY).getMinFluidTemperature(), + false, false, false, true), new ItemFluidContainer())) .onRegister( materialInfo(new ItemMaterialInfo(new MaterialStack(GTMaterials.StainlessSteel, GTValues.M * 6)))) @@ -444,7 +454,10 @@ public Component getItemName(ItemStack stack) { .setData(ProviderType.ITEM_MODEL, NonNullBiConsumer.noop()) .onRegister(attach(cellName(), ThermalFluidStats.create(FluidType.BUCKET_VOLUME * 128, - GTMaterials.Titanium.getProperty(PropertyKey.FLUID_PIPE).getMaxFluidTemperature(), true, + GTMaterials.Titanium.getProperty(PropertyKey.PIPENET_PROPERTIES) + .getProperty(MaterialFluidProperties.KEY).getMaxFluidTemperature(), + GTMaterials.Titanium.getProperty(PropertyKey.PIPENET_PROPERTIES) + .getProperty(MaterialFluidProperties.KEY).getMinFluidTemperature(), false, false, false, true), new ItemFluidContainer())) .onRegister(materialInfo(new ItemMaterialInfo(new MaterialStack(GTMaterials.Titanium, GTValues.M * 6)))) @@ -457,8 +470,11 @@ public Component getItemName(ItemStack stack) { .properties(p -> p.stacksTo(32)) .onRegister(attach(cellName(), ThermalFluidStats.create(FluidType.BUCKET_VOLUME * 512, - GTMaterials.TungstenSteel.getProperty(PropertyKey.FLUID_PIPE).getMaxFluidTemperature(), - true, false, false, false, true), + GTMaterials.TungstenSteel.getProperty(PropertyKey.PIPENET_PROPERTIES) + .getProperty(MaterialFluidProperties.KEY).getMaxFluidTemperature(), + GTMaterials.TungstenSteel.getProperty(PropertyKey.PIPENET_PROPERTIES) + .getProperty(MaterialFluidProperties.KEY).getMinFluidTemperature(), + false, false, false, true), new ItemFluidContainer())) .onRegister( materialInfo(new ItemMaterialInfo(new MaterialStack(GTMaterials.TungstenSteel, GTValues.M * 8)))) @@ -469,7 +485,7 @@ public Component getItemName(ItemStack stack) { .setData(ProviderType.ITEM_MODEL, NonNullBiConsumer.noop()) .onRegister( attach(cellName(), - ThermalFluidStats.create(FluidType.BUCKET_VOLUME, 1200, false, true, false, false, + ThermalFluidStats.create(FluidType.BUCKET_VOLUME, 1200, 213, true, false, false, true), new ItemFluidContainer())) .onRegister(materialInfo(new ItemMaterialInfo(new MaterialStack(GTMaterials.Glass, GTValues.M / 4)))) @@ -1939,7 +1955,7 @@ public Component getItemName(ItemStack stack) { public static ItemEntry COVER_STORAGE = REGISTRATE .item("storage_cover", ComponentItem::create) .lang("Storage Cover") - .onRegister(attach(new CoverPlaceBehavior(GTCovers.COVER_STORAGE))) + .onRegister(attach(new CoverPlaceBehavior(GTCovers.STORAGE))) .register(); public static ItemEntry COVER_SHUTTER = REGISTRATE .item("shutter_module_cover", ComponentItem::create) @@ -2515,7 +2531,7 @@ public Component getItemName(ItemStack stack) { .tag(Tags.Items.ARMORS_CHESTPLATES) .register(); public static ItemEntry NANO_CHESTPLATE_ADVANCED = REGISTRATE - .item("avanced_nanomuscle_chestplate", + .item("advanced_nanomuscle_chestplate", (p) -> new ArmorComponentItem(GTArmorMaterials.ARMOR, ArmorItem.Type.CHESTPLATE, p) .setArmorLogic(new AdvancedNanoMuscleSuite(512, 12_800_000L * (long) Math.max(1, @@ -2559,8 +2575,9 @@ public Component getItemName(ItemStack stack) { public static ItemEntry FERTILIZER = REGISTRATE.item("fertilizer", ComponentItem::create) .onRegister(attach(new FertilizerBehavior())).register(); - public static ItemEntry BLACKLIGHT = REGISTRATE.item("blacklight", Item::new) - .register(); + public static ItemEntry BLACKLIGHT = REGISTRATE.item("blacklight", Item::new).register(); + + public static ItemEntry LASER_REFLECTOR = REGISTRATE.item("dielectric_laser_mirror", Item::new).register(); public static ItemEntry RUBBER_BOAT = REGISTRATE .item("rubber_boat", p -> new GTBoatItem(false, GTBoat.BoatType.RUBBER, new Item.Properties())) diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/GTMachines.java b/src/main/java/com/gregtechceu/gtceu/common/data/GTMachines.java index e4291c6076..7b4d7a48ce 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/GTMachines.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/GTMachines.java @@ -7,10 +7,8 @@ import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.capability.recipe.ItemRecipeCapability; import com.gregtechceu.gtceu.api.data.RotationState; -import com.gregtechceu.gtceu.api.machine.MachineDefinition; -import com.gregtechceu.gtceu.api.machine.SimpleTieredMachine; -import com.gregtechceu.gtceu.api.machine.multiblock.CleanroomType; -import com.gregtechceu.gtceu.api.machine.multiblock.PartAbility; +import com.gregtechceu.gtceu.api.machine.*; +import com.gregtechceu.gtceu.api.machine.multiblock.*; import com.gregtechceu.gtceu.api.machine.steam.SimpleSteamMachine; import com.gregtechceu.gtceu.api.machine.steam.SteamBoilerMachine; import com.gregtechceu.gtceu.api.registry.GTRegistries; @@ -24,8 +22,8 @@ import com.gregtechceu.gtceu.common.machine.steam.SteamSolarBoiler; import com.gregtechceu.gtceu.common.machine.steam.SteamSolidBoilerMachine; import com.gregtechceu.gtceu.common.machine.storage.*; -import com.gregtechceu.gtceu.common.pipelike.fluidpipe.longdistance.LDFluidEndpointMachine; -import com.gregtechceu.gtceu.common.pipelike.item.longdistance.LDItemEndpointMachine; +import com.gregtechceu.gtceu.common.pipelike.longdistance.fluid.LDFluidEndpointMachine; +import com.gregtechceu.gtceu.common.pipelike.longdistance.item.LDItemEndpointMachine; import com.gregtechceu.gtceu.config.ConfigHolder; import com.gregtechceu.gtceu.data.lang.LangHandler; import com.gregtechceu.gtceu.integration.kjs.GTRegistryInfo; @@ -1058,17 +1056,17 @@ public class GTMachines { GTValues.tiersBetween(HV, GTCEuAPI.isHighTier() ? OpV : UV)); public static final MachineDefinition[] LASER_INPUT_HATCH_256 = registerLaserHatch(IN, 256, - PartAbility.INPUT_LASER); + PartAbility.LASER_RECEPTION); public static final MachineDefinition[] LASER_OUTPUT_HATCH_256 = registerLaserHatch(OUT, 256, - PartAbility.OUTPUT_LASER); + PartAbility.LASER_TRANSMISSION); public static final MachineDefinition[] LASER_INPUT_HATCH_1024 = registerLaserHatch(IN, 1024, - PartAbility.INPUT_LASER); + PartAbility.LASER_RECEPTION); public static final MachineDefinition[] LASER_OUTPUT_HATCH_1024 = registerLaserHatch(OUT, 1024, - PartAbility.OUTPUT_LASER); + PartAbility.LASER_TRANSMISSION); public static final MachineDefinition[] LASER_INPUT_HATCH_4096 = registerLaserHatch(IN, 4096, - PartAbility.INPUT_LASER); + PartAbility.LASER_RECEPTION); public static final MachineDefinition[] LASER_OUTPUT_HATCH_4096 = registerLaserHatch(OUT, 4096, - PartAbility.OUTPUT_LASER); + PartAbility.LASER_TRANSMISSION); public static void init() { GTMultiMachines.init(); diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/GTMaterialBlocks.java b/src/main/java/com/gregtechceu/gtceu/common/data/GTMaterialBlocks.java index 8c02d62b09..bbcad149ba 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/GTMaterialBlocks.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/GTMaterialBlocks.java @@ -3,20 +3,22 @@ import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.GTCEuAPI; import com.gregtechceu.gtceu.api.block.MaterialBlock; -import com.gregtechceu.gtceu.api.block.MaterialPipeBlock; import com.gregtechceu.gtceu.api.block.OreBlock; import com.gregtechceu.gtceu.api.data.chemical.material.Material; import com.gregtechceu.gtceu.api.data.chemical.material.properties.PropertyKey; import com.gregtechceu.gtceu.api.data.chemical.material.registry.MaterialRegistry; import com.gregtechceu.gtceu.api.data.tag.TagPrefix; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.PipeStructureRegistry; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.block.MaterialPipeBlockItem; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.block.PipeMaterialBlock; import com.gregtechceu.gtceu.api.item.MaterialBlockItem; -import com.gregtechceu.gtceu.api.item.MaterialPipeBlockItem; import com.gregtechceu.gtceu.api.item.SurfaceRockBlockItem; import com.gregtechceu.gtceu.api.registry.registrate.GTRegistrate; import com.gregtechceu.gtceu.common.block.*; -import com.gregtechceu.gtceu.common.pipelike.cable.Insulation; -import com.gregtechceu.gtceu.common.pipelike.fluidpipe.FluidPipeType; -import com.gregtechceu.gtceu.common.pipelike.item.ItemPipeType; +import com.gregtechceu.gtceu.common.pipelike.block.cable.CableBlock; +import com.gregtechceu.gtceu.common.pipelike.block.cable.CableStructure; +import com.gregtechceu.gtceu.common.pipelike.block.pipe.MaterialPipeBlock; +import com.gregtechceu.gtceu.common.pipelike.block.pipe.MaterialPipeStructure; import com.gregtechceu.gtceu.utils.FormattingUtil; import net.minecraft.client.renderer.RenderType; @@ -40,17 +42,14 @@ public class GTMaterialBlocks { .builder(); static ImmutableTable.Builder> CABLE_BLOCKS_BUILDER = ImmutableTable .builder(); - static ImmutableTable.Builder> FLUID_PIPE_BLOCKS_BUILDER = ImmutableTable - .builder(); - static ImmutableTable.Builder> ITEM_PIPE_BLOCKS_BUILDER = ImmutableTable + static ImmutableTable.Builder> MATERIAL_PIPE_BLOCKS_BUILDER = ImmutableTable .builder(); // Reference Tables public static Table> MATERIAL_BLOCKS; public static Map> SURFACE_ROCK_BLOCKS; public static Table> CABLE_BLOCKS; - public static Table> FLUID_PIPE_BLOCKS; - public static Table> ITEM_PIPE_BLOCKS; + public static Table> MATERIAL_PIPE_BLOCKS; // Material Blocks public static void generateMaterialBlocks() { @@ -179,15 +178,15 @@ private static void registerOreIndicator(Material material, GTRegistrate registr SURFACE_ROCK_BLOCKS_BUILDER.put(material, entry); } - // Material Cable & Wire Blocks + // Cable/Wire Blocks public static void generateCableBlocks() { GTCEu.LOGGER.debug("Generating GTCEu Cable/Wire Blocks..."); - for (Insulation insulation : Insulation.values()) { + for (CableStructure structure : PipeStructureRegistry.getStructures(CableStructure.class)) { for (MaterialRegistry registry : GTCEuAPI.materialManager.getRegistries()) { GTRegistrate registrate = registry.getRegistrate(); for (Material material : registry.getAllMaterials()) { - if (allowCableBlock(material, insulation)) { - registerCableBlock(material, insulation, registrate); + if (allowCableBlock(material, structure)) { + registerCableBlock(material, structure, registrate); } } } @@ -196,103 +195,61 @@ public static void generateCableBlocks() { GTCEu.LOGGER.debug("Generating GTCEu Cable/Wire Blocks... Complete!"); } - private static boolean allowCableBlock(Material material, Insulation insulation) { - return material.hasProperty(PropertyKey.WIRE) && !insulation.tagPrefix.isIgnored(material) && - !(insulation.isCable && material.getProperty(PropertyKey.WIRE).isSuperconductor()); - } - - private static void registerCableBlock(Material material, Insulation insulation, GTRegistrate registrate) { - var entry = registrate - .block("%s_%s".formatted(material.getName(), insulation.name), - p -> new CableBlock(p, insulation, material)) - .initialProperties(() -> Blocks.IRON_BLOCK) - .properties(p -> p.dynamicShape().noOcclusion().noLootTable().forceSolidOn()) - .transform(GTBlocks.unificationBlock(insulation.tagPrefix, material)) - .blockstate(NonNullBiConsumer.noop()) - .setData(ProviderType.LANG, NonNullBiConsumer.noop()) - .setData(ProviderType.LOOT, NonNullBiConsumer.noop()) - .addLayer(() -> RenderType::cutoutMipped) - .color(() -> MaterialPipeBlock::tintedColor) - .item(MaterialPipeBlockItem::new) - .model(NonNullBiConsumer.noop()) - .color(() -> MaterialPipeBlockItem::tintColor) - .build() - .register(); - CABLE_BLOCKS_BUILDER.put(insulation.tagPrefix, material, entry); - } - - // Material Fluid Pipe Blocks - public static void generateFluidPipeBlocks() { - GTCEu.LOGGER.debug("Generating GTCEu Fluid Pipe Blocks..."); - for (var fluidPipeType : FluidPipeType.values()) { - for (MaterialRegistry registry : GTCEuAPI.materialManager.getRegistries()) { - GTRegistrate registrate = registry.getRegistrate(); - for (Material material : registry.getAllMaterials()) { - if (allowFluidPipeBlock(material, fluidPipeType)) { - registerFluidPipeBlock(material, fluidPipeType, registrate); - } - } - } + private static boolean allowCableBlock(Material material, CableStructure insulation) { + if (material.hasProperty(PropertyKey.PIPENET_PROPERTIES)) { + return material.getProperty(PropertyKey.PIPENET_PROPERTIES).generatesStructure(insulation); } - FLUID_PIPE_BLOCKS = FLUID_PIPE_BLOCKS_BUILDER.build(); - GTCEu.LOGGER.debug("Generating GTCEu Fluid Pipe Blocks... Complete!"); - } - - private static boolean allowFluidPipeBlock(Material material, FluidPipeType fluidPipeType) { - return material.hasProperty(PropertyKey.FLUID_PIPE) && !fluidPipeType.tagPrefix.isIgnored(material); + return false; } - private static void registerFluidPipeBlock(Material material, FluidPipeType fluidPipeType, - GTRegistrate registrate) { + private static void registerCableBlock(Material material, CableStructure structure, GTRegistrate registrate) { var entry = registrate - .block("%s_%s_fluid_pipe".formatted(material.getName(), fluidPipeType.name), - p -> new FluidPipeBlock(p, fluidPipeType, material)) + .block("%s_%s".formatted(material.getName(), structure.name()), + p -> new CableBlock(p, structure, material)) .initialProperties(() -> Blocks.IRON_BLOCK) - .properties(p -> { - if (GTBlocks.doMetalPipe(material)) { - p.sound(GTSoundTypes.METAL_PIPE); - } - return p.dynamicShape().noOcclusion().noLootTable().forceSolidOn(); - }) - .transform(GTBlocks.unificationBlock(fluidPipeType.tagPrefix, material)) + .properties(p -> p.dynamicShape().noOcclusion().noLootTable().forceSolidOn()) + .transform(GTBlocks.unificationBlock(structure.prefix(), material)) .blockstate(NonNullBiConsumer.noop()) .setData(ProviderType.LANG, NonNullBiConsumer.noop()) .setData(ProviderType.LOOT, NonNullBiConsumer.noop()) .addLayer(() -> RenderType::cutoutMipped) - .color(() -> MaterialPipeBlock::tintedColor) + .color(() -> PipeMaterialBlock::tintedColor) .item(MaterialPipeBlockItem::new) .model(NonNullBiConsumer.noop()) .color(() -> MaterialPipeBlockItem::tintColor) .build() .register(); - FLUID_PIPE_BLOCKS_BUILDER.put(fluidPipeType.tagPrefix, material, entry); + CABLE_BLOCKS_BUILDER.put(structure.prefix(), material, entry); } - // Material Item Pipe Blocks - public static void generateItemPipeBlocks() { - GTCEu.LOGGER.debug("Generating GTCEu Item Pipe Blocks..."); - for (var itemPipeType : ItemPipeType.values()) { + // Material Pipe Blocks + public static void generateMaterialPipeBlocks() { + GTCEu.LOGGER.debug("Generating GTCEu Material Pipe Blocks..."); + for (var structure : PipeStructureRegistry.getStructures(MaterialPipeStructure.class)) { for (MaterialRegistry registry : GTCEuAPI.materialManager.getRegistries()) { GTRegistrate registrate = registry.getRegistrate(); for (Material material : registry.getAllMaterials()) { - if (allowItemPipeBlock(material, itemPipeType)) { - registerItemPipeBlock(material, itemPipeType, registrate); + if (allowMaterialPipeBlock(material, structure)) { + registerMaterialPipeBlock(material, structure, registrate); } } } } - ITEM_PIPE_BLOCKS = ITEM_PIPE_BLOCKS_BUILDER.build(); - GTCEu.LOGGER.debug("Generating GTCEu Item Pipe Blocks... Complete!"); + MATERIAL_PIPE_BLOCKS = MATERIAL_PIPE_BLOCKS_BUILDER.build(); + GTCEu.LOGGER.debug("Generating GTCEu Material Pipe Blocks... Complete!"); } - private static boolean allowItemPipeBlock(Material material, ItemPipeType itemPipeType) { - return material.hasProperty(PropertyKey.ITEM_PIPE) && !itemPipeType.getTagPrefix().isIgnored(material); + private static boolean allowMaterialPipeBlock(Material material, MaterialPipeStructure pipeType) { + return material.hasProperty(PropertyKey.PIPENET_PROPERTIES) && + material.getProperty(PropertyKey.PIPENET_PROPERTIES).generatesStructure(pipeType); } - private static void registerItemPipeBlock(Material material, ItemPipeType itemPipeType, GTRegistrate registrate) { + private static void registerMaterialPipeBlock(Material material, MaterialPipeStructure pipeType, + GTRegistrate registrate) { var entry = registrate - .block("%s_%s_item_pipe".formatted(material.getName(), itemPipeType.name), - p -> new ItemPipeBlock(p, itemPipeType, material)) + .block("%s_%s_pipe".formatted(pipeType.name(), material.getName()), + p -> new com.gregtechceu.gtceu.common.pipelike.block.pipe.MaterialPipeBlock(p, pipeType, + material)) .initialProperties(() -> Blocks.IRON_BLOCK) .properties(p -> { if (GTBlocks.doMetalPipe(material)) { @@ -300,17 +257,17 @@ private static void registerItemPipeBlock(Material material, ItemPipeType itemPi } return p.dynamicShape().noOcclusion().noLootTable().forceSolidOn(); }) - .transform(GTBlocks.unificationBlock(itemPipeType.getTagPrefix(), material)) + .transform(GTBlocks.unificationBlock(pipeType.prefix(), material)) .blockstate(NonNullBiConsumer.noop()) .setData(ProviderType.LANG, NonNullBiConsumer.noop()) .setData(ProviderType.LOOT, NonNullBiConsumer.noop()) .addLayer(() -> RenderType::cutoutMipped) - .color(() -> MaterialPipeBlock::tintedColor) + .color(() -> PipeMaterialBlock::tintedColor) .item(MaterialPipeBlockItem::new) .model(NonNullBiConsumer.noop()) .color(() -> MaterialPipeBlockItem::tintColor) .build() .register(); - ITEM_PIPE_BLOCKS_BUILDER.put(itemPipeType.getTagPrefix(), material, entry); + MATERIAL_PIPE_BLOCKS_BUILDER.put(pipeType.prefix(), material, entry); } } diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/GTMaterials.java b/src/main/java/com/gregtechceu/gtceu/common/data/GTMaterials.java index b6ff2a27c3..61c31043ca 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/GTMaterials.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/GTMaterials.java @@ -165,7 +165,7 @@ public static void init() { rock.setIgnored(Marble, SupplierMemoizer.memoizeBlockSupplier(() -> GTBlocks.MARBLE.get())); rock.setIgnored(Granite, Blocks.GRANITE); rock.setIgnored(Granite, Blocks.POLISHED_GRANITE); - rock.setIgnored(GraniteRed, SupplierMemoizer.memoizeBlockSupplier(() -> GTBlocks.RED_GRANITE.get())); + rock.setIgnored(RedGranite, SupplierMemoizer.memoizeBlockSupplier(() -> GTBlocks.RED_GRANITE.get())); rock.setIgnored(Andesite, Blocks.ANDESITE); rock.setIgnored(Andesite, Blocks.POLISHED_ANDESITE); rock.setIgnored(Diorite, Blocks.DIORITE); @@ -199,14 +199,14 @@ public static void init() { toolHeadWireCutter .addSecondaryMaterial(new MaterialStack(Steel, ring.materialAmount() + screw.materialAmount() * 2)); - pipeTinyFluid.setIgnored(Wood); - pipeHugeFluid.setIgnored(Wood); - pipeQuadrupleFluid.setIgnored(Wood); - pipeNonupleFluid.setIgnored(Wood); - pipeTinyFluid.setIgnored(TreatedWood); - pipeHugeFluid.setIgnored(TreatedWood); - pipeQuadrupleFluid.setIgnored(TreatedWood); - pipeNonupleFluid.setIgnored(TreatedWood); + pipeTiny.setIgnored(Wood); + pipeHuge.setIgnored(Wood); + pipeQuadruple.setIgnored(Wood); + pipeNonuple.setIgnored(Wood); + pipeTiny.setIgnored(TreatedWood); + pipeHuge.setIgnored(TreatedWood); + pipeQuadruple.setIgnored(TreatedWood); + pipeNonuple.setIgnored(TreatedWood); pipeSmallRestrictive.addSecondaryMaterial(new MaterialStack(Iron, ring.materialAmount() * 2)); pipeNormalRestrictive.addSecondaryMaterial(new MaterialStack(Iron, ring.materialAmount() * 2)); @@ -222,7 +222,6 @@ public static void init() { plateDouble.setIgnored(BorosilicateGlass); plateDouble.setIgnored(Wood); plateDouble.setIgnored(TreatedWood); - plate.setIgnored(BorosilicateGlass); foil.setIgnored(BorosilicateGlass); dustSmall.setIgnored(Lapotron); @@ -778,9 +777,9 @@ private static void excludeAllGemsButNormal(Material material) { public static Material ConstructionFoam; public static Material Oil; - public static Material OilHeavy; + public static Material HeavyOil; public static Material RawOil; - public static Material OilLight; + public static Material LightOil; public static Material NaturalGas; public static Material SulfuricHeavyFuel; public static Material HeavyFuel; @@ -911,7 +910,7 @@ private static void excludeAllGemsButNormal(Material material) { public static Material GarnetYellow; public static Material Marble; public static Material Deepslate; - public static Material GraniteRed; + public static Material RedGranite; public static Material Blackstone; public static Material VanadiumMagnetite; public static Material QuartzSand; diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/GTModels.java b/src/main/java/com/gregtechceu/gtceu/common/data/GTModels.java index 49cd489948..2bfc45ea6c 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/GTModels.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/GTModels.java @@ -3,21 +3,37 @@ import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.GTCEuAPI; import com.gregtechceu.gtceu.api.block.ActiveBlock; +import com.gregtechceu.gtceu.api.block.BlockProperties; import com.gregtechceu.gtceu.api.block.ICoilType; import com.gregtechceu.gtceu.api.block.IFilterType; import com.gregtechceu.gtceu.api.block.IFusionCasingType; import com.gregtechceu.gtceu.api.data.chemical.material.info.MaterialIconSet; +import com.gregtechceu.gtceu.api.data.chemical.material.properties.PipeNetProperties; import com.gregtechceu.gtceu.api.data.chemical.material.properties.PropertyKey; import com.gregtechceu.gtceu.api.fluids.GTFluid; import com.gregtechceu.gtceu.api.fluids.store.FluidStorage; import com.gregtechceu.gtceu.api.fluids.store.FluidStorageKey; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.PipeStructureRegistry; import com.gregtechceu.gtceu.api.machine.multiblock.IBatteryData; import com.gregtechceu.gtceu.common.block.*; +import com.gregtechceu.gtceu.common.block.LampBlock; +import com.gregtechceu.gtceu.common.pipelike.block.cable.CableBlock; +import com.gregtechceu.gtceu.common.pipelike.block.cable.CableStructure; +import com.gregtechceu.gtceu.common.pipelike.block.duct.DuctPipeBlock; +import com.gregtechceu.gtceu.common.pipelike.block.duct.DuctStructure; +import com.gregtechceu.gtceu.common.pipelike.block.laser.LaserPipeBlock; +import com.gregtechceu.gtceu.common.pipelike.block.laser.LaserStructure; +import com.gregtechceu.gtceu.common.pipelike.block.optical.OpticalPipeBlock; +import com.gregtechceu.gtceu.common.pipelike.block.optical.OpticalStructure; +import com.gregtechceu.gtceu.common.pipelike.block.pipe.MaterialPipeBlock; +import com.gregtechceu.gtceu.common.pipelike.block.pipe.MaterialPipeStructure; +import com.gregtechceu.gtceu.common.pipelike.handlers.properties.MaterialEnergyProperties; import com.gregtechceu.gtceu.core.MixinHelpers; import com.gregtechceu.gtceu.data.pack.GTDynamicResourcePack; import net.minecraft.client.Minecraft; import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.data.models.blockstates.*; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.GsonHelper; import net.minecraft.world.item.BlockItem; @@ -359,4 +375,114 @@ public static void registerMaterialFluidModels() { } } } + + public static void registerPipeModels() { + for (var material : GTCEuAPI.materialManager.getRegisteredMaterials()) { + PipeNetProperties properties = material.getProperty(PropertyKey.PIPENET_PROPERTIES); + if (properties == null) continue; + if (properties.hasProperty(MaterialEnergyProperties.KEY)) { + for (var structure : PipeStructureRegistry.getStructures(CableStructure.class)) { + if (!GTMaterialBlocks.CABLE_BLOCKS.contains(structure.prefix(), material)) { + continue; + } + + JsonObject json = new JsonObject(); + json.addProperty("loader", "gtceu:pipe"); + String modelId = structure.getModel().getLoc().toString(); + json.addProperty("model_id", modelId); + + CableBlock block = GTMaterialBlocks.CABLE_BLOCKS.get(structure.prefix(), material).get(); + ResourceLocation blockId = GTMaterialBlocks.CABLE_BLOCKS.get(structure.prefix(), material).getId(); + ResourceLocation blockModelId = blockId.withPrefix("block/"); + GTDynamicResourcePack.addBlockModel(blockModelId, json); + GTDynamicResourcePack.addItemModel(blockId, json); + + createPipeBlockState(blockId, blockModelId, block); + } + } + for (var structure : PipeStructureRegistry.getStructures(MaterialPipeStructure.class)) { + if (!GTMaterialBlocks.MATERIAL_PIPE_BLOCKS.contains(structure.prefix(), material)) { + continue; + } + + JsonObject json = new JsonObject(); + json.addProperty("loader", "gtceu:pipe"); + String modelId = structure.getModel().getLoc().toString(); + json.addProperty("model_id", modelId); + + MaterialPipeBlock block = GTMaterialBlocks.MATERIAL_PIPE_BLOCKS.get(structure.prefix(), material).get(); + ResourceLocation blockId = GTMaterialBlocks.MATERIAL_PIPE_BLOCKS.get(structure.prefix(), material) + .getId(); + ResourceLocation blockModelId = blockId.withPrefix("block/"); + GTDynamicResourcePack.addBlockModel(blockModelId, json); + GTDynamicResourcePack.addItemModel(blockId, json); + + createPipeBlockState(blockId, blockModelId, block); + } + } + for (var structure : PipeStructureRegistry.getStructures(DuctStructure.class)) { + JsonObject json = new JsonObject(); + json.addProperty("loader", "gtceu:pipe"); + String modelId = structure.getModel().getLoc().toString(); + json.addProperty("model_id", modelId); + + DuctPipeBlock block = GTBlocks.DUCT_PIPE_BLOCKS.get(structure).get(); + ResourceLocation blockId = GTBlocks.DUCT_PIPE_BLOCKS.get(structure).getId(); + ResourceLocation blockModelId = blockId.withPrefix("block/"); + GTDynamicResourcePack.addBlockModel(blockModelId, json); + GTDynamicResourcePack.addItemModel(blockId, json); + + createPipeBlockState(blockId, blockModelId, block); + } + { + JsonObject json = new JsonObject(); + json.addProperty("loader", "gtceu:pipe"); + String modelId = LaserStructure.NORMAL.getModel().getLoc().toString(); + json.addProperty("model_id", modelId); + + LaserPipeBlock block = GTBlocks.LASER_PIPE.get(); + ResourceLocation blockId = GTBlocks.LASER_PIPE.getId(); + ResourceLocation blockModelId = blockId.withPrefix("block/"); + GTDynamicResourcePack.addBlockModel(blockModelId, json); + GTDynamicResourcePack.addItemModel(blockId, json); + + createPipeBlockState(blockId, blockModelId, block); + } + { + JsonObject json = new JsonObject(); + json.addProperty("loader", "gtceu:pipe"); + String modelId = LaserStructure.MIRROR.getModel().getLoc().toString(); + json.addProperty("model_id", modelId); + + LaserPipeBlock block = GTBlocks.LASER_REFLECTOR_PIPE.get(); + ResourceLocation blockId = GTBlocks.LASER_REFLECTOR_PIPE.getId(); + ResourceLocation blockModelId = blockId.withPrefix("block/"); + GTDynamicResourcePack.addBlockModel(blockModelId, json); + GTDynamicResourcePack.addItemModel(blockId, json); + + createPipeBlockState(blockId, blockModelId, block); + } + { + JsonObject json = new JsonObject(); + json.addProperty("loader", "gtceu:pipe"); + String modelId = OpticalStructure.INSTANCE.getModel().getLoc().toString(); + json.addProperty("model_id", modelId); + + OpticalPipeBlock block = GTBlocks.OPTICAL_PIPE.get(); + ResourceLocation blockId = GTBlocks.OPTICAL_PIPE.getId(); + ResourceLocation blockModelId = blockId.withPrefix("block/"); + GTDynamicResourcePack.addBlockModel(blockModelId, json); + GTDynamicResourcePack.addItemModel(blockId, json); + + createPipeBlockState(blockId, blockModelId, block); + } + } + + private static void createPipeBlockState(ResourceLocation blockId, ResourceLocation blockModelId, Block block) { + Variant variant = Variant.variant().with(VariantProperties.MODEL, blockModelId); + GTDynamicResourcePack.addBlockState(blockId, MultiVariantGenerator.multiVariant(block) + .with(PropertyDispatch.property(BlockProperties.SERVER_TICK) + .select(true, variant) + .select(false, variant))); + } } diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/GTRecipeCapabilities.java b/src/main/java/com/gregtechceu/gtceu/common/data/GTRecipeCapabilities.java index 9a054413a4..554e6d4298 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/GTRecipeCapabilities.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/GTRecipeCapabilities.java @@ -22,7 +22,7 @@ public class GTRecipeCapabilities { public final static RecipeCapability FLUID = FluidRecipeCapability.CAP; public final static RecipeCapability BLOCK_STATE = BlockStateRecipeCapability.CAP; public final static RecipeCapability EU = EURecipeCapability.CAP; - public final static RecipeCapability CWU = CWURecipeCapability.CAP; + public final static RecipeCapability CWU = CWURecipeCapability.CAP; public static void init() { GTRegistries.RECIPE_CAPABILITIES.unfreeze(); diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/datafixer/GTDataFixers.java b/src/main/java/com/gregtechceu/gtceu/common/data/datafixer/GTDataFixers.java new file mode 100644 index 0000000000..cd7f05860b --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/data/datafixer/GTDataFixers.java @@ -0,0 +1,125 @@ +package com.gregtechceu.gtceu.common.data.datafixer; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.GTCEuAPI; +import com.gregtechceu.gtceu.api.datafixer.DataFixesInternals; +import com.gregtechceu.gtceu.common.datafixer.fixes.ActivablePipeConnectionFix; +import com.gregtechceu.gtceu.common.datafixer.fixes.OilVariantsRenameFix; +import com.gregtechceu.gtceu.common.datafixer.fixes.PipeConnectionFix; +import com.gregtechceu.gtceu.common.datafixer.schemas.V2; +import com.gregtechceu.gtceu.config.ConfigHolder; + +import net.minecraft.util.datafix.DataFixTypes; +import net.minecraft.util.datafix.fixes.*; +import net.minecraft.util.datafix.schemas.NamespacedSchema; + +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import com.mojang.datafixers.DataFixer; +import com.mojang.datafixers.DataFixerBuilder; +import com.mojang.datafixers.schemas.Schema; + +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.function.BiFunction; +import java.util.function.UnaryOperator; +import java.util.regex.Pattern; + +import static com.gregtechceu.gtceu.api.datafixer.DataFixesInternals.BASE_SCHEMA; + +public class GTDataFixers { + + private static final BiFunction SAME_NAMESPACED = NamespacedSchema::new; + + public static void init() { + if (!ConfigHolder.INSTANCE.compat.doDatafixers) { + return; + } + + GTCEu.LOGGER.info("Registering data fixers"); + + DataFixesInternals api = DataFixesInternals.get(); + + DataFixerBuilder builder = new DataFixerBuilder(GTCEuAPI.GT_DATA_VERSION); + addFixers(builder); + + ExecutorService executor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder() + .setNameFormat("GTM Datafixer Bootstrap").setDaemon(true).setPriority(1).build()); + DataFixer result = builder.buildOptimized(DataFixTypes.TYPES_FOR_LEVEL_LIST, executor); + api.registerFixer(GTCEuAPI.GT_DATA_VERSION, result); + } + + public static void addFixers(DataFixerBuilder builder) { + Schema schemaV0 = builder.addSchema(0, BASE_SCHEMA); + builder.addFixer(new AddNewChoices(schemaV0, "Added GT block entities", References.BLOCK_ENTITY)); + + Schema schemaV1 = builder.addSchema(1, SAME_NAMESPACED); + builder.addFixer(ItemRenameFix.create(schemaV1, "advanced_nanomuscle_chestplate rename fix", + createRenamer("gtceu:avanced_nanomuscle_chestplate", "gtceu:advanced_nanomuscle_chestplate"))); + + UnaryOperator renamer = createRenamer(Pattern.compile("gtceu:uranium_"), + "gtceu:uranium_238_"); + builder.addFixer(ItemRenameFix.create(schemaV1, "U238 item rename fix", renamer)); + builder.addFixer(BlockRenameFix.create(schemaV1, "U238 block rename fix", renamer)); + + renamer = createRenamer(Pattern.compile("gtceu:plutonium_"), "gtceu:plutonium_239_"); + builder.addFixer(BlockRenameFix.create(schemaV1, "Pu239 block rename fix", renamer)); + builder.addFixer(ItemRenameFix.create(schemaV1, "Pu239 item rename fix", renamer)); + + renamer = createRenamer(Pattern.compile("gtceu:granite_red"), "gtceu:red_granite"); + builder.addFixer(ItemRenameFix.create(schemaV1, "Red granite item rename fix", renamer)); + builder.addFixer(BlockRenameFix.create(schemaV1, "Red granite block rename fix", renamer)); + + builder.addFixer(ItemRenameFix.create(schemaV1, "Raw oil bucket rename fix", + createRenamer(OilVariantsRenameFix.RENAMED_ITEM_IDS))); + builder.addFixer(BlockRenameFix.create(schemaV1, "Raw oil block rename fix", + createRenamer(OilVariantsRenameFix.RENAMED_BLOCK_IDS))); + + Schema schemaV2 = builder.addSchema(2, V2::new); + builder.addFixer(new PipeConnectionFix(schemaV2, false, "gtceu:cable")); + builder.addFixer(new PipeConnectionFix(schemaV2, false, "gtceu:fluid_pipe")); + builder.addFixer(new PipeConnectionFix(schemaV2, false, "gtceu:item_pipe")); + builder.addFixer(new PipeConnectionFix(schemaV2, false, "gtceu:duct_pipe")); + builder.addFixer(new PipeConnectionFix(schemaV2, false, "gtceu:laser_pipe")); + builder.addFixer(new ActivablePipeConnectionFix(schemaV2, false, "gtceu:optical_pipe")); + builder.addFixer(new AddNewChoices(schemaV2, "Added generic pipe block entities", References.BLOCK_ENTITY)); + + renamer = createRenamer(Pattern.compile("([a-z]+_*[a-z]*)_([_\\w]+?)_item_pipe"), "$2_$1_pipe"); + builder.addFixer(ItemRenameFix.create(schemaV2, "Item pipe rename fix", renamer)); + builder.addFixer(BlockRenameFix.create(schemaV2, "Item pipe rename fix", renamer)); + + renamer = createRenamer(Pattern.compile("([a-z]+_*[a-z]*)_([_\\w]+?)_fluid_pipe"), "$2_$1_pipe"); + builder.addFixer(ItemRenameFix.create(schemaV2, "Fluid pipe rename fix", renamer)); + builder.addFixer(BlockRenameFix.create(schemaV2, "Fluid pipe rename fix", renamer)); + + renamer = createRenamer("gtceu:normal_optical_pipe", "gtceu:optical_fiber_cable"); + builder.addFixer(ItemRenameFix.create(schemaV2, "Optical cable rename fix", renamer)); + builder.addFixer(BlockRenameFix.create(schemaV2, "Optical cable rename fix", renamer)); + + renamer = createRenamer("gtceu:normal_laser_pipe", "gtceu:laser_pipe"); + builder.addFixer(ItemRenameFix.create(schemaV2, "Laser cable rename fix", renamer)); + builder.addFixer(BlockRenameFix.create(schemaV2, "Laser cable rename fix", renamer)); + + builder.addFixer(BlockEntityRenameFix.create(schemaV2, "Pipe block entity rename fix", + createRenamer(Map.of( + "gtceu:cable", "gtceu:material_pipe", + "gtceu:fluid_pipe", "gtceu:material_pipe", + "gtceu:item_pipe", "gtceu:material_pipe", + "gtceu:laser_pipe", "gtceu:activable_pipe", + "gtceu:optical_pipe", "gtceu:activable_pipe", + "gtceu:duct_pipe", "gtceu:pipe")))); + } + + private static UnaryOperator createRenamer(String pOldName, String pNewName) { + return id -> Objects.equals(NamespacedSchema.ensureNamespaced(id), pOldName) ? pNewName : id; + } + + private static UnaryOperator createRenamer(Map pRenameMap) { + return id -> pRenameMap.getOrDefault(NamespacedSchema.ensureNamespaced(id), id); + } + + private static UnaryOperator createRenamer(Pattern check, String replaceWith) { + return id -> check.matcher(NamespacedSchema.ensureNamespaced(id)).replaceAll(replaceWith); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/datafixer/GTReferences.java b/src/main/java/com/gregtechceu/gtceu/common/data/datafixer/GTReferences.java new file mode 100644 index 0000000000..0255fe5f9e --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/data/datafixer/GTReferences.java @@ -0,0 +1,25 @@ +package com.gregtechceu.gtceu.common.data.datafixer; + +import com.mojang.datafixers.DSL; + +public class GTReferences { + + public static final DSL.TypeReference MATERIAL_NAME = reference("material_name"); + public static final DSL.TypeReference COVER_NAME = reference("cover_name"); + public static final DSL.TypeReference COVER = reference("cover"); + + public static DSL.TypeReference reference(final String pName) { + return new DSL.TypeReference() { + + @Override + public String typeName() { + return pName; + } + + @Override + public String toString() { + return "@" + pName; + } + }; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTMachineUtils.java b/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTMachineUtils.java index 194495cebb..278655bea7 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTMachineUtils.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTMachineUtils.java @@ -11,7 +11,6 @@ import com.gregtechceu.gtceu.api.capability.recipe.ItemRecipeCapability; import com.gregtechceu.gtceu.api.data.RotationState; import com.gregtechceu.gtceu.api.data.chemical.material.Material; -import com.gregtechceu.gtceu.api.data.chemical.material.properties.FluidPipeProperties; import com.gregtechceu.gtceu.api.data.chemical.material.properties.PropertyKey; import com.gregtechceu.gtceu.api.data.medicalcondition.MedicalCondition; import com.gregtechceu.gtceu.api.data.tag.TagPrefix; @@ -46,6 +45,7 @@ import com.gregtechceu.gtceu.common.machine.multiblock.steam.LargeBoilerMachine; import com.gregtechceu.gtceu.common.machine.storage.CrateMachine; import com.gregtechceu.gtceu.common.machine.storage.DrumMachine; +import com.gregtechceu.gtceu.common.pipelike.handlers.properties.MaterialFluidProperties; import com.gregtechceu.gtceu.config.ConfigHolder; import com.gregtechceu.gtceu.utils.FormattingUtil; @@ -396,9 +396,12 @@ public static MachineDefinition registerDrum(Material material, int capacity, St () -> new MachineRenderer(GTCEu.id("block/machine/" + (wooden ? "wooden" : "metal") + "_drum"))) .tooltipBuilder((stack, list) -> { TANK_TOOLTIPS.accept(stack, list); - if (material.hasProperty(PropertyKey.FLUID_PIPE)) { - FluidPipeProperties pipeprops = material.getProperty(PropertyKey.FLUID_PIPE); - pipeprops.appendTooltips(list, false, true); + if (material.hasProperty(PropertyKey.PIPENET_PROPERTIES) && + material.getProperty(PropertyKey.PIPENET_PROPERTIES) + .hasProperty(MaterialFluidProperties.KEY)) { + MaterialFluidProperties pipeprops = material.getProperty(PropertyKey.PIPENET_PROPERTIES) + .getProperty(MaterialFluidProperties.KEY); + pipeprops.appendTooltips(list); } }) .tooltips(Component.translatable("gtceu.machine.quantum_tank.tooltip"), diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTMultiMachines.java b/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTMultiMachines.java index 7734e21e61..5b286ec87c 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTMultiMachines.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTMultiMachines.java @@ -991,9 +991,9 @@ public class GTMultiMachines { .setMinGlobalLimited(PowerSubstationMachine.MIN_CASINGS) .or(autoAbilities(true, false, false)) .or(abilities(PartAbility.INPUT_ENERGY, PartAbility.SUBSTATION_INPUT_ENERGY, - PartAbility.INPUT_LASER).setMinGlobalLimited(1)) + PartAbility.LASER_RECEPTION).setMinGlobalLimited(1)) .or(abilities(PartAbility.OUTPUT_ENERGY, PartAbility.SUBSTATION_OUTPUT_ENERGY, - PartAbility.OUTPUT_LASER).setMinGlobalLimited(1))) + PartAbility.LASER_TRANSMISSION).setMinGlobalLimited(1))) .where('G', blocks(CASING_LAMINATED_GLASS.get())) .where('B', Predicates.powerSubstationBatteries()) .build()) @@ -1098,7 +1098,7 @@ public class GTMultiMachines { public static final MultiblockMachineDefinition WOODEN_MULTIBLOCK_TANK = registerMultiblockTank( "wooden_multiblock_tank", "Wooden Multiblock Tank", 250 * 1000, CASING_WOOD_WALL, WOODEN_TANK_VALVE::getBlock, - new PropertyFluidFilter(340, false, false, false, false), + new PropertyFluidFilter(340, 121, false, false, false), (builder, overlay) -> builder.sidedWorkableCasingRenderer("block/casings/wood_wall", overlay)); public static final MachineDefinition STEEL_TANK_VALVE = GTMachineUtils.registerTankValve( diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTResearchMachines.java b/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTResearchMachines.java index 9a7016caff..a00bbf6915 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTResearchMachines.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/machines/GTResearchMachines.java @@ -21,9 +21,9 @@ import com.gregtechceu.gtceu.common.machine.multiblock.electric.research.HPCAMachine; import com.gregtechceu.gtceu.common.machine.multiblock.electric.research.NetworkSwitchMachine; import com.gregtechceu.gtceu.common.machine.multiblock.electric.research.ResearchStationMachine; +import com.gregtechceu.gtceu.common.machine.multiblock.part.ComputationHatchMachine; import com.gregtechceu.gtceu.common.machine.multiblock.part.DataAccessHatchMachine; import com.gregtechceu.gtceu.common.machine.multiblock.part.ObjectHolderMachine; -import com.gregtechceu.gtceu.common.machine.multiblock.part.OpticalComputationHatchMachine; import com.gregtechceu.gtceu.common.machine.multiblock.part.OpticalDataHatchMachine; import com.gregtechceu.gtceu.common.machine.multiblock.part.hpca.HPCABridgePartMachine; import com.gregtechceu.gtceu.common.machine.multiblock.part.hpca.HPCAComputationPartMachine; @@ -304,12 +304,12 @@ public class GTResearchMachines { public static final MachineDefinition COMPUTATION_HATCH_TRANSMITTER = registerDataHatch( "computation_transmitter_hatch", "Computation Data Transmission Hatch", - ZPM, (holder) -> new OpticalComputationHatchMachine(holder, true), + ZPM, (holder) -> new ComputationHatchMachine(holder, true), "computation_data_hatch", PartAbility.COMPUTATION_DATA_TRANSMISSION).register(); public static final MachineDefinition COMPUTATION_HATCH_RECEIVER = registerDataHatch( "computation_receiver_hatch", "Computation Data Reception Hatch", - ZPM, (holder) -> new OpticalComputationHatchMachine(holder, false), + ZPM, (holder) -> new ComputationHatchMachine(holder, false), "computation_data_hatch", PartAbility.COMPUTATION_DATA_RECEPTION).register(); public static final MachineDefinition DATA_HATCH_TRANSMITTER = registerDataHatch( diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/materials/ElementMaterials.java b/src/main/java/com/gregtechceu/gtceu/common/data/materials/ElementMaterials.java index acecab1ca3..a7c965da76 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/materials/ElementMaterials.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/materials/ElementMaterials.java @@ -97,7 +97,7 @@ public static void register() { .liquid(new FluidBuilder().temperature(1560)) .ore() .color(0x73d73d).secondaryColor(0x184537).iconSet(METALLIC) - .appendFlags(STD_METAL) + .appendFlags(STD_METAL, GENERATE_FOIL) .hazard(HazardProperty.HazardTrigger.SKIN_CONTACT, GTMedicalConditions.BERYLLIOSIS, false) .element(GTElements.Be) .buildAndRegister(); @@ -177,7 +177,7 @@ public static void register() { .appendFlags(EXT_METAL, GENERATE_ROTOR) .element(GTElements.Cr) .rotorStats(130, 155, 3.0f, 512) - .fluidPipeProperties(2180, 35, true, true, false, false) + .fluidPipeProperties(2180, 104, 35, true, false, false) .blast(1700, GasTier.LOW) .hazard(HazardProperty.HazardTrigger.SKIN_CONTACT, GTMedicalConditions.CARCINOGEN) .buildAndRegister(); @@ -315,7 +315,7 @@ public static void register() { GENERATE_SPRING, GENERATE_SPRING_SMALL, GENERATE_FINE_WIRE, GENERATE_FOIL) .element(GTElements.Au) .cableProperties(V[HV], 3, 2) - .fluidPipeProperties(1671, 25, true, true, false, false) + .fluidPipeProperties(1671, 25, true, false, false) .buildAndRegister(); Hafnium = new Material.Builder(GTCEu.id("hafnium")) @@ -380,7 +380,7 @@ public static void register() { .appendFlags(EXT2_METAL, GENERATE_FINE_WIRE, GENERATE_GEAR, GENERATE_FRAME) .element(GTElements.Ir) .rotorStats(130, 115, 3.0f, 2560) - .fluidPipeProperties(3398, 250, true, false, true, false) + .fluidPipeProperties(3398, 4, 250, false, true, false) .blast(b -> b.temp(4500, GasTier.HIGH) .blastStats(VA[IV], 1100) .vacuumStats(VA[EV], 250)) @@ -629,7 +629,7 @@ public static void register() { .itemPipeProperties(512, 4.0f) .buildAndRegister(); - Plutonium239 = new Material.Builder(GTCEu.id("plutonium")) + Plutonium239 = new Material.Builder(GTCEu.id("plutonium_239")) .ingot(3) .liquid(new FluidBuilder().temperature(913)) .ore(true) @@ -855,7 +855,8 @@ public static void register() { Titanium = new Material.Builder(GTCEu.id("titanium")) // todo Ore? Look at EBF recipe here if we do Ti ores .ingot(3).fluid() .color(0xed8eea).secondaryColor(0xff64bc).iconSet(METALLIC) - .appendFlags(EXT2_METAL, GENERATE_ROTOR, GENERATE_SMALL_GEAR, GENERATE_GEAR, GENERATE_FRAME) + .appendFlags(EXT2_METAL, GENERATE_ROTOR, GENERATE_SMALL_GEAR, GENERATE_GEAR, GENERATE_FRAME, + GENERATE_FOIL) .element(GTElements.Ti) .toolStats(ToolProperty.Builder.of(8.0F, 6.0F, 1536, 3) .enchantability(14).build()) @@ -883,13 +884,13 @@ public static void register() { .element(GTElements.W) .rotorStats(130, 115, 3.0f, 2560) .cableProperties(V[IV], 2, 2) - .fluidPipeProperties(4618, 50, true, true, false, true) + .fluidPipeProperties(4618, 50, true, false, true) .blast(b -> b.temp(3600, GasTier.MID) .blastStats(VA[EV], 1800) .vacuumStats(VA[HV], 300)) .buildAndRegister(); - Uranium238 = new Material.Builder(GTCEu.id("uranium")) + Uranium238 = new Material.Builder(GTCEu.id("uranium_238")) .ingot(3) .liquid(new FluidBuilder().temperature(1405)) .color(0x1d891d).secondaryColor(0x33342c).iconSet(RADIOACTIVE) @@ -954,7 +955,7 @@ public static void register() { .element(GTElements.Nq) .rotorStats(160, 105, 4.0f, 1280) .cableProperties(V[ZPM], 2, 2) - .fluidPipeProperties(3776, 200, true, false, true, true) + .fluidPipeProperties(3776, 3, 200, false, true, true) .blast(b -> b.temp(5000, GasTier.HIGH) .blastStats(VA[IV], 600) .vacuumStats(VA[EV], 150)) @@ -992,7 +993,7 @@ public static void register() { .toolStats(ToolProperty.Builder.of(180.0F, 100.0F, 65535, 6) .attackSpeed(0.5F).enchantability(33).magnetic().unbreakable().build()) .rotorStats(400, 250, 12.0f, 655360) - .fluidPipeProperties(100_000, 5000, true, true, true, true) + .fluidPipeProperties(100_000, 1, 5000, true, true, true) .radioactiveHazard(10) .buildAndRegister(); @@ -1015,13 +1016,13 @@ public static void register() { .element(GTElements.Dr) .toolStats(ToolProperty.Builder.of(14.0F, 12.0F, 8192, 5) .attackSpeed(0.3F).enchantability(33).magnetic().build()) - .fluidPipeProperties(9625, 500, true, true, true, true) + .fluidPipeProperties(9625, 1, 500, true, true, true) .buildAndRegister(); Trinium = new Material.Builder(GTCEu.id("trinium")) .ingot(7).fluid() .color(0x81808a).secondaryColor(0x351d4b).iconSet(SHINY) - .flags(GENERATE_FOIL, GENERATE_BOLT_SCREW, GENERATE_GEAR) + .flags(GENERATE_FOIL, GENERATE_BOLT_SCREW, GENERATE_GEAR, GENERATE_FRAME) .element(GTElements.Ke) .cableProperties(V[ZPM], 6, 4) .blast(b -> b.temp(7200, GasTier.HIGH) diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/materials/FirstDegreeMaterials.java b/src/main/java/com/gregtechceu/gtceu/common/data/materials/FirstDegreeMaterials.java index f4bea4af1b..1282fb701e 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/materials/FirstDegreeMaterials.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/materials/FirstDegreeMaterials.java @@ -612,7 +612,7 @@ public static void register() { .toolStats(ToolProperty.Builder.of(7.0F, 5.0F, 1024, 3) .enchantability(14).build()) .rotorStats(160, 115, 4.0f, 480) - .fluidPipeProperties(2428, 75, true, true, true, false) + .fluidPipeProperties(2428, 59, 75, true, true, false) .blast(b -> b.temp(1700, GasTier.LOW) .blastStats(VA[HV], 1100)) .buildAndRegister(); @@ -876,7 +876,7 @@ public static void register() { SiliconDioxide = new Material.Builder(GTCEu.id("silicon_dioxide")) .dust(1) .color(0xf2f2f2).secondaryColor(0xb2c4c7).iconSet(QUARTZ) - .flags(NO_SMASHING, NO_SMELTING) + .flags(NO_SMASHING, NO_SMELTING, GENERATE_FOIL) .components(Silicon, 1, Oxygen, 2) .hazard(HazardProperty.HazardTrigger.INHALATION, GTMedicalConditions.SILICOSIS, false) .buildAndRegister(); @@ -1324,7 +1324,7 @@ public static void register() { .color(0xE1B454).secondaryColor(0x223033).iconSet(METALLIC) .flags(DECOMPOSITION_BY_ELECTROLYZING) .components(Manganese, 1, Phosphorus, 1) - .cableProperties(V[LV], 2, 0, true, 78) + .cableProperties(V[LV], 2, 0, true) .blast(1200, GasTier.LOW) .buildAndRegister(); @@ -1334,7 +1334,7 @@ public static void register() { .color(0x603c1a).secondaryColor(0x423e39).iconSet(METALLIC) .flags(DECOMPOSITION_BY_ELECTROLYZING) .components(Magnesium, 1, Boron, 2) - .cableProperties(V[MV], 4, 0, true, 78) + .cableProperties(V[MV], 4, 0, true) .blast(b -> b.temp(2500, GasTier.LOW) .blastStats(VA[HV], 1000) .vacuumStats(VA[MV], 200)) @@ -1346,7 +1346,7 @@ public static void register() { .color(0x928547).secondaryColor(0x3f2e2e).iconSet(SHINY) .flags(DECOMPOSITION_BY_ELECTROLYZING) .components(Mercury, 1, Barium, 2, Calcium, 2, Copper, 3, Oxygen, 8) - .cableProperties(V[HV], 4, 0, true, 78) + .cableProperties(V[HV], 4, 0, true) .blast(b -> b.temp(3300, GasTier.LOW) .blastStats(VA[HV], 1500) .vacuumStats(VA[HV])) @@ -1358,7 +1358,7 @@ public static void register() { .color(0x457045).secondaryColor(0x66ff00).iconSet(RADIOACTIVE) .flags(DECOMPOSITION_BY_CENTRIFUGING) .components(Uranium238, 1, Platinum, 3) - .cableProperties(V[EV], 6, 0, true, 30) + .cableProperties(V[EV], 6, 0, true) .blast(b -> b.temp(4400, GasTier.MID) .blastStats(VA[EV], 1000) .vacuumStats(VA[EV], 200)) @@ -1371,7 +1371,7 @@ public static void register() { .color(0x850e85).secondaryColor(0x332f33).iconSet(SHINY) .flags(DECOMPOSITION_BY_CENTRIFUGING) .components(Samarium, 1, Iron, 1, Arsenic, 1, Oxygen, 1) - .cableProperties(V[IV], 6, 0, true, 30) + .cableProperties(V[IV], 6, 0, true) .blast(b -> b.temp(5200, GasTier.MID) .blastStats(VA[EV], 1500) .vacuumStats(VA[IV], 200)) @@ -1383,7 +1383,7 @@ public static void register() { .color(0x686760).secondaryColor(0x673300).iconSet(METALLIC) .flags(DECOMPOSITION_BY_ELECTROLYZING, GENERATE_FINE_WIRE) .components(Indium, 4, Tin, 2, Barium, 2, Titanium, 1, Copper, 7, Oxygen, 14) - .cableProperties(V[LuV], 8, 0, true, 5) + .cableProperties(V[LuV], 8, 0, true) .blast(b -> b.temp(6000, GasTier.HIGH) .blastStats(VA[IV], 1000) .vacuumStats(VA[LuV])) @@ -1395,7 +1395,7 @@ public static void register() { .color(0x232020).secondaryColor(0xff009c).iconSet(RADIOACTIVE) .flags(DECOMPOSITION_BY_CENTRIFUGING, GENERATE_FINE_WIRE) .components(Uranium238, 1, Rhodium, 1, Naquadah, 2) - .cableProperties(V[ZPM], 8, 0, true, 5) + .cableProperties(V[ZPM], 8, 0, true) .blast(b -> b.temp(9000, GasTier.HIGH) .blastStats(VA[IV], 1500) .vacuumStats(VA[ZPM], 200)) @@ -1409,7 +1409,7 @@ public static void register() { .color(0xc6b083).secondaryColor(0x45063d).iconSet(METALLIC) .flags(DECOMPOSITION_BY_CENTRIFUGING, GENERATE_FINE_WIRE) .components(NaquadahEnriched, 4, Trinium, 3, Europium, 2, Duranium, 1) - .cableProperties(V[UV], 16, 0, true, 3) + .cableProperties(V[UV], 16, 0, true) .blast(b -> b.temp(9900, GasTier.HIGH) .blastStats(VA[LuV], 1200) .vacuumStats(VA[UV], 200)) @@ -1421,7 +1421,7 @@ public static void register() { .color(0x897b76).secondaryColor(0x00c0ff).iconSet(RADIOACTIVE) .flags(DECOMPOSITION_BY_ELECTROLYZING) .components(Ruthenium, 1, Trinium, 2, Americium, 1, Neutronium, 2, Oxygen, 8) - .cableProperties(V[UHV], 24, 0, true, 3) + .cableProperties(V[UHV], 24, 0, true) .blast(b -> b.temp(10800, GasTier.HIGHER) .blastStats(VA[ZPM], 1000) .vacuumStats(VA[UHV], 200)) diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/materials/OrganicChemistryMaterials.java b/src/main/java/com/gregtechceu/gtceu/common/data/materials/OrganicChemistryMaterials.java index 545e3030f6..5644fe8598 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/materials/OrganicChemistryMaterials.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/materials/OrganicChemistryMaterials.java @@ -155,7 +155,7 @@ public static void register() { .toolStats( ToolProperty.Builder.of(1.0F, 1.0F, 512, 1, GTToolType.SOFT_MALLET, GTToolType.PLUNGER).build()) .components(Carbon, 2, Fluorine, 4) - .fluidPipeProperties(600, 100, true, true, false, false) + .fluidPipeProperties(600, 100, true, true, false) .buildAndRegister(); Sugar = new Material.Builder(GTCEu.id("sugar")) diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/materials/SecondDegreeMaterials.java b/src/main/java/com/gregtechceu/gtceu/common/data/materials/SecondDegreeMaterials.java index 378d370c77..b0b398b3de 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/materials/SecondDegreeMaterials.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/materials/SecondDegreeMaterials.java @@ -197,7 +197,7 @@ public static void register() { .components(SiliconDioxide, 4, Biotite, 1) .buildAndRegister(); - GraniteRed = new Material.Builder(GTCEu.id("granite_red")) + RedGranite = new Material.Builder(GTCEu.id("red_granite")) .dust() .color(0xFF0080).iconSet(ROUGH) .flags(NO_SMASHING) @@ -306,7 +306,7 @@ public static void register() { .toolStats(ToolProperty.Builder.of(3.0F, 3.0F, 1536, 3) .attackSpeed(-0.2F).enchantability(5).build()) .rotorStats(130, 115, 3.0f, 1920) - .fluidPipeProperties(2073, 50, true, true, false, false) + .fluidPipeProperties(2073, 50, true, true, false) .blast(1453, GasTier.LOW) .buildAndRegister(); @@ -323,7 +323,7 @@ public static void register() { .ingot(1) .liquid(new FluidBuilder().temperature(1921)) .color(0xFAFAFA).secondaryColor(0xfaf5c0).iconSet(SHINY) - .flags(GENERATE_FINE_WIRE, GENERATE_PLATE) + .flags(GENERATE_FINE_WIRE, GENERATE_PLATE, NO_SMASHING) .components(Boron, 1, SiliconDioxide, 7) .buildAndRegister(); diff --git a/src/main/java/com/gregtechceu/gtceu/common/data/materials/UnknownCompositionMaterials.java b/src/main/java/com/gregtechceu/gtceu/common/data/materials/UnknownCompositionMaterials.java index 4ebd2b5908..199d149790 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/data/materials/UnknownCompositionMaterials.java +++ b/src/main/java/com/gregtechceu/gtceu/common/data/materials/UnknownCompositionMaterials.java @@ -277,19 +277,19 @@ public static void register() { .flags(STICKY, FLAMMABLE) .buildAndRegister(); - OilHeavy = new Material.Builder(GTCEu.id("oil_heavy")) + HeavyOil = new Material.Builder(GTCEu.id("heavy_oil")) .liquid(new FluidBuilder().block().customStill()) .color(0x0A0A0A) .flags(STICKY, FLAMMABLE) .buildAndRegister(); - RawOil = new Material.Builder(GTCEu.id("oil_medium")) + RawOil = new Material.Builder(GTCEu.id("raw_oil")) .liquid(new FluidBuilder().block().customStill()) .color(0x0A0A0A) .flags(STICKY, FLAMMABLE) .buildAndRegister(); - OilLight = new Material.Builder(GTCEu.id("oil_light")) + LightOil = new Material.Builder(GTCEu.id("light_oil")) .liquid(new FluidBuilder().block().customStill()) .color(0x0A0A0A) .flags(STICKY, FLAMMABLE) diff --git a/src/main/java/com/gregtechceu/gtceu/common/datafixers/TagFixer.java b/src/main/java/com/gregtechceu/gtceu/common/datafixer/TagFixer.java similarity index 96% rename from src/main/java/com/gregtechceu/gtceu/common/datafixers/TagFixer.java rename to src/main/java/com/gregtechceu/gtceu/common/datafixer/TagFixer.java index 12ccd92737..9bc8030cea 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/datafixers/TagFixer.java +++ b/src/main/java/com/gregtechceu/gtceu/common/datafixer/TagFixer.java @@ -1,4 +1,4 @@ -package com.gregtechceu.gtceu.common.datafixers; +package com.gregtechceu.gtceu.common.datafixer; import com.gregtechceu.gtceu.utils.GTMath; diff --git a/src/main/java/com/gregtechceu/gtceu/common/datafixer/fixes/ActivablePipeConnectionFix.java b/src/main/java/com/gregtechceu/gtceu/common/datafixer/fixes/ActivablePipeConnectionFix.java new file mode 100644 index 0000000000..480a9119a4 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/datafixer/fixes/ActivablePipeConnectionFix.java @@ -0,0 +1,16 @@ +package com.gregtechceu.gtceu.common.datafixer.fixes; + +import com.mojang.datafixers.schemas.Schema; +import com.mojang.serialization.Dynamic; + +public class ActivablePipeConnectionFix extends PipeConnectionFix { + + public ActivablePipeConnectionFix(Schema outputSchema, boolean changesType, String type) { + super(outputSchema, changesType, type); + } + + public Dynamic fixTag(Dynamic tag) { + tag = super.fixTag(tag); + return tag.set("active", tag.get("isActive").result().get()); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/datafixer/fixes/OilVariantsRenameFix.java b/src/main/java/com/gregtechceu/gtceu/common/datafixer/fixes/OilVariantsRenameFix.java new file mode 100644 index 0000000000..de16a55653 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/datafixer/fixes/OilVariantsRenameFix.java @@ -0,0 +1,29 @@ +package com.gregtechceu.gtceu.common.datafixer.fixes; + +import com.google.common.collect.ImmutableMap; + +import java.util.Map; + +public class OilVariantsRenameFix { + + public static final Map RENAMED_ITEM_IDS = ImmutableMap.builder() + .put("gtceu:oil_heavy_bucket", "gtceu:heavy_oil_bucket") + .put("gtceu:oil_light_bucket", "gtceu:light_oil_bucket") + .put("gtceu:oil_medium_bucket", "gtceu:raw_oil_bucket") + .build(); + + public static final Map RENAMED_BLOCK_IDS = ImmutableMap.builder() + .put("gtceu:oil_heavy", "gtceu:heavy_oil") + .put("gtceu:oil_light", "gtceu:light_oil") + .put("gtceu:oil_medium", "gtceu:raw_oil") + .build(); + + public static final Map RENAMED_FLUID_IDS = ImmutableMap.builder() + .put("gtceu:oil_heavy", "gtceu:heavy_oil") + .put("gtceu:flowing_oil_heavy", "gtceu:flowing_heavy_oil") + .put("gtceu:oil_light", "gtceu:light_oil") + .put("gtceu:flowing_oil_light", "gtceu:flowing_light_oil") + .put("gtceu:oil_medium", "gtceu:raw_oil") + .put("gtceu:flowing_oil_medium", "gtceu:flowing_raw_oil") + .build(); +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/datafixer/fixes/PipeConnectionFix.java b/src/main/java/com/gregtechceu/gtceu/common/datafixer/fixes/PipeConnectionFix.java new file mode 100644 index 0000000000..f264fe7a1c --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/datafixer/fixes/PipeConnectionFix.java @@ -0,0 +1,26 @@ +package com.gregtechceu.gtceu.common.datafixer.fixes; + +import net.minecraft.util.datafix.fixes.NamedEntityFix; +import net.minecraft.util.datafix.fixes.References; + +import com.mojang.datafixers.DSL; +import com.mojang.datafixers.Typed; +import com.mojang.datafixers.schemas.Schema; +import com.mojang.serialization.Dynamic; + +public class PipeConnectionFix extends NamedEntityFix { + + public PipeConnectionFix(Schema outputSchema, boolean changesType, String type) { + super(outputSchema, changesType, "PipeConnectionFix", References.BLOCK_ENTITY, type); + } + + public Dynamic fixTag(Dynamic tag) { + tag = tag.set("connectionMask", tag.get("connections").result().get()); + return tag.set("blockedMask", tag.get("blockedConnections").result().get()); + } + + @Override + protected Typed fix(Typed typed) { + return typed.update(DSL.remainderFinder(), this::fixTag); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/datafixer/schemas/BaseGTSchema.java b/src/main/java/com/gregtechceu/gtceu/common/datafixer/schemas/BaseGTSchema.java new file mode 100644 index 0000000000..9d2c2222c9 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/datafixer/schemas/BaseGTSchema.java @@ -0,0 +1,36 @@ +package com.gregtechceu.gtceu.common.datafixer.schemas; + +import com.gregtechceu.gtceu.common.data.datafixer.GTReferences; + +import net.minecraft.util.datafix.schemas.NamespacedSchema; + +import com.google.common.collect.Maps; +import com.mojang.datafixers.DSL; +import com.mojang.datafixers.schemas.Schema; +import com.mojang.datafixers.types.templates.TypeTemplate; + +import java.util.Map; +import java.util.function.Supplier; + +public class BaseGTSchema extends NamespacedSchema { + + public BaseGTSchema(int versionKey, Schema parent) { + super(versionKey, parent); + } + + @Override + public void registerTypes(Schema schema, Map> entityTypes, + Map> blockEntityTypes) { + super.registerTypes(schema, entityTypes, blockEntityTypes); + schema.registerType(false, GTReferences.MATERIAL_NAME, () -> DSL.constType(namespacedString())); + schema.registerType(false, GTReferences.COVER_NAME, () -> DSL.constType(namespacedString())); + + Map> coverTypes = registerCoverDefinitions(schema); + schema.registerType(true, GTReferences.COVER, + () -> DSL.taggedChoiceLazy("id", namespacedString(), coverTypes)); + } + + public Map> registerCoverDefinitions(final Schema schema) { + return Maps.newHashMap(); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/datafixer/schemas/V0.java b/src/main/java/com/gregtechceu/gtceu/common/datafixer/schemas/V0.java new file mode 100644 index 0000000000..504192a3ad --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/datafixer/schemas/V0.java @@ -0,0 +1,286 @@ +package com.gregtechceu.gtceu.common.datafixer.schemas; + +import com.gregtechceu.gtceu.api.cover.CoverDefinition; +import com.gregtechceu.gtceu.api.machine.MachineDefinition; +import com.gregtechceu.gtceu.api.registry.GTRegistries; +import com.gregtechceu.gtceu.common.data.GTCovers; +import com.gregtechceu.gtceu.common.data.datafixer.GTReferences; + +import net.minecraft.util.datafix.fixes.References; + +import com.mojang.datafixers.DSL; +import com.mojang.datafixers.schemas.Schema; +import com.mojang.datafixers.types.templates.TypeTemplate; + +import java.util.Map; +import java.util.function.Supplier; + +@SuppressWarnings("Convert2MethodRef") +public class V0 extends BaseGTSchema { + + public V0(int versionKey, Schema parent) { + super(versionKey, parent); + } + + @Override + public void registerTypes(Schema schema, Map> entityTypes, + Map> blockEntityTypes) { + super.registerTypes(schema, entityTypes, blockEntityTypes); + } + + // Register schemas for all covers. + public Map> registerCoverDefinitions(final Schema schema) { + Map> map = super.registerCoverDefinitions(schema); + + schema.register(map, "gtceu:facade", () -> DSL.field("facadeState", References.BLOCK_STATE.in(schema))); + schema.register(map, "gtceu:item_filter", () -> DSL.field("filterMode", DSL.remainder())); + schema.register(map, "gtceu:fluid_filter", () -> DSL.remainder()); + schema.register(map, "gtceu:infinite_water", () -> DSL.remainder()); + schema.register(map, "gtceu:shutter", () -> DSL.field("workingEnabled", DSL.bool().template())); + schema.register(map, "gtceu:machine_controller", () -> DSL.fields( + "isInverted", DSL.bool().template(), + "minRedstoneStrength", DSL.intType().template(), + "controllerMode", DSL.string().template())); + for (CoverDefinition cover : GTCovers.CONVEYORS) { + schema.register(map, cover.getId().toString(), () -> DSL.allWithRemainder( + DSL.fields( + "transferRate", DSL.intType().template(), + "distributionMode", DSL.string().template(), + "io", DSL.string().template()), + DSL.fields( + "manualIOMode", DSL.string().template(), + "isWorkingEnabled", DSL.bool().template(), + "filterHandler", DSL.field( + "filterItem", References.ITEM_STACK.in(schema))))); + } + for (CoverDefinition cover : GTCovers.ROBOT_ARMS) { + schema.register(map, cover.getId().toString(), () -> DSL.allWithRemainder( + DSL.fields( + "transferRate", DSL.intType().template(), + "distributionMode", DSL.string().template(), + "io", DSL.string().template()), + DSL.fields( + "manualIOMode", DSL.string().template(), + "isWorkingEnabled", DSL.bool().template(), + "transferMode", DSL.string().template()), + DSL.fields( + "globalTransferLimit", DSL.intType().template(), + "filterHandler", DSL.field( + "filterItem", References.ITEM_STACK.in(schema))))); + } + for (CoverDefinition cover : GTCovers.PUMPS) { + schema.register(map, cover.getId().toString(), () -> DSL.allWithRemainder( + DSL.fields( + "currentMilliBucketsPerTick", DSL.longType().template(), + "distributionMode", DSL.string().template(), + "io", DSL.string().template()), + DSL.fields( + "manualIOMode", DSL.string().template(), + "isWorkingEnabled", DSL.bool().template(), + "bucketMode", DSL.string().template()), + DSL.fields( + "globalTransferLimit", DSL.intType().template(), + "filterHandler", DSL.field( + "filterItem", References.ITEM_STACK.in(schema))))); + } + for (CoverDefinition cover : GTCovers.FLUID_REGULATORS) { + schema.register(map, cover.getId().toString(), () -> DSL.allWithRemainder( + DSL.fields( + "currentMilliBucketsPerTick", DSL.longType().template(), + "distributionMode", DSL.string().template(), + "io", DSL.string().template()), + DSL.fields( + "manualIOMode", DSL.string().template(), + "isWorkingEnabled", DSL.bool().template(), + "bucketMode", DSL.string().template()), + DSL.fields("globalTransferLimit", DSL.intType().template(), + "transferMode", DSL.string().template(), + "globalTransferSizeMillibuckets", DSL.longType().template()), + DSL.fields( + "filterHandler", DSL.field( + "filterItem", References.ITEM_STACK.in(schema))))); + } + schema.register(map, "gtceu:item_voiding", () -> DSL.allWithRemainder( + DSL.fields( + "transferRate", DSL.intType().template(), + "distributionMode", DSL.string().template(), + "io", DSL.string().template()), + DSL.fields( + "manualIOMode", DSL.string().template(), + "isWorkingEnabled", DSL.bool().template(), + "filterHandler", DSL.field( + "filterItem", References.ITEM_STACK.in(schema))), + DSL.fields("isEnabled", DSL.bool().template()))); + schema.register(map, "gtceu:item_voiding_advanced", () -> DSL.allWithRemainder( + DSL.fields( + "transferRate", DSL.intType().template(), + "distributionMode", DSL.string().template(), + "io", DSL.string().template()), + DSL.fields( + "manualIOMode", DSL.string().template(), + "isWorkingEnabled", DSL.bool().template(), + "filterHandler", DSL.field( + "filterItem", References.ITEM_STACK.in(schema))), + DSL.fields( + "isEnabled", DSL.bool().template(), + "voidingMode", DSL.string().template(), + "globalVoidingLimit", DSL.intType().template()))); + schema.register(map, "gtceu:fluid_voiding", () -> DSL.allWithRemainder( + DSL.fields( + "currentMilliBucketsPerTick", DSL.longType().template(), + "distributionMode", DSL.string().template(), + "io", DSL.string().template()), + DSL.fields( + "manualIOMode", DSL.string().template(), + "isWorkingEnabled", DSL.bool().template(), + "bucketMode", DSL.string().template()), + DSL.fields( + "globalTransferLimit", DSL.intType().template(), + "filterHandler", DSL.field( + "filterItem", References.ITEM_STACK.in(schema))), + DSL.fields("isEnabled", DSL.bool().template()))); + schema.register(map, "gtceu:fluid_voiding_advanced", () -> DSL.allWithRemainder( + DSL.fields( + "currentMilliBucketsPerTick", DSL.longType().template(), + "distributionMode", DSL.string().template(), + "io", DSL.string().template()), + DSL.fields( + "manualIOMode", DSL.string().template(), + "isWorkingEnabled", DSL.bool().template(), + "bucketMode", DSL.string().template()), + DSL.fields( + "globalTransferLimit", DSL.intType().template(), + "filterHandler", DSL.field( + "filterItem", References.ITEM_STACK.in(schema))), + DSL.fields( + "isEnabled", DSL.bool().template(), + "voidingMode", DSL.string().template(), + "globalVoidingLimit", DSL.intType().template()))); + schema.register(map, "gtceu:activity_detector", () -> DSL.fields( + "isWorkingEnabled", DSL.bool().template(), + "isInverted", DSL.bool().template())); + schema.register(map, "gtceu:activity_detector_advanced", () -> DSL.fields( + "isWorkingEnabled", DSL.bool().template(), + "isInverted", DSL.bool().template())); + schema.register(map, "gtceu:fluid_detector", () -> DSL.fields( + "isWorkingEnabled", DSL.bool().template(), + "isInverted", DSL.bool().template())); + schema.register(map, "gtceu:fluid_detector_advanced", () -> DSL.allWithRemainder( + DSL.fields( + "isWorkingEnabled", DSL.bool().template(), + "isInverted", DSL.bool().template(), + "minValue", DSL.longType().template()), + DSL.fields( + "maxValue", DSL.longType().template(), + "filterHandler", DSL.field( + "filterItem", References.ITEM_STACK.in(schema))))); + schema.register(map, "gtceu:item_detector", () -> DSL.fields( + "isWorkingEnabled", DSL.bool().template(), + "isInverted", DSL.bool().template())); + schema.register(map, "gtceu:item_detector_advanced", () -> DSL.allWithRemainder( + DSL.fields( + "isWorkingEnabled", DSL.bool().template(), + "isInverted", DSL.bool().template(), + "minValue", DSL.intType().template()), + DSL.fields( + "maxValue", DSL.intType().template(), + "filterHandler", DSL.field( + "filterItem", References.ITEM_STACK.in(schema))))); + schema.register(map, "gtceu:energy_detector", () -> DSL.remainder()); + schema.register(map, "gtceu:energy_detector_advanced", () -> DSL.allWithRemainder( + DSL.fields( + "isWorkingEnabled", DSL.bool().template(), + "isInverted", DSL.bool().template(), + "minValue", DSL.longType().template()), + DSL.fields( + "maxValue", DSL.longType().template(), + "outputAmount", DSL.intType().template(), + "usePercent", DSL.bool().template()))); + schema.register(map, "gtceu:maintenance_detector", () -> DSL.remainder()); + for (CoverDefinition cover : GTCovers.SOLAR_PANEL) { + schema.register(map, cover.getId().toString(), () -> DSL.remainder()); + } + return map; + } + + @Override + public Map> registerBlockEntities(Schema schema) { + Map> map = super.registerBlockEntities(schema); + for (MachineDefinition definition : GTRegistries.MACHINES) { + registerInventory(schema, map, definition.getId().toString()); + } + Supplier cover = () -> DSL.fields( + "side", DSL.intType().template(), + GTReferences.COVER.in(schema)); + Supplier covers = () -> DSL.allWithRemainder( + DSL.fields( + "up", cover.get(), + "down", cover.get(), + "north", cover.get()), + DSL.fields( + "south", cover.get(), + "west", cover.get(), + "east", cover.get())); + final Supplier pipe = () -> DSL.allWithRemainder( + DSL.optionalFields( + "connections", DSL.intType().template(), + "blockedConnections", DSL.intType().template(), + "cover", covers.get(), + "paintingColor", DSL.intType().template(), + "frameMaterial", GTReferences.MATERIAL_NAME.in(schema))); + + schema.register(map, "gtceu:cable", () -> DSL.and( + DSL.optionalFields("temperature", DSL.intType().template()), + pipe.get())); + schema.register(map, "gtceu:fluid_pipe", pipe); + schema.register(map, "gtceu:item_pipe", pipe); + schema.register(map, "gtceu:laser_pipe", () -> DSL.and( + DSL.optionalFields("active", DSL.bool().template()), + pipe.get())); + schema.register(map, "gtceu:optical_pipe", () -> DSL.and( + DSL.optionalFields("isActive", DSL.bool().template()), + pipe.get())); + schema.register(map, "gtceu:duct_pipe", pipe); + return map; + } + + protected static void registerInventory(Schema schema, Map> map, String name) { + Supplier cover = () -> DSL.fields( + "side", DSL.intType().template(), + GTReferences.COVER.in(schema)); + Supplier covers = () -> DSL.allWithRemainder( + DSL.fields( + "up", cover.get(), + "down", cover.get(), + "north", cover.get()), + DSL.fields( + "south", cover.get(), + "west", cover.get(), + "east", cover.get())); + + schema.register(map, name, () -> DSL.or( + DSL.fields( + "importItems", + DSL.field("storage", + DSL.field("Items", + DSL.list(References.ITEM_STACK.in(schema)))), + "exportItems", + DSL.field("storage", + DSL.field("Items", + DSL.list(References.ITEM_STACK.in(schema)))), + DSL.field("cover", covers.get())), + DSL.or( + DSL.fields( + "inventory", + DSL.field("storage", + DSL.field("Items", + DSL.list(References.ITEM_STACK.in(schema))))), + DSL.or( + DSL.fields( + "cache", + DSL.field("storage", + DSL.field("Items", + DSL.list(References.ITEM_STACK.in(schema))))), + DSL.remainder())))); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/datafixer/schemas/V2.java b/src/main/java/com/gregtechceu/gtceu/common/datafixer/schemas/V2.java new file mode 100644 index 0000000000..22101b5500 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/datafixer/schemas/V2.java @@ -0,0 +1,43 @@ +package com.gregtechceu.gtceu.common.datafixer.schemas; + +import com.gregtechceu.gtceu.common.data.datafixer.GTReferences; + +import com.mojang.datafixers.DSL; +import com.mojang.datafixers.schemas.Schema; +import com.mojang.datafixers.types.templates.TypeTemplate; + +import java.util.Map; +import java.util.function.Supplier; + +public class V2 extends BaseGTSchema { + + public V2(int versionKey, Schema parent) { + super(versionKey, parent); + } + + @Override + public Map> registerBlockEntities(Schema schema) { + Map> map = super.registerBlockEntities(schema); + final Supplier covers = () -> DSL.field("covers", DSL.list( + DSL.fields( + "side", DSL.byteType().template(), + GTReferences.COVER.in(schema)))); + final Supplier pipe = () -> DSL.allWithRemainder( + DSL.fields( + "connectionMask", DSL.byteType().template(), + "renderMask", DSL.byteType().template(), + "coverHolder", covers.get()), + DSL.fields( + "blockedMask", DSL.byteType().template(), + "paintingColor", DSL.intType().template(), + "frameMaterial", GTReferences.MATERIAL_NAME.in(schema))); + + schema.register(map, "gtceu:pipe", pipe); + schema.register(map, "gtceu:activable_pipe", + () -> DSL.and( + DSL.optionalFields("active", DSL.bool().template()), + pipe.get())); + schema.register(map, "gtceu:material_pipe", pipe); + return map; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/item/ColorSprayBehaviour.java b/src/main/java/com/gregtechceu/gtceu/common/item/ColorSprayBehaviour.java index 48bf3de53d..62a707f0a1 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/item/ColorSprayBehaviour.java +++ b/src/main/java/com/gregtechceu/gtceu/common/item/ColorSprayBehaviour.java @@ -4,7 +4,7 @@ import com.gregtechceu.gtceu.api.GTValues; import com.gregtechceu.gtceu.api.blockentity.IPaintable; import com.gregtechceu.gtceu.api.blockentity.MetaMachineBlockEntity; -import com.gregtechceu.gtceu.api.blockentity.PipeBlockEntity; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; import com.gregtechceu.gtceu.api.item.component.IAddInformation; import com.gregtechceu.gtceu.api.item.component.IDurabilityBar; import com.gregtechceu.gtceu.api.item.component.IInteractionItem; diff --git a/src/main/java/com/gregtechceu/gtceu/common/item/PortableScannerBehavior.java b/src/main/java/com/gregtechceu/gtceu/common/item/PortableScannerBehavior.java index ee09f99565..4738c86939 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/item/PortableScannerBehavior.java +++ b/src/main/java/com/gregtechceu/gtceu/common/item/PortableScannerBehavior.java @@ -2,13 +2,15 @@ import com.gregtechceu.gtceu.api.GTValues; import com.gregtechceu.gtceu.api.block.IMachineBlock; -import com.gregtechceu.gtceu.api.blockentity.PipeBlockEntity; import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; import com.gregtechceu.gtceu.api.capability.IElectricItem; import com.gregtechceu.gtceu.api.capability.IEnergyContainer; import com.gregtechceu.gtceu.api.capability.IWorkable; import com.gregtechceu.gtceu.api.capability.forge.GTCapability; import com.gregtechceu.gtceu.api.data.worldgen.bedrockfluid.BedrockFluidVeinSavedData; +import com.gregtechceu.gtceu.api.graphnet.logic.NetLogicData; +import com.gregtechceu.gtceu.api.graphnet.pipenet.logic.TemperatureLogic; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; import com.gregtechceu.gtceu.api.gui.misc.ProspectorMode; import com.gregtechceu.gtceu.api.item.component.IAddInformation; import com.gregtechceu.gtceu.api.item.component.IInteractionItem; @@ -20,12 +22,14 @@ import com.gregtechceu.gtceu.api.machine.trait.RecipeLogic; import com.gregtechceu.gtceu.api.recipe.GTRecipe; import com.gregtechceu.gtceu.api.recipe.RecipeHelper; -import com.gregtechceu.gtceu.common.blockentity.FluidPipeBlockEntity; import com.gregtechceu.gtceu.common.capability.EnvironmentalHazardSavedData; import com.gregtechceu.gtceu.common.capability.LocalizedHazardSavedData; import com.gregtechceu.gtceu.common.data.GTSoundEntries; import com.gregtechceu.gtceu.common.network.GTNetwork; import com.gregtechceu.gtceu.common.network.packets.prospecting.SPacketProspectBedrockFluid; +import com.gregtechceu.gtceu.common.pipelike.net.energy.EnergyFlowData; +import com.gregtechceu.gtceu.common.pipelike.net.energy.EnergyFlowLogic; +import com.gregtechceu.gtceu.common.pipelike.net.energy.WorldEnergyNet; import com.gregtechceu.gtceu.utils.FormattingUtil; import com.gregtechceu.gtceu.utils.GTUtil; @@ -364,26 +368,74 @@ else if (machine instanceof IDataInfoProvider) list.addAll(provider.getDataInfo(mode)); } - } else if (tileEntity instanceof PipeBlockEntity pipe) { - + } else if (tileEntity instanceof PipeBlockEntity pipe) { // Pipes need special name handling - list.add(pipe.getPipeBlock().getName().withStyle(ChatFormatting.BLUE)); + list.add(pipe.getBlockType().getName().withStyle(ChatFormatting.BLUE)); + + if (mode == DisplayMode.SHOW_ALL || mode == DisplayMode.SHOW_ELECTRICAL_INFO) { + if (level instanceof ServerLevel serverLevel) { + NetLogicData data = pipe.getNetLogicData(WorldEnergyNet.getWorldNet(serverLevel).getNetworkID()); + if (data != null) { + int cumulativeCount = 0; + long cumulativeVoltage = 0; + long cumulativeAmperage = 0; + for (var memory : data.getLogicEntryDefaultable(EnergyFlowLogic.TYPE).getMemory().values()) { + cumulativeCount++; + int count = 0; + double voltage = 0; + long amperage = 0; + for (EnergyFlowData flow : memory) { + count++; + long prev = amperage; + amperage += flow.amperage(); + // weighted average + voltage = voltage * prev / amperage + + (double) (flow.voltage() * flow.amperage()) / amperage; + } + if (count != 0) { + cumulativeVoltage += voltage / count; + cumulativeAmperage += amperage / count; + } + } + if (cumulativeCount != 0) { + cumulativeVoltage /= cumulativeCount; + cumulativeAmperage /= cumulativeCount; + } + list.add(Component.translatable("behavior.portable_scanner.divider")); + list.add(Component.translatable("behavior.portable_scanner.eu_per_sec", + Component.translatable(FormattingUtil.formatNumbers(cumulativeVoltage)) + .withStyle(ChatFormatting.RED))); + list.add(Component.translatable("behavior.portable_scanner.amp_per_sec", + Component.translatable(FormattingUtil.formatNumbers(cumulativeAmperage)) + .withStyle(ChatFormatting.RED))); + + long tick = GTUtil.getCurrentServerTick(); + int temp = data.getLogicEntryDefaultable(TemperatureLogic.TYPE).getTemperature(tick); + list.add(Component.translatable("behavior.portable_scanner.temperature", + Component.translatable(FormattingUtil.formatNumbers(temp)) + .withStyle(ChatFormatting.RED))); + + } + } + } + + if (mode == DisplayMode.SHOW_ALL || mode == DisplayMode.SHOW_BLOCK_INFO) { + if (tileEntity.getCapability(ForgeCapabilities.FLUID_HANDLER).isPresent()) { + // Getting fluid info always costs 500 + energyCost += 500; + } + } // Pipe-specific info if (tileEntity instanceof IDataInfoProvider dataInfoProvider) { list.add(Component.translatable("behavior.portable_scanner.divider")); list.addAll(dataInfoProvider.getDataInfo(mode)); } - - if (tileEntity instanceof FluidPipeBlockEntity) { - // Getting fluid info always costs 500 - energyCost += 500; - } } else if (tileEntity instanceof IDataInfoProvider dataInfoProvider) { list.add(Component.translatable("behavior.portable_scanner.divider")); list.addAll(dataInfoProvider.getDataInfo(mode)); } else { - list.add(Component.translatable(state.getBlock().getDescriptionId()).withStyle(ChatFormatting.BLUE)); + list.add(state.getBlock().getName().withStyle(ChatFormatting.BLUE)); } // Environmental information diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/electric/AirScrubberMachine.java b/src/main/java/com/gregtechceu/gtceu/common/machine/electric/AirScrubberMachine.java index 620c31e7f6..3efff081fe 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/machine/electric/AirScrubberMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/electric/AirScrubberMachine.java @@ -1,12 +1,13 @@ package com.gregtechceu.gtceu.common.machine.electric; import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; +import com.gregtechceu.gtceu.api.capability.IHazardParticleContainer; import com.gregtechceu.gtceu.api.data.medicalcondition.MedicalCondition; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; import com.gregtechceu.gtceu.api.machine.SimpleTieredMachine; import com.gregtechceu.gtceu.api.machine.feature.IEnvironmentalHazardCleaner; import com.gregtechceu.gtceu.api.recipe.GTRecipe; -import com.gregtechceu.gtceu.common.blockentity.DuctPipeBlockEntity; import com.gregtechceu.gtceu.common.capability.EnvironmentalHazardSavedData; import com.gregtechceu.gtceu.common.data.GTRecipeTypes; import com.gregtechceu.gtceu.common.data.machines.GTMachineUtils; @@ -23,15 +24,18 @@ import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.chunk.LevelChunk; -import it.unimi.dsi.fastutil.objects.Object2FloatMap; -import it.unimi.dsi.fastutil.objects.Object2FloatOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2FloatLinkedOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2FloatSortedMap; import lombok.Getter; import org.jetbrains.annotations.Nullable; -import static com.gregtechceu.gtceu.api.GTValues.LV; -import static com.gregtechceu.gtceu.api.GTValues.VHA; +import java.util.Map; +import java.util.stream.Collectors; -public class AirScrubberMachine extends SimpleTieredMachine implements IEnvironmentalHazardCleaner { +import static com.gregtechceu.gtceu.api.GTValues.*; + +public class AirScrubberMachine extends SimpleTieredMachine + implements IEnvironmentalHazardCleaner, IHazardParticleContainer { public static final float MIN_CLEANING_PER_OPERATION = 10; @@ -39,10 +43,12 @@ public class AirScrubberMachine extends SimpleTieredMachine implements IEnvironm @Getter private float removedLastSecond; + private float maxRemovePerSecond; public AirScrubberMachine(IMachineBlockEntity holder, int tier, Object... args) { super(holder, tier, GTMachineUtils.largeTankSizeFunction, args); this.cleaningPerOperation = MIN_CLEANING_PER_OPERATION; + this.maxRemovePerSecond = MIN_CLEANING_PER_OPERATION; } @Override @@ -73,6 +79,9 @@ public boolean beforeWorking(@Nullable GTRecipe recipe) { if (super.beforeWorking(recipe) && recipe != null) { // Sets the amount of hazard to clean based on the recipe tier, not the machine tier this.cleaningPerOperation = MIN_CLEANING_PER_OPERATION * (recipe.ocLevel + 1); + // value is the result of adding all values in relativePositions (in onWorking) together. + float value = 0.76f * (float) Math.pow(tier, 2.62); + this.maxRemovePerSecond = cleaningPerOperation * value; return true; } return false; @@ -90,7 +99,7 @@ public boolean onWorking() { for (Direction dir : GTUtil.DIRECTIONS) { BlockPos offset = getPos().relative(dir); if (GTCapabilityHelper.getHazardContainer(getLevel(), offset, dir.getOpposite()) != null) { - if (getLevel().getBlockEntity(offset) instanceof DuctPipeBlockEntity duct && + if (getLevel().getBlockEntity(offset) instanceof PipeBlockEntity duct && !duct.isConnected(dir.getOpposite())) { continue; } @@ -102,7 +111,7 @@ public boolean onWorking() { EnvironmentalHazardSavedData savedData = EnvironmentalHazardSavedData.getOrCreate(serverLevel); final ChunkPos pos = new ChunkPos(getPos()); - Object2FloatMap relativePositions = new Object2FloatOpenHashMap<>(); + Object2FloatSortedMap relativePositions = new Object2FloatLinkedOpenHashMap<>(); int radius = tier / 2; if (radius <= 0) { // LV scrubber can only process the chunk it's in @@ -113,15 +122,24 @@ public boolean onWorking() { relativePositions.put(new ChunkPos(pos.x + x, pos.z + z), Mth.sqrt(Mth.abs(x * z)) + 1); } } + // sort positions to be lowest to highest distance so that the cleaning limit stops at the edges + // instead of possibly the center + relativePositions = relativePositions.object2FloatEntrySet().stream() + .sorted(Map.Entry.comparingByValue()) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, + Float::sum, Object2FloatLinkedOpenHashMap::new)); } - for (ChunkPos rel : relativePositions.keySet()) { - final float distance = relativePositions.getFloat(rel); - savedData.getHazardZones().compute(rel, (chunkPos, zone) -> { + for (var entry : relativePositions.object2FloatEntrySet()) { + final float distance = entry.getFloatValue(); + savedData.getHazardZones().compute(entry.getKey(), (chunkPos, zone) -> { if (zone == null || zone.strength() <= 0) { return null; } float toClean = cleaningPerOperation / distance; + if (removedLastSecond + toClean > maxRemovePerSecond) { + return zone; + } removedLastSecond += toClean; zone.removeStrength(toClean); if (zone.strength() <= 0) { @@ -136,4 +154,38 @@ public boolean onWorking() { } return true; } + + @Override + public boolean inputsHazard(Direction side, MedicalCondition condition) { + return removedLastSecond < maxRemovePerSecond; + } + + @Override + public float changeHazard(MedicalCondition condition, float amount, boolean simulate) { + if (removedLastSecond >= maxRemovePerSecond) { + return 0; + } + float result = Math.min(amount, maxRemovePerSecond - removedLastSecond); + if (!simulate) { + cleanHazard(condition, result); + removedLastSecond += result; + } + return result; + } + + // Disallow emptying hazards from scrubbers + @Override + public float removeHazard(MedicalCondition condition, float particlesToRemove, boolean simulate) { + return 0; + } + + @Override + public float getHazardStored(MedicalCondition condition) { + return removedLastSecond; + } + + @Override + public float getHazardCapacity(MedicalCondition condition) { + return maxRemovePerSecond; + } } diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/electric/BatteryBufferMachine.java b/src/main/java/com/gregtechceu/gtceu/common/machine/electric/BatteryBufferMachine.java index 81a0033b9e..a9efae3140 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/machine/electric/BatteryBufferMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/electric/BatteryBufferMachine.java @@ -232,7 +232,7 @@ public void serverTick() { long outAmps = 0L; if (genAmps > 0) { - outAmps = energyContainer.acceptEnergyFromNetwork(outFacing.getOpposite(), voltage, genAmps); + outAmps = energyContainer.acceptEnergyFromNetwork(outFacing.getOpposite(), voltage, genAmps, false); if (outAmps == 0 && internalAmps == 0) return; } @@ -260,7 +260,7 @@ public void serverTick() { } @Override - public long acceptEnergyFromNetwork(@Nullable Direction side, long voltage, long amperage) { + public long acceptEnergyFromNetwork(@Nullable Direction side, long voltage, long amperage, boolean simulate) { var latestTimeStamp = getMachine().getOffsetTimer(); if (lastTimeStamp < latestTimeStamp) { amps = 0; diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/electric/ChargerMachine.java b/src/main/java/com/gregtechceu/gtceu/common/machine/electric/ChargerMachine.java index d8a52218e2..ba8cf60360 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/machine/electric/ChargerMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/electric/ChargerMachine.java @@ -194,7 +194,7 @@ protected EnergyBatteryTrait(int inventorySize) { } @Override - public long acceptEnergyFromNetwork(@Nullable Direction side, long voltage, long amperage) { + public long acceptEnergyFromNetwork(@Nullable Direction side, long voltage, long amperage, boolean simulate) { var latestTimeStamp = getMachine().getOffsetTimer(); if (lastTimeStamp < latestTimeStamp) { amps = 0; diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/electric/WorldAcceleratorMachine.java b/src/main/java/com/gregtechceu/gtceu/common/machine/electric/WorldAcceleratorMachine.java index 24d2d80d8c..3070ba675e 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/machine/electric/WorldAcceleratorMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/electric/WorldAcceleratorMachine.java @@ -2,9 +2,9 @@ import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.GTValues; -import com.gregtechceu.gtceu.api.blockentity.PipeBlockEntity; import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; import com.gregtechceu.gtceu.api.capability.IControllable; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; import com.gregtechceu.gtceu.api.gui.GuiTextures; import com.gregtechceu.gtceu.api.item.tool.GTToolType; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/ActiveTransformerMachine.java b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/ActiveTransformerMachine.java index efdeed95fa..958e049d8e 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/ActiveTransformerMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/ActiveTransformerMachine.java @@ -128,7 +128,7 @@ private List getPrioritySortedParts() { if (PartAbility.SUBSTATION_OUTPUT_ENERGY.isApplicable(partBlock)) return 2; - if (PartAbility.OUTPUT_LASER.isApplicable(partBlock)) + if (PartAbility.LASER_TRANSMISSION.isApplicable(partBlock)) return 3; } @@ -154,8 +154,8 @@ public static TraceabilityPredicate getHatchPredicates() { .or(abilities(PartAbility.OUTPUT_ENERGY).setPreviewCount(2)) .or(abilities(PartAbility.SUBSTATION_INPUT_ENERGY).setPreviewCount(1)) .or(abilities(PartAbility.SUBSTATION_OUTPUT_ENERGY).setPreviewCount(1)) - .or(abilities(PartAbility.INPUT_LASER).setPreviewCount(1)) - .or(abilities(PartAbility.OUTPUT_LASER).setPreviewCount(1)); + .or(abilities(PartAbility.LASER_RECEPTION).setPreviewCount(1)) + .or(abilities(PartAbility.LASER_TRANSMISSION).setPreviewCount(1)); } @Override diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/CleanroomMachine.java b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/CleanroomMachine.java index 2201c4da2d..acd6cd0751 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/CleanroomMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/CleanroomMachine.java @@ -102,8 +102,8 @@ public class CleanroomMachine extends WorkableElectricMultiblockMachine @Nullable private Collection cleanroomReceivers; - public CleanroomMachine(IMachineBlockEntity metaTileEntityId) { - super(metaTileEntityId); + public CleanroomMachine(IMachineBlockEntity metaBlockEntityId) { + super(metaBlockEntityId); } ////////////////////////////////////// @@ -423,7 +423,7 @@ protected TraceabilityPredicate innerPredicate() { Set receivers = blockWorldState.getMatchContext().getOrCreate("cleanroomReceiver", Sets::newHashSet); // all non-GTMachines are allowed inside by default - BlockEntity blockEntity = blockWorldState.getTileEntity(); + BlockEntity blockEntity = blockWorldState.getBlockEntity(); if (blockEntity instanceof IMachineBlockEntity machineBlockEntity) { var machine = machineBlockEntity.getMetaMachine(); if (isMachineBanned(machine)) { diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/research/HPCAMachine.java b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/research/HPCAMachine.java index e0d55da620..d2aa8632c2 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/research/HPCAMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/research/HPCAMachine.java @@ -2,6 +2,7 @@ import com.gregtechceu.gtceu.api.GTValues; import com.gregtechceu.gtceu.api.capability.*; +import com.gregtechceu.gtceu.api.capability.data.IComputationProvider; import com.gregtechceu.gtceu.api.capability.recipe.EURecipeCapability; import com.gregtechceu.gtceu.api.capability.recipe.FluidRecipeCapability; import com.gregtechceu.gtceu.api.capability.recipe.IO; @@ -55,7 +56,6 @@ import it.unimi.dsi.fastutil.objects.ObjectArrayList; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import lombok.Getter; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; @@ -66,7 +66,7 @@ @MethodsReturnNonnullByDefault @ParametersAreNonnullByDefault public class HPCAMachine extends WorkableElectricMultiblockMachine - implements IOpticalComputationProvider, IControllable { + implements IComputationProvider, IControllable { private static final double IDLE_TEMPERATURE = 200; private static final double DAMAGE_TEMPERATURE = 1000; @@ -78,8 +78,6 @@ public class HPCAMachine extends WorkableElectricMultiblockMachine @DescSynced private final HPCAGridHandler hpcaHandler; - private boolean hasNotEnoughEnergy; - @Persisted private double temperature = IDLE_TEMPERATURE; // start at idle temperature @@ -166,20 +164,17 @@ public void onStructureInvalid() { } @Override - public int requestCWUt(int cwut, boolean simulate, @NotNull Collection seen) { - seen.add(this); - return isActive() && isWorkingEnabled() && !hasNotEnoughEnergy ? hpcaHandler.allocateCWUt(cwut, simulate) : 0; + public long supplyCWU(long requested, boolean simulate) { + return isActive() && isWorkingEnabled() ? hpcaHandler.allocateCWUt(requested, simulate) : 0; } @Override - public int getMaxCWUt(@NotNull Collection seen) { - seen.add(this); - return isActive() && isWorkingEnabled() ? hpcaHandler.getMaxCWUt() : 0; + public long maxCWUt() { + return isActive() && isWorkingEnabled() ? hpcaHandler.maxCWUt() : 0; } @Override - public boolean canBridge(@NotNull Collection seen) { - seen.add(this); + public boolean supportsBridging() { // don't show a problem if the structure is not yet formed return !isFormed() || hpcaHandler.hasHPCABridge(); } @@ -215,22 +210,25 @@ private void consumeEnergy() { energyToConsume += maintenance.getNumMaintenanceProblems() * energyToConsume / 10; } - if (this.hasNotEnoughEnergy && energyContainer.getInputPerSec() > 19L * energyToConsume) { - this.hasNotEnoughEnergy = false; + if (recipeLogic.getStatus() == RecipeLogic.Status.SUSPEND) { + return; + } + + if (recipeLogic.getStatus() == RecipeLogic.Status.WAITING && + energyContainer.getInputPerSec() > 19L * energyToConsume) { + recipeLogic.setStatus(RecipeLogic.Status.WORKING); } if (this.energyContainer.getEnergyStored() >= energyToConsume) { - if (!hasNotEnoughEnergy) { + if (recipeLogic.getStatus() != RecipeLogic.Status.WAITING) { long consumed = this.energyContainer.removeEnergy(energyToConsume); if (consumed == energyToConsume) { getRecipeLogic().setStatus(RecipeLogic.Status.WORKING); } else { - this.hasNotEnoughEnergy = true; getRecipeLogic().setStatus(RecipeLogic.Status.WAITING); } } } else { - this.hasNotEnoughEnergy = true; getRecipeLogic().setStatus(RecipeLogic.Status.WAITING); } } @@ -287,7 +285,7 @@ public void addDisplayText(List textList) { // Provided Computation Component cwutInfo = Component.literal( - hpcaHandler.cachedCWUt + " / " + hpcaHandler.getMaxCWUt() + " CWU/t") + hpcaHandler.cachedCWUt + " / " + hpcaHandler.maxCWUt() + " CWU/t") .withStyle(ChatFormatting.AQUA); tl.add(Component.translatable( "gtceu.multiblock.hpca.computation", @@ -446,7 +444,7 @@ public void tick() { */ public double calculateTemperatureChange(IFluidHandler coolantTank, boolean forceCoolWithActive) { // calculate temperature increase - int maxCWUt = Math.max(1, getMaxCWUt()); // avoids dividing by 0 and the behavior is no different + int maxCWUt = Math.max(1, maxCWUt()); // avoids dividing by 0 and the behavior is no different int maxCoolingDemand = getMaxCoolingDemand(); // temperature increase is proportional to the amount of actively used computation @@ -541,10 +539,10 @@ public void attemptDamageHPCA() { } /** Allocate computation on a given request. Allocates for one tick. */ - public int allocateCWUt(int cwut, boolean simulate) { - int maxCWUt = getMaxCWUt(); + public long allocateCWUt(long requested, boolean simulate) { + int maxCWUt = maxCWUt(); int availableCWUt = maxCWUt - this.allocatedCWUt; - int toAllocate = Math.min(cwut, availableCWUt); + long toAllocate = Math.min(requested, availableCWUt); if (!simulate) { this.allocatedCWUt += toAllocate; } @@ -552,7 +550,7 @@ public int allocateCWUt(int cwut, boolean simulate) { } /** The maximum amount of CWUs (Compute Work Units) created per tick. */ - public int getMaxCWUt() { + public int maxCWUt() { int maxCWUt = 0; for (var computationProvider : computationProviders) { maxCWUt += computationProvider.getCWUPerTick(); @@ -562,7 +560,7 @@ public int getMaxCWUt() { /** The current EU/t this HPCA should use, considering passive drain, current computation, etc.. */ public long getCurrentEUt() { - long maximumCWUt = Math.max(1, getMaxCWUt()); // behavior is no different setting this to 1 if it is 0 + long maximumCWUt = Math.max(1, maxCWUt()); // behavior is no different setting this to 1 if it is 0 long maximumEUt = getMaxEUt(); long upkeepEUt = getUpkeepEUt(); @@ -635,7 +633,7 @@ public int getMaxCoolantDemand() { public void addInfo(List textList) { // Max Computation - MutableComponent data = Component.literal(Integer.toString(getMaxCWUt())).withStyle(ChatFormatting.AQUA); + MutableComponent data = Component.literal(Integer.toString(maxCWUt())).withStyle(ChatFormatting.AQUA); textList.add(Component.translatable("gtceu.multiblock.hpca.info_max_computation", data) .withStyle(ChatFormatting.GRAY)); diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/research/NetworkSwitchMachine.java b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/research/NetworkSwitchMachine.java index f402658b8a..debf095c19 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/research/NetworkSwitchMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/research/NetworkSwitchMachine.java @@ -1,38 +1,29 @@ package com.gregtechceu.gtceu.common.machine.multiblock.electric.research; import com.gregtechceu.gtceu.api.GTValues; -import com.gregtechceu.gtceu.api.capability.IOpticalComputationHatch; -import com.gregtechceu.gtceu.api.capability.IOpticalComputationProvider; -import com.gregtechceu.gtceu.api.capability.recipe.IO; +import com.gregtechceu.gtceu.api.capability.data.IDataAccess; +import com.gregtechceu.gtceu.api.capability.data.query.ComputationQuery; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; -import com.gregtechceu.gtceu.api.machine.MetaMachine; import com.gregtechceu.gtceu.api.machine.multiblock.MultiblockDisplayText; import com.gregtechceu.gtceu.api.machine.multiblock.PartAbility; -import com.gregtechceu.gtceu.api.machine.trait.NotifiableComputationContainer; +import com.gregtechceu.gtceu.utils.GTUtil; import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.network.chat.Component; import net.minecraft.world.level.block.Block; -import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; -import lombok.AccessLevel; -import lombok.Getter; -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; -import java.util.Collection; import java.util.List; -import java.util.Set; import javax.annotation.ParametersAreNonnullByDefault; @MethodsReturnNonnullByDefault @ParametersAreNonnullByDefault -public class NetworkSwitchMachine extends DataBankMachine implements IOpticalComputationProvider { +public class NetworkSwitchMachine extends DataBankMachine { public static final int EUT_PER_HATCH = GTValues.VA[GTValues.IV]; - private final MultipleComputationHandler computationHandler = new MultipleComputationHandler(this); + private long nextQueryTick; + private ComputationQuery query; public NetworkSwitchMachine(IMachineBlockEntity holder) { super(holder); @@ -51,68 +42,7 @@ protected int calculateEnergyUsage() { ++transmitters; } } - return GTValues.VA[GTValues.IV] * (receivers + transmitters); - } - - @Override - public void onStructureFormed() { - super.onStructureFormed(); - List receivers = new ArrayList<>(); - List transmitters = new ArrayList<>(); - for (var part : this.getParts()) { - if (part instanceof IOpticalComputationHatch hatch) { - Block block = part.self().getBlockState().getBlock(); - if (PartAbility.COMPUTATION_DATA_RECEPTION.isApplicable(block)) { - receivers.add(hatch); - } - if (PartAbility.COMPUTATION_DATA_TRANSMISSION.isApplicable(block)) { - transmitters.add(hatch); - } - } else if (part.getRecipeHandlers().stream().anyMatch(IOpticalComputationHatch.class::isInstance)) { - var hatch = part.getRecipeHandlers().stream().filter(IOpticalComputationHatch.class::isInstance) - .map(IOpticalComputationHatch.class::cast).findFirst().orElse(null); - if (hatch != null) { - Block block = part.self().getBlockState().getBlock(); - if (PartAbility.COMPUTATION_DATA_RECEPTION.isApplicable(block)) { - receivers.add(hatch); - } - if (PartAbility.COMPUTATION_DATA_TRANSMISSION.isApplicable(block)) { - transmitters.add(hatch); - } - } - } - } - computationHandler.onStructureForm(receivers, transmitters); - } - - @Override - public void onStructureInvalid() { - super.onStructureInvalid(); - computationHandler.reset(); - } - - @Override - public int getEnergyUsage() { - return isFormed() ? computationHandler.getEUt() : 0; - } - - @Override - public int requestCWUt(int cwut, boolean simulate, @NotNull Collection seen) { - seen.add(this); - return isActive() && !getRecipeLogic().isWaiting() ? computationHandler.requestCWUt(cwut, simulate, seen) : 0; - } - - @Override - public int getMaxCWUt(@NotNull Collection seen) { - seen.add(this); - return isFormed() ? computationHandler.getMaxCWUt(seen) : 0; - } - - // allows chaining Network Switches together - @Override - public boolean canBridge(@NotNull Collection seen) { - seen.add(this); - return true; + return EUT_PER_HATCH * (receivers + transmitters); } @Override @@ -124,7 +54,7 @@ public void addDisplayText(List textList) { "gtceu.multiblock.idling", "gtceu.multiblock.data_bank.providing") .addEnergyUsageExactLine(getEnergyUsage()) - .addComputationUsageLine(computationHandler.getMaxCWUtForDisplay()) + .addComputationUsageLine(queryConnected().maxCWUt()) .addWorkingStatusLine(); } @@ -139,105 +69,17 @@ public void addDisplayText(List textList) { * } */ - /** Handles computation load across multiple receivers and to multiple transmitters. */ - private static class MultipleComputationHandler extends NotifiableComputationContainer { - - // providers in the NS provide distributable computation to the NS - private final Set providers = new ObjectOpenHashSet<>(); - // transmitters in the NS give computation to other multis - private final Set transmitters = new ObjectOpenHashSet<>(); - - /** The EU/t cost of this Network Switch given the attached providers and transmitters. */ - @Getter(value = AccessLevel.PRIVATE) - private int EUt; - - public MultipleComputationHandler(MetaMachine machine) { - super(machine, IO.IN, false); - } - - private void onStructureForm(Collection providers, - Collection transmitters) { - reset(); - this.providers.addAll(providers); - this.transmitters.addAll(transmitters); - this.EUt = (providers.size() + transmitters.size()) * EUT_PER_HATCH; - } - - private void reset() { - providers.clear(); - transmitters.clear(); - EUt = 0; - } - - @Override - public int requestCWUt(int cwut, boolean simulate, @NotNull Collection seen) { - if (seen.contains(this)) return 0; - // The max CWU/t that this Network Switch can provide, combining all its inputs. - seen.add(this); - Collection bridgeSeen = new ArrayList<>(seen); - int allocatedCWUt = 0; - for (var provider : providers) { - if (!provider.canBridge(bridgeSeen)) continue; - int allocated = provider.requestCWUt(cwut, simulate, seen); - allocatedCWUt += allocated; - cwut -= allocated; - if (cwut == 0) break; - } - return allocatedCWUt; - } - - public int getMaxCWUtForDisplay() { - Collection seen = new ArrayList<>(); - // The max CWU/t that this Network Switch can provide, combining all its inputs. - seen.add(this); - Collection bridgeSeen = new ArrayList<>(seen); - int maximumCWUt = 0; - for (var provider : providers) { - if (!provider.canBridge(bridgeSeen)) continue; - maximumCWUt += provider.getMaxCWUt(seen); - } - return maximumCWUt; - } - - public int getMaxCWUt(@NotNull Collection seen) { - if (seen.contains(this)) return 0; - // The max CWU/t that this Network Switch can provide, combining all its inputs. - seen.add(this); - Collection bridgeSeen = new ArrayList<>(seen); - int maximumCWUt = 0; - for (var provider : providers) { - if (!provider.canBridge(bridgeSeen)) continue; - maximumCWUt += provider.getMaxCWUt(seen); - } - return maximumCWUt; - } - - @Override - public boolean canBridge(@NotNull Collection seen) { - if (seen.contains(this)) return false; - seen.add(this); - for (var provider : providers) { - if (provider.canBridge(seen)) { - return true; - } - } - return false; - } - - /** Test if any of the provider hatches do not allow bridging */ - private boolean hasNonBridgingConnections() { - Collection seen = new ArrayList<>(); - for (var provider : providers) { - if (!provider.canBridge(seen)) { - return true; - } - } - return false; - } - - @Override - public IOpticalComputationProvider getComputationProvider() { - return this; + private ComputationQuery queryConnected() { + long tick = GTUtil.getCurrentServerTick(); + if (tick >= nextQueryTick) { + this.query = new ComputationQuery(); + List dataAccesses = getParts().stream() + .filter(IDataAccess.class::isInstance) + .map(IDataAccess.class::cast) + .toList(); + IDataAccess.accessData(dataAccesses, query); + this.nextQueryTick = tick + 10; } + return this.query; } } diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/research/ResearchStationMachine.java b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/research/ResearchStationMachine.java index da8cc09883..56319b1290 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/research/ResearchStationMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/electric/research/ResearchStationMachine.java @@ -1,9 +1,9 @@ package com.gregtechceu.gtceu.common.machine.multiblock.electric.research; import com.gregtechceu.gtceu.api.capability.IObjectHolder; -import com.gregtechceu.gtceu.api.capability.IOpticalComputationProvider; -import com.gregtechceu.gtceu.api.capability.IOpticalComputationReceiver; -import com.gregtechceu.gtceu.api.capability.forge.GTCapability; +import com.gregtechceu.gtceu.api.capability.data.IComputationUser; +import com.gregtechceu.gtceu.api.capability.data.IDataAccess; +import com.gregtechceu.gtceu.api.capability.data.query.ComputationQuery; import com.gregtechceu.gtceu.api.capability.recipe.CWURecipeCapability; import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.capability.recipe.IRecipeCapabilityHolder; @@ -34,10 +34,8 @@ @MethodsReturnNonnullByDefault @ParametersAreNonnullByDefault public class ResearchStationMachine extends WorkableElectricMultiblockMachine - implements IOpticalComputationReceiver, IDisplayUIMachine { + implements IComputationUser, IDisplayUIMachine { - @Getter - private IOpticalComputationProvider computationProvider; @Getter private IObjectHolder objectHolder; @@ -59,36 +57,26 @@ public ResearchStationRecipeLogic getRecipeLogic() { public void onStructureFormed() { super.onStructureFormed(); for (IMultiPart part : getParts()) { - IOpticalComputationProvider provider = part.self().holder.self() - .getCapability(GTCapability.CAPABILITY_COMPUTATION_PROVIDER).resolve().orElse(null); - if (provider != null) { - this.computationProvider = provider; - } - if (part instanceof IObjectHolder objectHolder) { - this.objectHolder = objectHolder; + if (part instanceof IObjectHolder holder) { + if (part.self().getFrontFacing() != this.getFrontFacing().getOpposite()) { + onStructureInvalid(); + return; + } + this.objectHolder = holder; this.getCapabilitiesProxy().put(IO.IN, ItemRecipeCapability.CAP, - Collections.singletonList(objectHolder.getAsHandler())); + Collections.singletonList(holder.getAsHandler())); + break; } } // should never happen, but would rather do this than have an obscure NPE - if (computationProvider == null || objectHolder == null) { - onStructureInvalid(); - } - } - - @Override - public boolean checkPattern() { - boolean isFormed = super.checkPattern(); - if (isFormed && objectHolder != null && objectHolder.getFrontFacing() != getFrontFacing().getOpposite()) { + if (objectHolder == null) { onStructureInvalid(); } - return isFormed; } @Override public void onStructureInvalid() { - computationProvider = null; // recheck the ability to make sure it wasn't the one broken for (IMultiPart part : getParts()) { if (part instanceof IObjectHolder holder) { @@ -106,6 +94,21 @@ public boolean dampingWhenWaiting() { return false; } + @Override + public long requestCWU(long requested, boolean simulate) { + return queryConnected().requestCWU(requested, simulate); + } + + private ComputationQuery queryConnected() { + ComputationQuery query = new ComputationQuery(); + List dataAccesses = getParts().stream() + .filter(IDataAccess.class::isInstance) + .map(IDataAccess.class::cast) + .toList(); + IDataAccess.accessData(dataAccesses, query); + return query; + } + @Override public void addDisplayText(List textList) { MultiblockDisplayText.builder(textList, isFormed()) @@ -121,8 +124,8 @@ public void addDisplayText(List textList) { private static class ResearchStationRecipeLogic extends RecipeLogic { - public ResearchStationRecipeLogic(ResearchStationMachine metaTileEntity) { - super(metaTileEntity); + public ResearchStationRecipeLogic(ResearchStationMachine metaBlockEntity) { + super(metaBlockEntity); } @NotNull @@ -237,9 +240,10 @@ public void onRecipeFinish() { holder.setHeldItem(ItemStack.EMPTY); ItemStack outputItem = ItemStack.EMPTY; - if (lastRecipe.getOutputContents(ItemRecipeCapability.CAP).size() >= 1) { + var outputContents = lastRecipe.getOutputContents(ItemRecipeCapability.CAP); + if (!outputContents.isEmpty()) { outputItem = ItemRecipeCapability.CAP - .of(getLastRecipe().getOutputContents(ItemRecipeCapability.CAP).get(0).content).getItems()[0]; + .of(outputContents.get(0).content).getItems()[0]; } holder.setDataItem(outputItem); holder.setLocked(false); diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/CleaningMaintenanceHatchPartMachine.java b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/CleaningMaintenanceHatchPartMachine.java index 5af6892bc5..56a2218952 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/CleaningMaintenanceHatchPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/CleaningMaintenanceHatchPartMachine.java @@ -28,8 +28,8 @@ public class CleaningMaintenanceHatchPartMachine extends AutoMaintenanceHatchPar @Getter private final CleanroomType cleanroomType; - public CleaningMaintenanceHatchPartMachine(IMachineBlockEntity metaTileEntityId, CleanroomType cleanroomType) { - super(metaTileEntityId); + public CleaningMaintenanceHatchPartMachine(IMachineBlockEntity holder, CleanroomType cleanroomType) { + super(holder); this.cleanroomType = cleanroomType; DUMMY_CLEANROOM = DummyCleanroom.createForTypes(Collections.singletonList(cleanroomType)); } diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/OpticalComputationHatchMachine.java b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/ComputationHatchMachine.java similarity index 90% rename from src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/OpticalComputationHatchMachine.java rename to src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/ComputationHatchMachine.java index ebd6f03658..0659c60b1e 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/OpticalComputationHatchMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/ComputationHatchMachine.java @@ -16,14 +16,14 @@ @MethodsReturnNonnullByDefault @ParametersAreNonnullByDefault -public class OpticalComputationHatchMachine extends MultiblockPartMachine { +public class ComputationHatchMachine extends MultiblockPartMachine { @Getter private final boolean transmitter; protected NotifiableComputationContainer computationContainer; - public OpticalComputationHatchMachine(IMachineBlockEntity holder, boolean transmitter) { + public ComputationHatchMachine(IMachineBlockEntity holder, boolean transmitter) { super(holder); this.transmitter = transmitter; this.computationContainer = createComputationContainer(transmitter); diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/DataAccessHatchMachine.java b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/DataAccessHatchMachine.java index 32389e8ee7..3b2e703675 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/DataAccessHatchMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/DataAccessHatchMachine.java @@ -1,7 +1,10 @@ package com.gregtechceu.gtceu.common.machine.multiblock.part; import com.gregtechceu.gtceu.api.GTValues; -import com.gregtechceu.gtceu.api.capability.IDataAccessHatch; +import com.gregtechceu.gtceu.api.capability.data.IDataAccess; +import com.gregtechceu.gtceu.api.capability.data.query.DataAccessFormat; +import com.gregtechceu.gtceu.api.capability.data.query.DataQueryObject; +import com.gregtechceu.gtceu.api.capability.data.query.RecipeDataQuery; import com.gregtechceu.gtceu.api.capability.recipe.IO; import com.gregtechceu.gtceu.api.capability.recipe.ItemRecipeCapability; import com.gregtechceu.gtceu.api.gui.GuiTextures; @@ -46,7 +49,7 @@ @MethodsReturnNonnullByDefault @ParametersAreNonnullByDefault public class DataAccessHatchMachine extends TieredPartMachine - implements IMachineLife, IDataAccessHatch, IDataInfoProvider { + implements IMachineLife, IDataAccess, IDataInfoProvider { protected static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( DataAccessHatchMachine.class, MultiblockPartMachine.MANAGED_FIELD_HOLDER); @@ -137,9 +140,19 @@ private void rebuildData(boolean isDataBank) { } @Override - public boolean isRecipeAvailable(@NotNull GTRecipe recipe, @NotNull Collection seen) { - seen.add(this); - return recipe.conditions.stream().noneMatch(ResearchCondition.class::isInstance) || recipes.contains(recipe); + public boolean accessData(@NotNull DataQueryObject queryObject) { + if (queryObject instanceof RecipeDataQuery query) { + if (isCreative || recipes.contains(query.getRecipe())) { + query.setFound(); + return true; + } + } + return false; + } + + @Override + public @NotNull DataAccessFormat getFormat() { + return DataAccessFormat.STANDARD; } @NotNull @@ -180,7 +193,16 @@ public void addedToController(IMultiController controller) { @Override public GTRecipe modifyRecipe(GTRecipe recipe) { - return IDataAccessHatch.super.modifyRecipe(recipe); + // creative hatches do not need to check, they always have the recipe + if (this.isCreative()) return recipe; + if (recipe.conditions.stream().noneMatch(ResearchCondition.class::isInstance)) return recipe; + + // hatches need to have the recipe available + RecipeDataQuery query = new RecipeDataQuery(recipe); + if (query.traverseTo(this) && this.accessData(query)) { + return recipe; + } + return null; } @Override diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/MaintenanceHatchPartMachine.java b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/MaintenanceHatchPartMachine.java index 0d7c00d5b2..4b881921f4 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/MaintenanceHatchPartMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/MaintenanceHatchPartMachine.java @@ -88,8 +88,8 @@ public class MaintenanceHatchPartMachine extends TieredPartMachine @Nullable protected TickableSubscription maintenanceSubs; - public MaintenanceHatchPartMachine(IMachineBlockEntity metaTileEntityId, boolean isConfigurable) { - super(metaTileEntityId, isConfigurable ? 3 : 1); + public MaintenanceHatchPartMachine(IMachineBlockEntity metaBlockEntityId, boolean isConfigurable) { + super(metaBlockEntityId, isConfigurable ? 3 : 1); this.isConfigurable = isConfigurable; this.itemStackHandler = createInventory(); this.itemStackHandler.setFilter(itemStack -> itemStack.is(GTItems.DUCT_TAPE.get())); diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/ObjectHolderMachine.java b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/ObjectHolderMachine.java index 22ef0219ce..004c4254f6 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/ObjectHolderMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/ObjectHolderMachine.java @@ -122,8 +122,8 @@ public ManagedFieldHolder getFieldHolder() { private class ObjectHolderHandler extends NotifiableItemStackHandler { - public ObjectHolderHandler(MetaMachine metaTileEntity) { - super(metaTileEntity, 2, IO.IN, IO.BOTH, size -> new CustomItemStackHandler(size) { + public ObjectHolderHandler(MetaMachine machine) { + super(machine, 2, IO.IN, IO.BOTH, size -> new CustomItemStackHandler(size) { @Override public int getSlotLimit(int slot) { diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/OpticalDataHatchMachine.java b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/OpticalDataHatchMachine.java index 8f9719c6b0..84beee9771 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/OpticalDataHatchMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/multiblock/part/OpticalDataHatchMachine.java @@ -1,15 +1,17 @@ package com.gregtechceu.gtceu.common.machine.multiblock.part; -import com.gregtechceu.gtceu.api.capability.IDataAccessHatch; -import com.gregtechceu.gtceu.api.capability.IOpticalDataAccessHatch; import com.gregtechceu.gtceu.api.capability.IWorkable; +import com.gregtechceu.gtceu.api.capability.data.IDataAccess; +import com.gregtechceu.gtceu.api.capability.data.IStandardDataAccess; +import com.gregtechceu.gtceu.api.capability.data.query.DataQueryObject; +import com.gregtechceu.gtceu.api.capability.data.query.IBridgeable; +import com.gregtechceu.gtceu.api.capability.data.query.RecipeDataQuery; import com.gregtechceu.gtceu.api.capability.forge.GTCapability; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; import com.gregtechceu.gtceu.api.machine.feature.multiblock.IMultiController; import com.gregtechceu.gtceu.api.machine.multiblock.PartAbility; import com.gregtechceu.gtceu.api.machine.multiblock.part.MultiblockPartMachine; import com.gregtechceu.gtceu.api.recipe.GTRecipe; -import com.gregtechceu.gtceu.common.blockentity.OpticalPipeBlockEntity; import net.minecraft.MethodsReturnNonnullByDefault; import net.minecraft.world.InteractionHand; @@ -22,14 +24,13 @@ import org.jetbrains.annotations.NotNull; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import javax.annotation.ParametersAreNonnullByDefault; @MethodsReturnNonnullByDefault @ParametersAreNonnullByDefault -public class OpticalDataHatchMachine extends MultiblockPartMachine implements IOpticalDataAccessHatch { +public class OpticalDataHatchMachine extends MultiblockPartMachine implements IStandardDataAccess { @Getter private final boolean isTransmitter; @@ -40,61 +41,44 @@ public OpticalDataHatchMachine(IMachineBlockEntity holder, boolean isTransmitter } @Override - public boolean isRecipeAvailable(@NotNull GTRecipe recipe, @NotNull Collection seen) { - seen.add(this); + public boolean accessData(@NotNull DataQueryObject queryObject) { if (!getControllers().isEmpty()) { if (isTransmitter()) { IMultiController controller = getControllers().get(0); - if (!(controller instanceof IWorkable workable) || !workable.isActive()) return false; + if (!controller.isFormed() || (controller instanceof IWorkable workable && !workable.isActive())) + return false; - List dataAccesses = new ArrayList<>(); - List transmitters = new ArrayList<>(); + List dataAccesses = new ArrayList<>(); + List reception = new ArrayList<>(); for (var part : controller.getParts()) { Block block = part.self().getBlockState().getBlock(); - if (part instanceof IDataAccessHatch hatch && PartAbility.DATA_ACCESS.isApplicable(block)) { + if (part instanceof IDataAccess hatch && PartAbility.DATA_ACCESS.isApplicable(block)) { dataAccesses.add(hatch); } - if (part instanceof IDataAccessHatch hatch && + if (part instanceof IStandardDataAccess hatch && PartAbility.OPTICAL_DATA_RECEPTION.isApplicable(block)) { - transmitters.add(hatch); + reception.add(hatch); } } - return isRecipeAvailable(dataAccesses, seen, recipe) || - isRecipeAvailable(transmitters, seen, recipe); - } else { - BlockEntity tileEntity = getLevel().getBlockEntity(getPos().relative(getFrontFacing())); - if (tileEntity == null) return false; + if (IDataAccess.accessData(dataAccesses, queryObject)) + return true; - if (tileEntity instanceof OpticalPipeBlockEntity) { - // noinspection DataFlowIssue - IDataAccessHatch cap = tileEntity.getCapability(GTCapability.CAPABILITY_DATA_ACCESS, - getFrontFacing().getOpposite()).orElse(null); - // noinspection ConstantValue - return cap != null && cap.isRecipeAvailable(recipe, seen); + if (queryObject instanceof IBridgeable bridgeable && reception.size() > 1) { + bridgeable.setBridged(); } - } - } - return false; - } - - private static boolean isRecipeAvailable(@NotNull Iterable hatches, - @NotNull Collection seen, - @NotNull GTRecipe recipe) { - for (IDataAccessHatch hatch : hatches) { - if (seen.contains(hatch)) continue; - if (hatch.isRecipeAvailable(recipe, seen)) { - return true; + return IDataAccess.accessData(reception, queryObject); + } else { + BlockEntity tileEntity = getNeighbor(getFrontFacing()); + if (tileEntity == null) return false; + IDataAccess cap = tileEntity.getCapability(GTCapability.CAPABILITY_DATA_ACCESS, + getFrontFacing().getOpposite()).resolve().orElse(null); + if (queryObject.traverseTo(cap)) return cap.accessData(queryObject); } } return false; } - @Override - public boolean isCreative() { - return false; - } - @Override public boolean shouldOpenUI(Player player, InteractionHand hand, BlockHitResult hit) { return false; @@ -107,6 +91,11 @@ public boolean canShared() { @Override public GTRecipe modifyRecipe(GTRecipe recipe) { - return IOpticalDataAccessHatch.super.modifyRecipe(recipe); + // creative hatches do not need to check, they always have the recipe + RecipeDataQuery query = new RecipeDataQuery(recipe); + + // hatches need to have the recipe available + if (this.accessData(query)) return recipe; + return null; } } diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/storage/CreativeComputationProviderMachine.java b/src/main/java/com/gregtechceu/gtceu/common/machine/storage/CreativeComputationProviderMachine.java index ce8a64fd97..bb49427b00 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/machine/storage/CreativeComputationProviderMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/storage/CreativeComputationProviderMachine.java @@ -1,6 +1,6 @@ package com.gregtechceu.gtceu.common.machine.storage; -import com.gregtechceu.gtceu.api.capability.IOpticalComputationProvider; +import com.gregtechceu.gtceu.api.capability.data.IComputationProvider; import com.gregtechceu.gtceu.api.gui.GuiTextures; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; import com.gregtechceu.gtceu.api.machine.MetaMachine; @@ -21,25 +21,22 @@ import net.minecraft.world.entity.player.Player; import lombok.Getter; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Collection; - import javax.annotation.ParametersAreNonnullByDefault; @ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault public class CreativeComputationProviderMachine extends MetaMachine - implements IUIMachine, IOpticalComputationProvider { + implements IUIMachine, IComputationProvider { public static final ManagedFieldHolder MANAGED_FIELD_HOLDER = new ManagedFieldHolder( CreativeComputationProviderMachine.class, MetaMachine.MANAGED_FIELD_HOLDER); @Persisted - private int maxCWUt; - private int lastRequestedCWUt; - private int requestedCWUPerSec; + private long maxCWUt; + private long lastRequestedCWUt; + private long requestedCWUPerSec; @Persisted @Getter private boolean active; @@ -75,10 +72,8 @@ protected void updateComputationTick() { } @Override - public int requestCWUt( - int cwut, boolean simulate, @NotNull Collection seen) { - seen.add(this); - int requestedCWUt = active ? Math.min(cwut, maxCWUt) : 0; + public long supplyCWU(long cwut, boolean simulate) { + long requestedCWUt = active ? Math.min(cwut, maxCWUt) : 0; if (!simulate) { this.requestedCWUPerSec += requestedCWUt; } @@ -86,14 +81,12 @@ public int requestCWUt( } @Override - public int getMaxCWUt(@NotNull Collection seen) { - seen.add(this); + public long maxCWUt() { return active ? maxCWUt : 0; } @Override - public boolean canBridge(@NotNull Collection seen) { - seen.add(this); + public boolean supportsBridging() { return true; } diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/storage/CreativeEnergyContainerMachine.java b/src/main/java/com/gregtechceu/gtceu/common/machine/storage/CreativeEnergyContainerMachine.java index 7f16e0bcee..e22fc91c44 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/machine/storage/CreativeEnergyContainerMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/storage/CreativeEnergyContainerMachine.java @@ -4,6 +4,7 @@ import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; import com.gregtechceu.gtceu.api.capability.IEnergyContainer; import com.gregtechceu.gtceu.api.capability.ILaserContainer; +import com.gregtechceu.gtceu.api.capability.ILaserRelay; import com.gregtechceu.gtceu.api.gui.GuiTextures; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; import com.gregtechceu.gtceu.api.machine.MetaMachine; @@ -87,17 +88,22 @@ protected void updateEnergyTick() { } ampsReceived = 0; if (!active || !source || voltage <= 0 || amps <= 0) return; - int ampsUsed = 0; + long ampsUsed = 0; for (var facing : GTUtil.DIRECTIONS) { var opposite = facing.getOpposite(); IEnergyContainer container = GTCapabilityHelper.getEnergyContainer(getLevel(), getPos().relative(facing), opposite); // Try to get laser capability - if (container == null) - container = GTCapabilityHelper.getLaser(getLevel(), getPos().relative(facing), opposite); + if (container == null) { + ILaserRelay relay = GTCapabilityHelper.getLaser(getLevel(), getPos().relative(facing), opposite); + if (relay != null) { + ampsUsed += relay.receiveLaser(voltage, amps - ampsUsed); + } + + } if (container != null && container.inputsEnergy(opposite) && container.getEnergyCanBeInserted() > 0) { - ampsUsed += container.acceptEnergyFromNetwork(opposite, voltage, amps - ampsUsed); + ampsUsed += container.acceptEnergyFromNetwork(opposite, voltage, amps - ampsUsed, false); if (ampsUsed >= amps) { break; } @@ -107,7 +113,7 @@ protected void updateEnergyTick() { } @Override - public long acceptEnergyFromNetwork(Direction side, long voltage, long amperage) { + public long acceptEnergyFromNetwork(Direction side, long voltage, long amperage, boolean simulate) { if (source || !active || ampsReceived >= amps) { return 0; } @@ -244,4 +250,9 @@ public ModularUI createUI(Player entityPlayer) { .setBackground(ColorPattern.BLACK.rectTexture()) .setValue(GTValues.VNF[setTier])); } + + @Override + public long receiveLaser(long laserVoltage, long laserAmperage) { + return acceptEnergyFromNetwork(null, laserVoltage, laserAmperage, false); + } } diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/storage/DrumMachine.java b/src/main/java/com/gregtechceu/gtceu/common/machine/storage/DrumMachine.java index ca3df71a46..b0085b6332 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/machine/storage/DrumMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/storage/DrumMachine.java @@ -12,6 +12,7 @@ import com.gregtechceu.gtceu.api.machine.feature.IDropSaveMachine; import com.gregtechceu.gtceu.api.machine.feature.IInteractedMachine; import com.gregtechceu.gtceu.api.machine.trait.NotifiableFluidTank; +import com.gregtechceu.gtceu.common.pipelike.handlers.properties.MaterialFluidProperties; import com.gregtechceu.gtceu.utils.GTTransferUtils; import com.lowdragmc.lowdraglib.gui.texture.ResourceTexture; @@ -91,7 +92,8 @@ public ManagedFieldHolder getFieldHolder() { protected NotifiableFluidTank createCacheFluidHandler(Object... args) { return new NotifiableFluidTank(this, 1, maxStoredFluids, IO.BOTH) - .setFilter(material.getProperty(PropertyKey.FLUID_PIPE)); + .setFilter( + material.getProperty(PropertyKey.PIPENET_PROPERTIES).getProperty(MaterialFluidProperties.KEY)); } @Override diff --git a/src/main/java/com/gregtechceu/gtceu/common/machine/trait/miner/SteamMinerLogic.java b/src/main/java/com/gregtechceu/gtceu/common/machine/trait/miner/SteamMinerLogic.java index e06945ab6c..118139121c 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/machine/trait/miner/SteamMinerLogic.java +++ b/src/main/java/com/gregtechceu/gtceu/common/machine/trait/miner/SteamMinerLogic.java @@ -8,13 +8,13 @@ public class SteamMinerLogic extends MinerLogic { /** * Creates the logic for steam miners * - * @param metaTileEntity the {@link IRecipeLogicMachine} this logic belongs to - * @param fortune the fortune amount to apply when mining ores - * @param speed the speed in ticks per block mined - * @param maximumRadius the maximum radius (square shaped) the miner can mine in + * @param metaBlockEntity the {@link IRecipeLogicMachine} this logic belongs to + * @param fortune the fortune amount to apply when mining ores + * @param speed the speed in ticks per block mined + * @param maximumRadius the maximum radius (square shaped) the miner can mine in */ - public SteamMinerLogic(IRecipeLogicMachine metaTileEntity, int fortune, int speed, int maximumRadius) { - super(metaTileEntity, fortune, speed, maximumRadius); + public SteamMinerLogic(IRecipeLogicMachine metaBlockEntity, int fortune, int speed, int maximumRadius) { + super(metaBlockEntity, fortune, speed, maximumRadius); } @Override diff --git a/src/main/java/com/gregtechceu/gtceu/common/network/GTNetwork.java b/src/main/java/com/gregtechceu/gtceu/common/network/GTNetwork.java index 9018e4c8b1..dde4b84bd2 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/network/GTNetwork.java +++ b/src/main/java/com/gregtechceu/gtceu/common/network/GTNetwork.java @@ -19,6 +19,8 @@ public class GTNetwork { public static void init() { NETWORK.registerC2S(CPacketKeysPressed.class); + NETWORK.registerS2C(SPacketSyncTickCount.class); + NETWORK.registerS2C(SPacketSyncOreVeins.class); NETWORK.registerS2C(SPacketSyncFluidVeins.class); NETWORK.registerS2C(SPacketSyncBedrockOreVeins.class); diff --git a/src/main/java/com/gregtechceu/gtceu/common/network/packets/SPacketSyncTickCount.java b/src/main/java/com/gregtechceu/gtceu/common/network/packets/SPacketSyncTickCount.java new file mode 100644 index 0000000000..00347e27ec --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/network/packets/SPacketSyncTickCount.java @@ -0,0 +1,36 @@ +package com.gregtechceu.gtceu.common.network.packets; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.client.ClientProxy; + +import com.lowdragmc.lowdraglib.networking.IHandlerContext; +import com.lowdragmc.lowdraglib.networking.IPacket; + +import net.minecraft.network.FriendlyByteBuf; + +import lombok.AllArgsConstructor; + +@AllArgsConstructor +public class SPacketSyncTickCount implements IPacket { + + private long serverTickCount; + + public SPacketSyncTickCount() { + serverTickCount = GTCEu.getMinecraftServer().getTickCount(); + } + + @Override + public void encode(FriendlyByteBuf buf) { + buf.writeVarLong(serverTickCount); + } + + @Override + public void decode(FriendlyByteBuf buf) { + serverTickCount = buf.readVarLong(); + } + + @Override + public void execute(IHandlerContext handler) { + ClientProxy.setServerTickCount(serverTickCount); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/block/cable/CableBlock.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/block/cable/CableBlock.java new file mode 100644 index 0000000000..948017dc48 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/block/cable/CableBlock.java @@ -0,0 +1,135 @@ +package com.gregtechceu.gtceu.common.pipelike.block.cable; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.data.chemical.material.Material; +import com.gregtechceu.gtceu.api.graphnet.pipenet.IPipeNetNodeHandler; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IBurnable; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.block.PipeMaterialBlock; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; +import com.gregtechceu.gtceu.api.item.tool.GTToolType; +import com.gregtechceu.gtceu.client.renderer.pipe.CableModel; +import com.gregtechceu.gtceu.common.data.GTDamageTypes; +import com.gregtechceu.gtceu.common.pipelike.net.energy.EnergyFlowData; +import com.gregtechceu.gtceu.common.pipelike.net.energy.EnergyFlowLogic; +import com.gregtechceu.gtceu.common.pipelike.net.energy.SuperconductorLogic; +import com.gregtechceu.gtceu.common.pipelike.net.energy.WorldEnergyNet; +import com.gregtechceu.gtceu.utils.GTUtil; + +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.level.BlockAndTintGetter; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; + +public class CableBlock extends PipeMaterialBlock implements IBurnable { + + private static final Map> CACHE = new Object2ObjectOpenHashMap<>(); + + private static final ThreadLocal RELOCATING_TILE = ThreadLocal.withInitial(() -> Boolean.FALSE); + + public CableBlock(BlockBehaviour.Properties properties, CableStructure structure, Material material) { + super(properties, structure, material); + CACHE.compute(material, (k, v) -> { + if (v == null) v = new Object2ObjectOpenHashMap<>(); + v.put(structure, this); + return v; + }); + } + + public int tinted(BlockState blockState, @Nullable BlockAndTintGetter blockAndTintGetter, + @Nullable BlockPos blockPos, int index) { + if (getStructure().isInsulated() && index == 1) { + return CableModel.DEFAULT_INSULATION_COLOR; + } + return index == 0 ? material.getMaterialRGB() : -1; + } + + @Override + public CableStructure getStructure() { + return (CableStructure) super.getStructure(); + } + + @Override + public GTToolType getToolClass() { + return GTToolType.WIRE_CUTTER; + } + + @Override + protected String getConnectLangKey() { + return "gtceu.tool_action.wire_cutter.connect"; + } + + @Override + public void partialBurn(BlockState state, Level world, BlockPos pos) { + CableStructure structure = getStructure(); + if (structure.partialBurnStructure() != null) { + RELOCATING_TILE.set(Boolean.TRUE); + PipeBlockEntity oldPipe = null; + if (world.getBlockEntity(pos) instanceof PipeBlockEntity old) { + oldPipe = old; + } + + BlockState newState = CACHE.get(material).get(structure.partialBurnStructure()).defaultBlockState(); + world.setBlockAndUpdate(pos, newState); + RELOCATING_TILE.set(Boolean.FALSE); + + BlockEntity newBlockEntity = world.getBlockEntity(pos); + if (oldPipe != null && newBlockEntity instanceof PipeBlockEntity pipe) { + pipe.load(oldPipe.saveWithoutMetadata()); + pipe.initialize(); + pipe.syncNow(true); + pipe.markAsDirty(); + } + } + } + + @Override + public @NotNull IPipeNetNodeHandler getHandler(PipeBlockEntity tileContext) { + if (RELOCATING_TILE.get()) { + // prevent node removal when relocating tile + return IPipeNetNodeHandler.EMPTY; + } + return super.getHandler(tileContext); + } + + @Override + public void entityInside(BlockState state, Level level, BlockPos pos, Entity entity) { + super.entityInside(state, level, pos, entity); + if (!(level instanceof ServerLevel serverLevel) || getStructure().isInsulated() || + !(entity instanceof LivingEntity living)) + return; + PipeBlockEntity tile = getBlockEntity(level, pos); + if (tile != null && tile.getFrameMaterial() == null && tile.getOffsetTimer() % 10 == 0) { + WorldPipeNode node = WorldEnergyNet.getWorldNet(serverLevel).getNode(pos); + if (node != null) { + if (node.getData().getLogicEntryNullable(SuperconductorLogic.TYPE) != null) return; + EnergyFlowLogic logic = node.getData().getLogicEntryNullable(EnergyFlowLogic.TYPE); + if (logic != null) { + long tick = GTCEu.getMinecraftServer().getTickCount(); + long cumulativeDamage = 0; + for (EnergyFlowData data : logic.getFlow(tick)) { + cumulativeDamage += (GTUtil.getTierByVoltage(data.voltage()) + 1) * data.amperage() * 4; + } + if (cumulativeDamage != 0) { + living.hurt(GTDamageTypes.ELECTRIC.source(serverLevel), cumulativeDamage); + // TODO advancement + // if (living instanceof ServerPlayer serverPlayer) { + // AdvancementTriggers.ELECTROCUTION_DEATH.trigger(serverPlayer); + // } + } + } + } + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/block/cable/CableStructure.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/block/cable/CableStructure.java new file mode 100644 index 0000000000..4b71f9822b --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/block/cable/CableStructure.java @@ -0,0 +1,99 @@ +package com.gregtechceu.gtceu.common.pipelike.block.cable; + +import com.gregtechceu.gtceu.api.data.tag.TagPrefix; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IInsulatable; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IPipeMaterialStructure; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.PipeStructureRegistrationEvent; +import com.gregtechceu.gtceu.api.gui.GuiTextures; +import com.gregtechceu.gtceu.client.renderer.pipe.PipeModelRedirector; +import com.gregtechceu.gtceu.client.renderer.pipe.PipeModelRegistry; + +import com.lowdragmc.lowdraglib.gui.texture.ResourceTexture; + +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.function.Supplier; + +public record CableStructure(String name, int material, int costFactor, TagPrefix prefix, + @Nullable CableStructure partialBurnStructure, @Nullable Integer partialBurnThreshold, + float renderThickness, Supplier> model) + implements IPipeMaterialStructure, IInsulatable { + + public static final int INSULATION_BURN_TEMP = 1000; + + public static final CableStructure WIRE_SINGLE = new CableStructure("single_wire", 1, 2, TagPrefix.wireGtSingle, + null, null, 0.125f, () -> () -> PipeModelRegistry.getCableModel(0)); + public static final CableStructure WIRE_DOUBLE = new CableStructure("double_wire", 2, 2, TagPrefix.wireGtDouble, + null, null, 0.25f, () -> () -> PipeModelRegistry.getCableModel(0)); + public static final CableStructure WIRE_QUADRUPLE = new CableStructure("quadruple_wire", 4, 3, + TagPrefix.wireGtQuadruple, null, null, 0.375f, () -> () -> PipeModelRegistry.getCableModel(0)); + public static final CableStructure WIRE_OCTAL = new CableStructure("octal_wire", 8, 3, TagPrefix.wireGtOctal, null, + null, 0.5f, () -> () -> PipeModelRegistry.getCableModel(0)); + public static final CableStructure WIRE_HEX = new CableStructure("hex_wire", 16, 3, TagPrefix.wireGtHex, null, null, + 0.75f, () -> () -> PipeModelRegistry.getCableModel(0)); + + public static final CableStructure CABLE_SINGLE = new CableStructure("single_cable", 1, 1, TagPrefix.cableGtSingle, + WIRE_SINGLE, INSULATION_BURN_TEMP, 0.25f, () -> () -> PipeModelRegistry.getCableModel(1)); + public static final CableStructure CABLE_DOUBLE = new CableStructure("double_cable", 2, 1, TagPrefix.cableGtDouble, + WIRE_DOUBLE, INSULATION_BURN_TEMP, 0.375f, () -> () -> PipeModelRegistry.getCableModel(2)); + public static final CableStructure CABLE_QUADRUPLE = new CableStructure("quadruple_cable", 4, 1, + TagPrefix.cableGtQuadruple, WIRE_QUADRUPLE, INSULATION_BURN_TEMP, 0.5f, + () -> () -> PipeModelRegistry.getCableModel(3)); + public static final CableStructure CABLE_OCTAL = new CableStructure("octal_cable", 8, 1, TagPrefix.cableGtOctal, + WIRE_OCTAL, INSULATION_BURN_TEMP, 0.75f, () -> () -> PipeModelRegistry.getCableModel(4)); + public static final CableStructure CABLE_HEX = new CableStructure("hex_cable", 16, 1, TagPrefix.cableGtHex, + WIRE_HEX, INSULATION_BURN_TEMP, 1f, () -> () -> PipeModelRegistry.getCableModel(5)); + + @Override + public @NotNull String getSerializedName() { + return name; + } + + @Override + public TagPrefix getPrefix() { + return prefix; + } + + @Override + public float getRenderThickness() { + return renderThickness; + } + + @Override + public ResourceTexture getPipeTexture(boolean isBlock) { + return isBlock ? GuiTextures.TOOL_WIRE_CONNECT : GuiTextures.TOOL_WIRE_BLOCK; + } + + @Override + public boolean isPaintable() { + return true; + } + + @Override + public boolean isInsulated() { + return partialBurnStructure != null; + } + + @Override + @OnlyIn(Dist.CLIENT) + public PipeModelRedirector getModel() { + return model.get().get(); + } + + public static void register(@NotNull PipeStructureRegistrationEvent event) { + event.register(WIRE_SINGLE); + event.register(WIRE_DOUBLE); + event.register(WIRE_QUADRUPLE); + event.register(WIRE_OCTAL); + event.register(WIRE_HEX); + event.register(CABLE_SINGLE); + event.register(CABLE_DOUBLE); + event.register(CABLE_QUADRUPLE); + event.register(CABLE_OCTAL); + event.register(CABLE_HEX); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/block/duct/DuctPipeBlock.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/block/duct/DuctPipeBlock.java new file mode 100644 index 0000000000..4210d5af0f --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/block/duct/DuctPipeBlock.java @@ -0,0 +1,28 @@ +package com.gregtechceu.gtceu.common.pipelike.block.duct; + +import com.gregtechceu.gtceu.api.graphnet.pipenet.IPipeNetNodeHandler; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.block.PipeBlock; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; +import com.gregtechceu.gtceu.common.pipelike.handlers.DuctNetHandler; + +import net.minecraft.world.item.ItemStack; + +import org.jetbrains.annotations.NotNull; + +public class DuctPipeBlock extends PipeBlock { + + public DuctPipeBlock(Properties properties, DuctStructure structure) { + super(properties, structure); + } + + @Override + @NotNull + public IPipeNetNodeHandler getHandler(PipeBlockEntity blockEntityContext) { + return DuctNetHandler.INSTANCE; + } + + @Override + protected @NotNull IPipeNetNodeHandler getHandler(@NotNull ItemStack stack) { + return DuctNetHandler.INSTANCE; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/block/duct/DuctStructure.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/block/duct/DuctStructure.java new file mode 100644 index 0000000000..76e4f741c8 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/block/duct/DuctStructure.java @@ -0,0 +1,55 @@ +package com.gregtechceu.gtceu.common.pipelike.block.duct; + +import com.gregtechceu.gtceu.api.graphnet.logic.NetLogicData; +import com.gregtechceu.gtceu.api.graphnet.logic.WeightFactorLogic; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IPipeStructure; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.PipeStructureRegistrationEvent; +import com.gregtechceu.gtceu.client.renderer.pipe.PipeModelRedirector; +import com.gregtechceu.gtceu.client.renderer.pipe.PipeModelRegistry; +import com.gregtechceu.gtceu.common.pipelike.net.duct.DuctThroughputLogic; + +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import org.jetbrains.annotations.NotNull; + +public record DuctStructure(String name, float renderThickness, float rateMultiplier) implements IPipeStructure { + + public static final DuctStructure SMALL = new DuctStructure("small", 0.375f, 2f); + public static final DuctStructure NORMAL = new DuctStructure("normal", 0.5f, 4f); + public static final DuctStructure LARGE = new DuctStructure("large", 0.75f, 8f); + public static final DuctStructure HUGE = new DuctStructure("huge", 0.75f, 16f); + + @Override + public @NotNull String getSerializedName() { + return name; + } + + @Override + public float getRenderThickness() { + return renderThickness; + } + + @Override + public boolean isPaintable() { + return true; + } + + @Override + @OnlyIn(Dist.CLIENT) + public PipeModelRedirector getModel() { + return PipeModelRegistry.getDuctModel(); + } + + public void mutateData(NetLogicData data) { + data.setLogicEntry(DuctThroughputLogic.TYPE.getWith(this.rateMultiplier)) + .setLogicEntry(WeightFactorLogic.TYPE.getWith(2048f / this.rateMultiplier)); + } + + public static void register(@NotNull PipeStructureRegistrationEvent event) { + event.register(SMALL); + event.register(NORMAL); + event.register(LARGE); + event.register(HUGE); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/block/laser/LaserPipeBlock.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/block/laser/LaserPipeBlock.java new file mode 100644 index 0000000000..167189efd5 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/block/laser/LaserPipeBlock.java @@ -0,0 +1,45 @@ +package com.gregtechceu.gtceu.common.pipelike.block.laser; + +import com.gregtechceu.gtceu.api.graphnet.pipenet.IPipeNetNodeHandler; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.block.ActivablePipeBlock; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; +import com.gregtechceu.gtceu.api.item.tool.GTToolType; +import com.gregtechceu.gtceu.common.pipelike.handlers.LaserNetHandler; + +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.state.BlockBehaviour; + +import org.jetbrains.annotations.NotNull; + +public class LaserPipeBlock extends ActivablePipeBlock { + + public LaserPipeBlock(BlockBehaviour.Properties properties, LaserStructure structure) { + super(properties, structure); + } + + @Override + public GTToolType getToolClass() { + return GTToolType.WIRE_CUTTER; + } + + @Override + protected String getConnectLangKey() { + return "gtceu.tool_action.wire_cutter.connect"; + } + + @Override + public boolean allowsBlocking() { + return false; + } + + @Override + @NotNull + public IPipeNetNodeHandler getHandler(PipeBlockEntity tileContext) { + return LaserNetHandler.INSTANCE; + } + + @Override + protected @NotNull IPipeNetNodeHandler getHandler(@NotNull ItemStack stack) { + return LaserNetHandler.INSTANCE; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/block/laser/LaserStructure.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/block/laser/LaserStructure.java new file mode 100644 index 0000000000..1d95756fc5 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/block/laser/LaserStructure.java @@ -0,0 +1,82 @@ +package com.gregtechceu.gtceu.common.pipelike.block.laser; + +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IPipeStructure; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.PipeStructureRegistrationEvent; +import com.gregtechceu.gtceu.api.gui.GuiTextures; +import com.gregtechceu.gtceu.client.renderer.pipe.PipeModelRedirector; +import com.gregtechceu.gtceu.client.renderer.pipe.PipeModelRegistry; +import com.gregtechceu.gtceu.utils.GTUtil; + +import com.lowdragmc.lowdraglib.gui.texture.ResourceTexture; + +import net.minecraft.core.Direction; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import org.jetbrains.annotations.NotNull; + +import java.util.function.Supplier; + +public record LaserStructure(String name, float renderThickness, boolean mirror, + Supplier> model) + implements IPipeStructure { + + public static final LaserStructure NORMAL = new LaserStructure("laser_pipe_normal", 0.375f, + false, () -> () -> PipeModelRegistry.getLaserModel()); + public static final LaserStructure MIRROR = new LaserStructure("laser_pipe_mirror", 0.5f, + true, () -> () -> PipeModelRegistry.getLaserModel()); + + @Override + public ResourceTexture getPipeTexture(boolean isBlock) { + return isBlock ? GuiTextures.TOOL_WIRE_CONNECT : GuiTextures.TOOL_WIRE_BLOCK; + } + + @Override + public boolean canConnectTo(Direction side, byte connectionMask) { + if (mirror) { + byte connectionCount = 0; + for (Direction facing : GTUtil.DIRECTIONS) { + if (facing == side) continue; + if (GTUtil.evalMask(facing, connectionMask)) { + if (facing.getOpposite() == side) return false; // must be a bent connection + connectionCount++; + } + if (connectionCount > 1) return false; + } + } else { + for (Direction facing : GTUtil.DIRECTIONS) { + if (facing == side) continue; + if (GTUtil.evalMask(facing, connectionMask)) { + return facing.getOpposite() == side; + } + } + } + return true; + } + + @Override + public @NotNull String getSerializedName() { + return name; + } + + @Override + public float getRenderThickness() { + return renderThickness; + } + + @Override + public boolean isPaintable() { + return true; + } + + @Override + @OnlyIn(Dist.CLIENT) + public PipeModelRedirector getModel() { + return model.get().get(); + } + + public static void register(@NotNull PipeStructureRegistrationEvent event) { + event.register(NORMAL); + event.register(MIRROR); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/block/optical/OpticalPipeBlock.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/block/optical/OpticalPipeBlock.java new file mode 100644 index 0000000000..1907c80c18 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/block/optical/OpticalPipeBlock.java @@ -0,0 +1,44 @@ +package com.gregtechceu.gtceu.common.pipelike.block.optical; + +import com.gregtechceu.gtceu.api.graphnet.pipenet.IPipeNetNodeHandler; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.block.ActivablePipeBlock; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; +import com.gregtechceu.gtceu.api.item.tool.GTToolType; +import com.gregtechceu.gtceu.common.pipelike.handlers.DuctNetHandler; + +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.state.BlockBehaviour; + +import org.jetbrains.annotations.NotNull; + +public class OpticalPipeBlock extends ActivablePipeBlock { + + public OpticalPipeBlock(BlockBehaviour.Properties properties, OpticalStructure structure) { + super(properties, structure); + } + + @Override + public GTToolType getToolClass() { + return GTToolType.WIRE_CUTTER; + } + + @Override + protected String getConnectLangKey() { + return "gtceu.tool_action.wire_cutter.connect"; + } + + @Override + public boolean allowsBlocking() { + return false; + } + + @Override + public IPipeNetNodeHandler getHandler(PipeBlockEntity tileContext) { + return DuctNetHandler.INSTANCE; + } + + @Override + protected @NotNull IPipeNetNodeHandler getHandler(@NotNull ItemStack stack) { + return DuctNetHandler.INSTANCE; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/block/optical/OpticalStructure.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/block/optical/OpticalStructure.java new file mode 100644 index 0000000000..1f920b0c97 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/block/optical/OpticalStructure.java @@ -0,0 +1,68 @@ +package com.gregtechceu.gtceu.common.pipelike.block.optical; + +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IPipeStructure; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.PipeStructureRegistrationEvent; +import com.gregtechceu.gtceu.api.gui.GuiTextures; +import com.gregtechceu.gtceu.client.renderer.pipe.PipeModelRedirector; +import com.gregtechceu.gtceu.client.renderer.pipe.PipeModelRegistry; +import com.gregtechceu.gtceu.utils.GTUtil; + +import com.lowdragmc.lowdraglib.gui.texture.ResourceTexture; + +import net.minecraft.core.Direction; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import org.jetbrains.annotations.NotNull; + +import java.util.function.Supplier; + +public record OpticalStructure(String name, float renderThickness, Supplier> model) + implements IPipeStructure { + + public static final OpticalStructure INSTANCE = new OpticalStructure("optical_pipe_normal", 0.375f, + () -> () -> PipeModelRegistry.getOpticalModel()); + + @Override + public ResourceTexture getPipeTexture(boolean isBlock) { + return isBlock ? GuiTextures.TOOL_WIRE_CONNECT : GuiTextures.TOOL_WIRE_BLOCK; + } + + @Override + public boolean canConnectTo(Direction side, byte connectionMask) { + byte connectionCount = 0; + for (Direction facing : GTUtil.DIRECTIONS) { + if (facing == side) continue; + if (GTUtil.evalMask(facing, connectionMask)) { + connectionCount++; + } + if (connectionCount > 1) return false; + } + return true; + } + + @Override + public @NotNull String getSerializedName() { + return name; + } + + @Override + public float getRenderThickness() { + return renderThickness; + } + + @Override + public boolean isPaintable() { + return true; + } + + @Override + @OnlyIn(Dist.CLIENT) + public PipeModelRedirector getModel() { + return model.get().get(); + } + + public static void register(@NotNull PipeStructureRegistrationEvent event) { + event.register(INSTANCE); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/block/pipe/MaterialPipeBlock.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/block/pipe/MaterialPipeBlock.java new file mode 100644 index 0000000000..7ed02d226b --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/block/pipe/MaterialPipeBlock.java @@ -0,0 +1,20 @@ +package com.gregtechceu.gtceu.common.pipelike.block.pipe; + +import com.gregtechceu.gtceu.api.data.chemical.material.Material; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IBurnable; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IFreezable; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.block.PipeMaterialBlock; + +import net.minecraft.world.level.block.state.BlockBehaviour; + +public class MaterialPipeBlock extends PipeMaterialBlock implements IBurnable, IFreezable { + + public MaterialPipeBlock(BlockBehaviour.Properties properties, MaterialPipeStructure structure, Material material) { + super(properties, structure, material); + } + + @Override + public MaterialPipeStructure getStructure() { + return (MaterialPipeStructure) super.getStructure(); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/block/pipe/MaterialPipeStructure.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/block/pipe/MaterialPipeStructure.java new file mode 100644 index 0000000000..7079d3b8f9 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/block/pipe/MaterialPipeStructure.java @@ -0,0 +1,104 @@ +package com.gregtechceu.gtceu.common.pipelike.block.pipe; + +import com.gregtechceu.gtceu.api.data.tag.TagPrefix; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IPipeChanneledStructure; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IPipeMaterialStructure; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.PipeStructureRegistrationEvent; +import com.gregtechceu.gtceu.client.renderer.pipe.PipeModelRedirector; +import com.gregtechceu.gtceu.client.renderer.pipe.PipeModelRegistry; + +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import org.jetbrains.annotations.NotNull; + +import java.util.function.Supplier; + +public record MaterialPipeStructure(String name, int material, int channelCount, boolean restrictive, TagPrefix prefix, + float renderThickness, Supplier> model) + implements IPipeMaterialStructure, IPipeChanneledStructure { + + public static final MaterialPipeStructure TINY = new MaterialPipeStructure("tiny", 1, 1, false, + TagPrefix.pipeTiny, 0.25f, () -> () -> PipeModelRegistry.getPipeModel(0)); + public static final MaterialPipeStructure SMALL = new MaterialPipeStructure("small", 2, 1, false, + TagPrefix.pipeSmall, 0.375f, () -> () -> PipeModelRegistry.getPipeModel(1)); + public static final MaterialPipeStructure NORMAL = new MaterialPipeStructure("normal", 6, 1, false, + TagPrefix.pipeNormal, 0.5f, () -> () -> PipeModelRegistry.getPipeModel(2)); + public static final MaterialPipeStructure LARGE = new MaterialPipeStructure("large", 12, 1, false, + TagPrefix.pipeLarge, 0.75f, () -> () -> PipeModelRegistry.getPipeModel(3)); + public static final MaterialPipeStructure HUGE = new MaterialPipeStructure("huge", 24, 1, false, + TagPrefix.pipeHuge, 0.875f, () -> () -> PipeModelRegistry.getPipeModel(4)); + + public static final MaterialPipeStructure QUADRUPLE = new MaterialPipeStructure("quadruple", 8, 4, false, + TagPrefix.pipeQuadruple, 0.95f, () -> () -> PipeModelRegistry.getPipeModel(5)); + public static final MaterialPipeStructure NONUPLE = new MaterialPipeStructure("nonuple", 18, 9, false, + TagPrefix.pipeNonuple, 0.95f, () -> () -> PipeModelRegistry.getPipeModel(6)); + + public static final MaterialPipeStructure TINY_RESTRICTIVE = new MaterialPipeStructure("tiny_restrictive", 1, + 1, true, TagPrefix.pipeTinyRestrictive, 0.25f, () -> () -> PipeModelRegistry.getPipeRestrictiveModel(0)); + public static final MaterialPipeStructure SMALL_RESTRICTIVE = new MaterialPipeStructure("small_restrictive", 2, + 1, true, TagPrefix.pipeSmallRestrictive, 0.375f, () -> () -> PipeModelRegistry.getPipeRestrictiveModel(1)); + public static final MaterialPipeStructure NORMAL_RESTRICTIVE = new MaterialPipeStructure("normal_restrictive", + 6, 1, true, TagPrefix.pipeNormalRestrictive, 0.5f, + () -> () -> PipeModelRegistry.getPipeRestrictiveModel(2)); + public static final MaterialPipeStructure LARGE_RESTRICTIVE = new MaterialPipeStructure("large_restrictive", + 12, 1, true, TagPrefix.pipeLargeRestrictive, 0.75f, + () -> () -> PipeModelRegistry.getPipeRestrictiveModel(3)); + public static final MaterialPipeStructure HUGE_RESTRICTIVE = new MaterialPipeStructure("huge_restrictive", 24, + 1, true, TagPrefix.pipeHugeRestrictive, 0.875f, () -> () -> PipeModelRegistry.getPipeRestrictiveModel(4)); + + public static final MaterialPipeStructure QUADRUPLE_RESTRICTIVE = new MaterialPipeStructure( + "quadruple_restrictive", 8, 4, true, TagPrefix.pipeQuadrupleRestrictive, 0.95f, + () -> () -> PipeModelRegistry.getPipeRestrictiveModel(5)); + public static final MaterialPipeStructure NONUPLE_RESTRICTIVE = new MaterialPipeStructure( + "nonuple_restrictive", 18, 9, true, TagPrefix.pipeNonupleRestrictive, 0.95f, + () -> () -> PipeModelRegistry.getPipeRestrictiveModel(6)); + + @Override + public @NotNull String getSerializedName() { + return name; + } + + @Override + public TagPrefix getPrefix() { + return prefix; + } + + @Override + public float getRenderThickness() { + return renderThickness; + } + + @Override + public int getChannelCount() { + return channelCount; + } + + @Override + @OnlyIn(Dist.CLIENT) + public PipeModelRedirector getModel() { + return model.get().get(); + } + + @Override + public boolean isPaintable() { + return true; + } + + public static void register(@NotNull PipeStructureRegistrationEvent event) { + event.register(TINY); + event.register(SMALL); + event.register(NORMAL); + event.register(LARGE); + event.register(HUGE); + event.register(QUADRUPLE); + event.register(NONUPLE); + event.register(TINY_RESTRICTIVE); + event.register(SMALL_RESTRICTIVE); + event.register(NORMAL_RESTRICTIVE); + event.register(LARGE_RESTRICTIVE); + event.register(HUGE_RESTRICTIVE); + event.register(QUADRUPLE_RESTRICTIVE); + event.register(NONUPLE_RESTRICTIVE); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/cable/CableData.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/cable/CableData.java deleted file mode 100644 index e45ef0c9b7..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/cable/CableData.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.cable; - -import com.gregtechceu.gtceu.api.data.chemical.material.properties.WireProperties; -import com.gregtechceu.gtceu.api.pipenet.IAttachData; - -import net.minecraft.core.Direction; - -import lombok.Getter; -import lombok.experimental.Accessors; - -import java.util.Objects; - -/** - * @author KilaBash - * @date 2023/3/1 - * @implNote CableData - */ -@Accessors(fluent = true) -public class CableData implements IAttachData { - - @Getter - WireProperties properties; - @Getter - byte connections; - - public CableData(WireProperties properties, byte connections) { - this.properties = properties; - this.connections = connections; - } - - @Override - public boolean canAttachTo(Direction side) { - return (connections & (1 << side.ordinal())) != 0; - } - - @Override - public boolean setAttached(Direction side, boolean attach) { - var result = canAttachTo(side); - if (result != attach) { - if (attach) { - connections |= (1 << side.ordinal()); - } else { - connections &= ~(1 << side.ordinal()); - } - } - return result != attach; - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof CableData cableData) { - return cableData.properties.equals(properties) && connections == cableData.connections; - } - return super.equals(obj); - } - - @Override - public int hashCode() { - return Objects.hash(properties, connections); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/cable/EnergyNet.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/cable/EnergyNet.java deleted file mode 100644 index 2b9931a5d0..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/cable/EnergyNet.java +++ /dev/null @@ -1,94 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.cable; - -import com.gregtechceu.gtceu.api.data.chemical.material.properties.WireProperties; -import com.gregtechceu.gtceu.api.pipenet.LevelPipeNet; -import com.gregtechceu.gtceu.api.pipenet.Node; -import com.gregtechceu.gtceu.api.pipenet.PipeNet; - -import net.minecraft.core.BlockPos; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.world.level.Level; - -import java.util.*; - -public class EnergyNet extends PipeNet { - - private final Map> NET_DATA = new HashMap<>(); - - private long lastEnergyFluxPerSec; - private long energyFluxPerSec; - private long lastTime; - - protected EnergyNet(LevelPipeNet world) { - super(world); - } - - public List getNetData(BlockPos pipePos) { - List data = NET_DATA.get(pipePos); - if (data == null) { - data = EnergyNetWalker.createNetData(this, pipePos); - if (data == null) { - // walker failed, don't cache so it tries again on next insertion - return Collections.emptyList(); - } - data.sort(Comparator.comparingInt(EnergyRoutePath::getDistance)); - NET_DATA.put(pipePos, data); - } - return data; - } - - @Override - public void onNeighbourUpdate(BlockPos fromPos) { - NET_DATA.clear(); - } - - @Override - public void onPipeConnectionsUpdate() { - NET_DATA.clear(); - } - - @Override - protected void transferNodeData(Map> transferredNodes, - PipeNet parentNet) { - super.transferNodeData(transferredNodes, parentNet); - NET_DATA.clear(); - ((EnergyNet) parentNet).NET_DATA.clear(); - } - - @Override - protected void writeNodeData(WireProperties nodeData, CompoundTag tagCompound) { - tagCompound.putLong("voltage", nodeData.getVoltage()); - tagCompound.putInt("amperage", nodeData.getAmperage()); - tagCompound.putInt("loss", nodeData.getLossPerBlock()); - } - - @Override - protected WireProperties readNodeData(CompoundTag tagCompound) { - long voltage = tagCompound.getLong("voltage"); - int amperage = tagCompound.getInt("amperage"); - int lossPerBlock = tagCompound.getInt("loss"); - return new WireProperties(voltage, amperage, lossPerBlock); - } - - ////////////////////////////////////// - // ******* Pipe Status *******// - ////////////////////////////////////// - - public long getEnergyFluxPerSec() { - Level world = getLevel(); - if (world != null && !world.isClientSide && (world.getGameTime() - lastTime) >= 20) { - lastTime = world.getGameTime(); - clearCache(); - } - return lastEnergyFluxPerSec; - } - - public void addEnergyFluxPerSec(long energy) { - energyFluxPerSec += energy; - } - - public void clearCache() { - lastEnergyFluxPerSec = energyFluxPerSec; - energyFluxPerSec = 0; - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/cable/EnergyNetHandler.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/cable/EnergyNetHandler.java deleted file mode 100644 index 5501d80610..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/cable/EnergyNetHandler.java +++ /dev/null @@ -1,164 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.cable; - -import com.gregtechceu.gtceu.GTCEu; -import com.gregtechceu.gtceu.api.capability.IEnergyContainer; -import com.gregtechceu.gtceu.common.blockentity.CableBlockEntity; -import com.gregtechceu.gtceu.utils.GTUtil; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.core.particles.ParticleTypes; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.block.Blocks; - -import java.util.Objects; - -public class EnergyNetHandler implements IEnergyContainer { - - private EnergyNet net; - private boolean transfer; - private final CableBlockEntity cable; - private final Direction facing; - - public EnergyNetHandler(EnergyNet net, CableBlockEntity cable, Direction facing) { - this.net = Objects.requireNonNull(net); - this.cable = Objects.requireNonNull(cable); - this.facing = facing; - } - - public EnergyNet getNet() { - return net; - } - - public void updateNetwork(EnergyNet net) { - this.net = net; - } - - @Override - public long getEnergyCanBeInserted() { - return getEnergyCapacity(); - } - - @Override - public long acceptEnergyFromNetwork(Direction side, long voltage, long amperage) { - if (transfer) return 0; - if (side == null) { - if (facing == null) return 0; - side = facing; - } - - long amperesUsed = 0L; - for (EnergyRoutePath path : net.getNetData(cable.getPipePos())) { - if (path.getMaxLoss() >= voltage) { - // Will lose all the energy with this path, so don't use it - continue; - } - - if (cable.getPipePos().equals(path.getTargetPipePos()) && side == path.getTargetFacing()) { - // Do not insert into source handler - continue; - } - - IEnergyContainer dest = path.getHandler(getNet().getLevel()); - if (dest == null) continue; - - Direction facing = path.getTargetFacing().getOpposite(); - if (!dest.inputsEnergy(facing) || dest.getEnergyCanBeInserted() <= 0) continue; - - long pathVoltage = voltage - path.getMaxLoss(); - boolean cableBroken = false; - for (CableBlockEntity cable : path.getPath()) { - if (cable.getMaxVoltage() < voltage) { - int heat = (int) (Math.log( - GTUtil.getTierByVoltage(voltage) - GTUtil.getTierByVoltage(cable.getMaxVoltage())) * - 45 + 36.5); - cable.applyHeat(heat); - - cableBroken = cable.isInValid(); - if (cableBroken) { - // a cable burned away (or insulation melted) - break; - } - - // limit transfer to cables max and void rest - pathVoltage = Math.min(cable.getMaxVoltage(), pathVoltage); - } - } - - if (cableBroken) continue; - - transfer = true; - long amps = dest.acceptEnergyFromNetwork(facing, pathVoltage, amperage - amperesUsed); - transfer = false; - if (amps == 0) continue; - - amperesUsed += amps; - long voltageTraveled = voltage; - for (CableBlockEntity cable : path.getPath()) { - voltageTraveled -= cable.getNodeData().getLossPerBlock(); - if (voltageTraveled <= 0) break; - - if (!cable.isInValid()) { - cable.incrementAmperage(amps, voltageTraveled); - } - } - - if (amperage == amperesUsed) break; - } - - net.addEnergyFluxPerSec(amperesUsed * voltage); - return amperesUsed; - } - - private void burnCable(ServerLevel serverLevel, BlockPos pos) { - serverLevel.setBlockAndUpdate(pos, Blocks.FIRE.defaultBlockState()); - if (!getNet().getLevel().isClientSide) { - getNet().getLevel().sendParticles(ParticleTypes.LARGE_SMOKE, - pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, - 5 + getNet().getLevel().random.nextInt(3), 0.0, 0.0, 0.0, 0.1); - } - } - - @Override - public long getInputAmperage() { - return cable.getNodeData().getAmperage(); - } - - @Override - public long getInputVoltage() { - return cable.getNodeData().getVoltage(); - } - - @Override - public long getEnergyCapacity() { - return getInputVoltage() * getInputAmperage(); - } - - @Override - public long changeEnergy(long energyToAdd) { - GTCEu.LOGGER.warn("Do not use changeEnergy() for cables! Use acceptEnergyFromNetwork()"); - return acceptEnergyFromNetwork(null, - energyToAdd / getInputAmperage(), - energyToAdd / getInputVoltage()) * getInputVoltage(); - } - - @Override - public boolean outputsEnergy(Direction side) { - return true; - } - - @Override - public boolean inputsEnergy(Direction side) { - return true; - } - - @Override - public long getEnergyStored() { - return 0; - } - - @Override - public boolean isOneProbeHidden() { - return true; - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/cable/EnergyNetWalker.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/cable/EnergyNetWalker.java deleted file mode 100644 index c4404a0f6d..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/cable/EnergyNetWalker.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.cable; - -import com.gregtechceu.gtceu.GTCEu; -import com.gregtechceu.gtceu.api.capability.IEnergyContainer; -import com.gregtechceu.gtceu.api.capability.forge.GTCapability; -import com.gregtechceu.gtceu.api.data.chemical.material.properties.WireProperties; -import com.gregtechceu.gtceu.api.pipenet.PipeNetWalker; -import com.gregtechceu.gtceu.common.blockentity.CableBlockEntity; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.world.level.block.entity.BlockEntity; - -import org.apache.commons.lang3.ArrayUtils; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.List; - -public class EnergyNetWalker extends PipeNetWalker { - - @Nullable - public static List createNetData(EnergyNet pipeNet, BlockPos sourcePipe) { - try { - EnergyNetWalker walker = new EnergyNetWalker(pipeNet, sourcePipe, 1, new ArrayList<>()); - walker.traversePipeNet(); - return walker.routes; - } catch (Exception e) { - GTCEu.LOGGER.error("error while create net data for energynet", e); - } - return null; - } - - private final List routes; - private CableBlockEntity[] pipes = {}; - private int loss; - - public EnergyNetWalker(EnergyNet pipeNet, BlockPos sourcePipe, int walkedBlocks, List routes) { - super(pipeNet, sourcePipe, walkedBlocks); - this.routes = routes; - } - - @NotNull - @Override - protected PipeNetWalker createSubWalker(EnergyNet pipeNet, - Direction facingToNextPos, - BlockPos nextPos, - int walkedBlocks) { - EnergyNetWalker walker = new EnergyNetWalker(pipeNet, nextPos, walkedBlocks, routes); - walker.loss = loss; - walker.pipes = pipes; - return walker; - } - - @Override - protected void checkPipe(CableBlockEntity pipeTile, BlockPos pos) { - pipes = ArrayUtils.add(pipes, pipeTile); - loss += pipeTile.getNodeData().getLossPerBlock(); - } - - @Override - protected void checkNeighbour(CableBlockEntity pipeTile, BlockPos pipePos, Direction faceToNeighbour, - @Nullable BlockEntity neighbourTile) { - // assert that the last added pipe is the current pipe - if (pipeTile != pipes[pipes.length - 1]) - throw new IllegalStateException( - "The current pipe is not the last added pipe. Something went seriously wrong!"); - if (neighbourTile != null) { - IEnergyContainer container = neighbourTile - .getCapability(GTCapability.CAPABILITY_ENERGY_CONTAINER, faceToNeighbour.getOpposite()).resolve() - .orElse(null); - if (container != null) { - routes.add(new EnergyRoutePath(pipePos.immutable(), faceToNeighbour, pipes, getWalkedBlocks(), loss)); - } - } - } - - @Override - protected Class getBasePipeClass() { - return CableBlockEntity.class; - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/cable/EnergyRoutePath.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/cable/EnergyRoutePath.java deleted file mode 100644 index c74b197643..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/cable/EnergyRoutePath.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.cable; - -import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; -import com.gregtechceu.gtceu.api.capability.IEnergyContainer; -import com.gregtechceu.gtceu.api.pipenet.IRoutePath; -import com.gregtechceu.gtceu.common.blockentity.CableBlockEntity; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.world.level.Level; - -import lombok.Getter; -import org.jetbrains.annotations.Nullable; - -public class EnergyRoutePath implements IRoutePath { - - private final CableBlockEntity targetPipe; - @Getter - private final BlockPos targetPipePos; - @Getter - private final Direction targetFacing; - @Getter - private final int distance; - @Getter - private final CableBlockEntity[] path; - @Getter - private final long maxLoss; - - public EnergyRoutePath(BlockPos targetPipePos, Direction targetFacing, CableBlockEntity[] path, int distance, - long maxLoss) { - this.targetPipe = path[path.length - 1]; - this.targetPipePos = targetPipePos; - this.targetFacing = targetFacing; - this.path = path; - this.distance = distance; - this.maxLoss = maxLoss; - } - - @Nullable - public IEnergyContainer getHandler(Level world) { - return GTCapabilityHelper.getEnergyContainer(world, getTargetPipePos().relative(targetFacing), - targetFacing.getOpposite()); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/cable/Insulation.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/cable/Insulation.java deleted file mode 100644 index 56ecba27f8..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/cable/Insulation.java +++ /dev/null @@ -1,103 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.cable; - -import com.gregtechceu.gtceu.GTCEu; -import com.gregtechceu.gtceu.api.data.chemical.material.Material; -import com.gregtechceu.gtceu.api.data.chemical.material.info.MaterialIconType; -import com.gregtechceu.gtceu.api.data.chemical.material.properties.WireProperties; -import com.gregtechceu.gtceu.api.data.tag.TagPrefix; -import com.gregtechceu.gtceu.api.pipenet.IMaterialPipeType; -import com.gregtechceu.gtceu.client.model.PipeModel; - -import net.minecraft.resources.ResourceLocation; - -import lombok.Getter; -import org.jetbrains.annotations.Nullable; - -import java.util.function.Supplier; - -import static com.gregtechceu.gtceu.api.data.tag.TagPrefix.*; - -public enum Insulation implements IMaterialPipeType { - - WIRE_SINGLE("single_wire", 0.125f, 1, 2, wireGtSingle, -1, false), - WIRE_DOUBLE("double_wire", 0.25f, 2, 2, wireGtDouble, -1, false), - WIRE_QUADRUPLE("quadruple_wire", 0.375f, 4, 3, wireGtQuadruple, -1, false), - WIRE_OCTAL("octal_wire", 0.5f, 8, 3, wireGtOctal, -1, false), - WIRE_HEX("hex_wire", 0.75f, 16, 3, wireGtHex, -1, false), - - CABLE_SINGLE("single_cable", 0.25f, 1, 1, cableGtSingle, 0, true), - CABLE_DOUBLE("double_cable", 0.375f, 2, 1, cableGtDouble, 1, true), - CABLE_QUADRUPLE("quadruple_cable", 0.5f, 4, 1, cableGtQuadruple, 2, true), - CABLE_OCTAL("octal_cable", 0.75f, 8, 1, cableGtOctal, 3, true), - CABLE_HEX("hex_cable", 1.0f, 16, 1, cableGtHex, 4, true); - - public static final ResourceLocation TYPE_ID = GTCEu.id("insulation"); - - public final String name; - public final float thickness; - public final int amperage; - public final int lossMultiplier; - @Getter - public final TagPrefix tagPrefix; - public final int insulationLevel; - public final boolean isCable; - - Insulation(String name, float thickness, int amperage, int lossMultiplier, TagPrefix TagPrefix, int insulated, - boolean isCable) { - this.name = name; - this.thickness = thickness; - this.amperage = amperage; - this.tagPrefix = TagPrefix; - this.insulationLevel = insulated; - this.lossMultiplier = lossMultiplier; - this.isCable = isCable; - } - - @Override - public float getThickness() { - return thickness; - } - - @Override - public WireProperties modifyProperties(WireProperties baseProperties) { - int lossPerBlock; - if (!baseProperties.isSuperconductor() && baseProperties.getLossPerBlock() == 0) - lossPerBlock = (int) (0.75 * lossMultiplier); - else lossPerBlock = baseProperties.getLossPerBlock() * lossMultiplier; - - return new WireProperties(baseProperties.getVoltage(), baseProperties.getAmperage() * amperage, lossPerBlock, - baseProperties.isSuperconductor()); - } - - public boolean isCable() { - return ordinal() > 4; - } - - @Override - public boolean isPaintable() { - return true; - } - - @Override - public ResourceLocation type() { - return TYPE_ID; - } - - public PipeModel createPipeModel(Material material) { - Supplier wireSideTexturePath = () -> MaterialIconType.wire - .getBlockTexturePath(material.getMaterialIconSet(), true).withSuffix("_side"); - Supplier wireEndTexturePath = () -> MaterialIconType.wire - .getBlockTexturePath(material.getMaterialIconSet(), true).withSuffix("_end"); - Supplier<@Nullable ResourceLocation> wireSideOverlayTexturePath = () -> MaterialIconType.wire - .getBlockTexturePath(material.getMaterialIconSet(), "side_overlay", true); - Supplier<@Nullable ResourceLocation> wireEndOverlayTexturePath = () -> MaterialIconType.wire - .getBlockTexturePath(material.getMaterialIconSet(), "end_overlay", true); - PipeModel model = new PipeModel(thickness, - isCable ? () -> GTCEu.id("block/cable/insulation_5") : wireSideTexturePath, wireEndTexturePath, - wireSideOverlayTexturePath, wireEndOverlayTexturePath); - if (isCable) { - model.setEndOverlayTexture(GTCEu.id("block/cable/insulation_%s".formatted(insulationLevel))); - } - return model; - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/cable/LevelEnergyNet.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/cable/LevelEnergyNet.java deleted file mode 100644 index 799e65281b..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/cable/LevelEnergyNet.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.cable; - -import com.gregtechceu.gtceu.api.data.chemical.material.properties.WireProperties; -import com.gregtechceu.gtceu.api.pipenet.LevelPipeNet; - -import net.minecraft.nbt.CompoundTag; -import net.minecraft.server.level.ServerLevel; - -public class LevelEnergyNet extends LevelPipeNet { - - public static LevelEnergyNet getOrCreate(ServerLevel serverLevel) { - return serverLevel.getDataStorage().computeIfAbsent(tag -> new LevelEnergyNet(serverLevel, tag), - () -> new LevelEnergyNet(serverLevel), "gtcue_energy_net"); - } - - public LevelEnergyNet(ServerLevel serverLevel) { - super(serverLevel); - } - - public LevelEnergyNet(ServerLevel serverLevel, CompoundTag tag) { - super(serverLevel, tag); - } - - @Override - protected EnergyNet createNetInstance() { - return new EnergyNet(this); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/cable/PerTickLongCounter.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/cable/PerTickLongCounter.java deleted file mode 100644 index 72f0d8bdb5..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/cable/PerTickLongCounter.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.cable; - -import net.minecraft.world.level.Level; - -public class PerTickLongCounter { - - private final long defaultValue; - private long lastUpdatedWorldTime; - private long lastValue; - private long currentValue; - - public PerTickLongCounter() { - this(0); - } - - public PerTickLongCounter(long defaultValue) { - this.defaultValue = defaultValue; - this.currentValue = defaultValue; - this.lastValue = defaultValue; - } - - private void checkValueState(Level world) { - if (world == null) return; - long currentWorldTime = world.getGameTime(); - if (currentWorldTime != lastUpdatedWorldTime) { - if (currentWorldTime == lastUpdatedWorldTime + 1) { - // last updated time is 1 tick ago, so we can move current value to last - // before resetting it to default value - this.lastValue = currentValue; - } else { - // otherwise, set last value as default value - this.lastValue = defaultValue; - } - this.lastUpdatedWorldTime = currentWorldTime; - this.currentValue = defaultValue; - } - } - - public long get(Level world) { - checkValueState(world); - return currentValue; - } - - public long getLast(Level world) { - checkValueState(world); - return lastValue; - } - - public void increment(Level world, long value) { - checkValueState(world); - this.currentValue += value; - } - - public void set(Level world, long value) { - checkValueState(world); - this.currentValue = value; - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/duct/DuctNetHandler.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/duct/DuctNetHandler.java deleted file mode 100644 index 611446b545..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/duct/DuctNetHandler.java +++ /dev/null @@ -1,181 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.duct; - -import com.gregtechceu.gtceu.api.GTValues; -import com.gregtechceu.gtceu.api.capability.IHazardParticleContainer; -import com.gregtechceu.gtceu.api.data.chemical.material.properties.HazardProperty; -import com.gregtechceu.gtceu.api.data.medicalcondition.MedicalCondition; -import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; -import com.gregtechceu.gtceu.api.machine.feature.IEnvironmentalHazardCleaner; -import com.gregtechceu.gtceu.common.blockentity.DuctPipeBlockEntity; -import com.gregtechceu.gtceu.common.capability.EnvironmentalHazardSavedData; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.core.particles.ParticleTypes; -import net.minecraft.server.level.ServerLevel; - -import lombok.Getter; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.List; -import java.util.Objects; - -public class DuctNetHandler implements IHazardParticleContainer { - - @Getter - private DuctPipeNet net; - private final DuctPipeBlockEntity pipe; - private final Direction facing; - - public DuctNetHandler(DuctPipeNet net, @NotNull DuctPipeBlockEntity pipe, @Nullable Direction facing) { - this.net = net; - this.pipe = pipe; - this.facing = facing; - } - - public void updateNetwork(DuctPipeNet net) { - this.net = net; - } - - @Nullable - private IHazardParticleContainer getInnerContainer() { - if (net == null || pipe.isInValid() || facing == null || pipe.isBlocked(facing)) { - return null; - } - - final List data = net.getNetData(pipe.getPipePos(), facing); - if (data == null) { - return null; - } - - return new IHazardParticleContainer() { - - @Override - public boolean inputsHazard(Direction side, MedicalCondition condition) { - return data.stream() - .map(path -> path.getHandler(net.getLevel())) - .filter(Objects::nonNull) - .anyMatch(handler -> handler.inputsHazard(side, condition)); - } - - @Override - public float changeHazard(MedicalCondition condition, float differenceAmount) { - float total = 0; - for (DuctRoutePath path : data) { - IHazardParticleContainer handler = path.getHandler(net.getLevel()); - if (handler == null && path.getTargetPipe().isConnected(path.getTargetFacing())) { - if (net.getLevel().getBlockEntity(path.getTargetPipePos() - .relative(path.getTargetFacing())) instanceof IMachineBlockEntity machineBE && - machineBE.getMetaMachine() instanceof IEnvironmentalHazardCleaner cleaner) { - cleaner.cleanHazard(condition, differenceAmount); - break; - } - - var savedData = EnvironmentalHazardSavedData.getOrCreate(net.getLevel()); - savedData.addZone(path.getTargetPipePos().relative(path.getTargetFacing()), - differenceAmount, true, HazardProperty.HazardTrigger.INHALATION, condition); - total += differenceAmount; - emitPollutionParticles(net.getLevel(), path.getTargetPipePos(), path.getTargetFacing()); - break; - } else if (handler == null) { - continue; - } - float change = handler.changeHazard(condition, differenceAmount); - differenceAmount -= change; - total += change; - if (differenceAmount <= 0) { - break; - } - } - return total; - } - - @Override - public float getHazardStored(MedicalCondition condition) { - float total = 0; - for (DuctRoutePath path : data) { - IHazardParticleContainer handler = path.getHandler(net.getLevel()); - if (handler != null) { - total += handler.getHazardStored(condition); - } - } - return total; - } - - @Override - public float getHazardCapacity(MedicalCondition condition) { - float total = 0; - for (DuctRoutePath path : data) { - IHazardParticleContainer handler = path.getHandler(net.getLevel()); - if (handler != null) { - total += handler.getHazardCapacity(condition); - } else if (path.getTargetPipe().isConnected(path.getTargetFacing())) { - total += Integer.MAX_VALUE; - break; - } - } - return total; - } - }; - } - - @Override - public boolean inputsHazard(Direction side, MedicalCondition condition) { - IHazardParticleContainer handler = getInnerContainer(); - if (handler == null) return false; - return handler.inputsHazard(side, condition); - } - - @Override - public boolean outputsHazard(Direction side, MedicalCondition condition) { - return true; - } - - @Override - public float changeHazard(MedicalCondition condition, float differenceAmount) { - IHazardParticleContainer handler = getInnerContainer(); - if (handler == null) return 0; - return handler.changeHazard(condition, differenceAmount); - } - - @Override - public float getHazardStored(MedicalCondition condition) { - IHazardParticleContainer handler = getInnerContainer(); - if (handler == null) return 0; - return handler.getHazardStored(condition); - } - - @Override - public float getHazardCapacity(MedicalCondition condition) { - IHazardParticleContainer handler = getInnerContainer(); - if (handler == null) return 0; - return handler.getHazardCapacity(condition); - } - - public static void emitPollutionParticles(ServerLevel level, BlockPos pos, Direction frontFacing) { - float xPos = frontFacing.getStepX() * 0.76F + pos.getX() + 0.25F; - float yPos = frontFacing.getStepY() * 0.76F + pos.getY() + 0.25F; - float zPos = frontFacing.getStepZ() * 0.76F + pos.getZ() + 0.25F; - - float ySpd = frontFacing.getStepY() * 0.1F + 0.2F + 0.1F * GTValues.RNG.nextFloat(); - float xSpd; - float zSpd; - - if (frontFacing.getStepY() == -1) { - float temp = GTValues.RNG.nextFloat() * 2 * (float) Math.PI; - xSpd = (float) Math.sin(temp) * 0.1F; - zSpd = (float) Math.cos(temp) * 0.1F; - } else { - xSpd = frontFacing.getStepX() * (0.1F + 0.2F * GTValues.RNG.nextFloat()); - zSpd = frontFacing.getStepZ() * (0.1F + 0.2F * GTValues.RNG.nextFloat()); - } - level.sendParticles(ParticleTypes.LARGE_SMOKE, - xPos + GTValues.RNG.nextFloat() * 0.5F, - yPos + GTValues.RNG.nextFloat() * 0.5F, - zPos + GTValues.RNG.nextFloat() * 0.5F, - 1, - xSpd, ySpd, zSpd, - 0.1); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/duct/DuctNetWalker.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/duct/DuctNetWalker.java deleted file mode 100644 index 46878c5012..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/duct/DuctNetWalker.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.duct; - -import com.gregtechceu.gtceu.GTCEu; -import com.gregtechceu.gtceu.api.capability.IHazardParticleContainer; -import com.gregtechceu.gtceu.api.capability.forge.GTCapability; -import com.gregtechceu.gtceu.api.pipenet.PipeNetWalker; -import com.gregtechceu.gtceu.common.blockentity.DuctPipeBlockEntity; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraftforge.common.util.LazyOptional; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.List; - -public class DuctNetWalker extends PipeNetWalker { - - public static List createNetData(DuctPipeNet pipeNet, BlockPos sourcePipe, Direction sourceFacing) { - if (!(pipeNet.getLevel().getBlockEntity(sourcePipe) instanceof DuctPipeBlockEntity)) { - return null; - } - try { - DuctNetWalker walker = new DuctNetWalker(pipeNet, sourcePipe, 1, new ArrayList<>(), null); - walker.sourcePipe = sourcePipe; - walker.facingToHandler = sourceFacing; - walker.traversePipeNet(); - return walker.inventories; - } catch (Exception e) { - GTCEu.LOGGER.error("error while create net data for DuctPipeNet", e); - } - return null; - } - - private DuctPipeProperties minProperties; - private final List inventories; - private BlockPos sourcePipe; - private Direction facingToHandler; - - protected DuctNetWalker(DuctPipeNet world, BlockPos sourcePipe, int distance, List inventories, - DuctPipeProperties properties) { - super(world, sourcePipe, distance); - this.inventories = inventories; - this.minProperties = properties; - } - - @NotNull - @Override - protected PipeNetWalker createSubWalker(DuctPipeNet pipeNet, - Direction facingToNextPos, - BlockPos nextPos, - int walkedBlocks) { - DuctNetWalker walker = new DuctNetWalker(pipeNet, nextPos, walkedBlocks, inventories, minProperties); - walker.facingToHandler = facingToHandler; - walker.sourcePipe = sourcePipe; - return walker; - } - - @Override - protected Class getBasePipeClass() { - return DuctPipeBlockEntity.class; - } - - @Override - protected void checkPipe(DuctPipeBlockEntity pipeTile, BlockPos pos) { - DuctPipeProperties pipeProperties = pipeTile.getNodeData(); - if (minProperties == null) { - minProperties = pipeProperties; - } else { - minProperties = new DuctPipeProperties( - Math.min(minProperties.getTransferRate(), pipeProperties.getTransferRate())); - } - } - - @Override - protected void checkNeighbour(DuctPipeBlockEntity pipeTile, BlockPos pipePos, Direction faceToNeighbour, - @Nullable BlockEntity neighbourTile) { - if ((pipePos.equals(sourcePipe) && faceToNeighbour == facingToHandler)) { - return; - } - if (neighbourTile != null) { - LazyOptional handler = neighbourTile.getCapability( - GTCapability.CAPABILITY_HAZARD_CONTAINER, - faceToNeighbour.getOpposite()); - if (handler.isPresent()) { - inventories.add(new DuctRoutePath(pipeTile, faceToNeighbour, getWalkedBlocks(), minProperties)); - } - } else if (pipeTile.isConnected(faceToNeighbour)) { - inventories.add(new DuctRoutePath(pipeTile, faceToNeighbour, getWalkedBlocks(), minProperties)); - } - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/duct/DuctPipeNet.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/duct/DuctPipeNet.java deleted file mode 100644 index 26d7247ed6..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/duct/DuctPipeNet.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.duct; - -import com.gregtechceu.gtceu.api.pipenet.LevelPipeNet; -import com.gregtechceu.gtceu.api.pipenet.Node; -import com.gregtechceu.gtceu.api.pipenet.PipeNet; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.nbt.CompoundTag; - -import java.util.*; - -public class DuctPipeNet extends PipeNet { - - private final Map> NET_DATA = new HashMap<>(); - - public DuctPipeNet(LevelPipeNet> world) { - super(world); - } - - public List getNetData(BlockPos pipePos, Direction facing) { - List data = NET_DATA.get(pipePos); - if (data == null) { - data = DuctNetWalker.createNetData(this, pipePos, facing); - if (data == null) { - // walker failed, don't cache so it tries again on next insertion - return Collections.emptyList(); - } - NET_DATA.put(pipePos, data); - } - return data; - } - - @Override - public void onNeighbourUpdate(BlockPos fromPos) { - NET_DATA.clear(); - } - - @Override - public void onPipeConnectionsUpdate() { - NET_DATA.clear(); - } - - @Override - protected void transferNodeData(Map> transferredNodes, - PipeNet parentNet) { - super.transferNodeData(transferredNodes, parentNet); - NET_DATA.clear(); - ((DuctPipeNet) parentNet).NET_DATA.clear(); - } - - @Override - protected void writeNodeData(DuctPipeProperties nodeData, CompoundTag tagCompound) { - tagCompound.putFloat("Rate", nodeData.getTransferRate()); - } - - @Override - protected DuctPipeProperties readNodeData(CompoundTag tagCompound) { - return new DuctPipeProperties(tagCompound.getFloat("Rate")); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/duct/DuctPipeProperties.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/duct/DuctPipeProperties.java deleted file mode 100644 index 2812f1b832..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/duct/DuctPipeProperties.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.duct; - -import lombok.Getter; -import lombok.Setter; - -import java.util.Objects; - -public class DuctPipeProperties { - - /** - * rate in stacks per sec - */ - @Getter - @Setter - private float transferRate; - - public DuctPipeProperties(float transferRate) { - this.transferRate = transferRate; - } - - /** - * Default property constructor. - */ - public DuctPipeProperties() { - this(0.25f); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - DuctPipeProperties that = (DuctPipeProperties) o; - return Float.compare(that.transferRate, transferRate) == 0; - } - - @Override - public int hashCode() { - return Objects.hash(transferRate); - } - - @Override - public String toString() { - return "DuctPipeProperties{" + - "transferRate=" + transferRate + - '}'; - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/duct/DuctPipeType.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/duct/DuctPipeType.java deleted file mode 100644 index 1323fa297f..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/duct/DuctPipeType.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.duct; - -import com.gregtechceu.gtceu.GTCEu; -import com.gregtechceu.gtceu.api.pipenet.IPipeType; -import com.gregtechceu.gtceu.client.model.PipeModel; - -import net.minecraft.resources.ResourceLocation; -import net.minecraft.util.StringRepresentable; - -import lombok.Getter; - -import java.util.Locale; - -public enum DuctPipeType implements IPipeType, StringRepresentable { - - SMALL("small", 0.375f, 2f), - NORMAL("normal", 0.5f, 4f), - LARGE("large", 0.75f, 8f), - HUGE("huge", 0.875f, 16f), - ; - - public static final ResourceLocation TYPE_ID = GTCEu.id("duct"); - public static final DuctPipeType[] VALUES = values(); - - @Getter - public final String name; - @Getter - private final float thickness; - @Getter - private final float rateMultiplier; - - DuctPipeType(String name, float thickness, float rateMultiplier) { - this.name = name; - this.thickness = thickness; - this.rateMultiplier = rateMultiplier; - } - - @Override - public DuctPipeProperties modifyProperties(DuctPipeProperties baseProperties) { - return baseProperties; - } - - @Override - public boolean isPaintable() { - return true; - } - - @Override - public ResourceLocation type() { - return TYPE_ID; - } - - @Override - public String getSerializedName() { - return name().toLowerCase(Locale.ROOT); - } - - public PipeModel createPipeModel() { - return new PipeModel(thickness, () -> GTCEu.id("block/pipe/pipe_duct_side"), - () -> GTCEu.id("block/pipe/pipe_duct_in"), - null, null); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/duct/DuctRoutePath.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/duct/DuctRoutePath.java deleted file mode 100644 index 8488e7e589..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/duct/DuctRoutePath.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.duct; - -import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; -import com.gregtechceu.gtceu.api.capability.IHazardParticleContainer; -import com.gregtechceu.gtceu.api.pipenet.IRoutePath; -import com.gregtechceu.gtceu.common.blockentity.DuctPipeBlockEntity; -import com.gregtechceu.gtceu.utils.FacingPos; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.world.level.Level; - -import lombok.Getter; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class DuctRoutePath implements IRoutePath { - - @Getter - private final DuctPipeBlockEntity targetPipe; - @NotNull - @Getter - private final Direction targetFacing; - @Getter - private final int distance; - @Getter - private final DuctPipeProperties properties; - - public DuctRoutePath(DuctPipeBlockEntity targetPipe, @NotNull Direction facing, int distance, - DuctPipeProperties properties) { - this.targetPipe = targetPipe; - this.targetFacing = facing; - this.distance = distance; - this.properties = properties; - } - - @Override - public @NotNull BlockPos getTargetPipePos() { - return targetPipe.getPipePos(); - } - - @Override - public @Nullable IHazardParticleContainer getHandler(Level world) { - return GTCapabilityHelper.getHazardContainer(world, getTargetPipePos().relative(targetFacing), - targetFacing.getOpposite()); - } - - public FacingPos toFacingPos() { - return new FacingPos(getTargetPipePos(), targetFacing); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/duct/LevelDuctPipeNet.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/duct/LevelDuctPipeNet.java deleted file mode 100644 index ab88dab06a..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/duct/LevelDuctPipeNet.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.duct; - -import com.gregtechceu.gtceu.api.pipenet.LevelPipeNet; - -import net.minecraft.nbt.CompoundTag; -import net.minecraft.server.level.ServerLevel; - -public class LevelDuctPipeNet extends LevelPipeNet { - - private static final String DATA_ID = "gtceu_duct_pipe_net"; - - public static LevelDuctPipeNet getOrCreate(ServerLevel serverLevel) { - return serverLevel.getDataStorage().computeIfAbsent(tag -> new LevelDuctPipeNet(serverLevel, tag), - () -> new LevelDuctPipeNet(serverLevel), DATA_ID); - } - - public LevelDuctPipeNet(ServerLevel serverLevel) { - super(serverLevel); - } - - public LevelDuctPipeNet(ServerLevel serverLevel, CompoundTag tag) { - super(serverLevel, tag); - } - - @Override - protected DuctPipeNet createNetInstance() { - return new DuctPipeNet(this); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/fluidpipe/FluidPipeData.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/fluidpipe/FluidPipeData.java deleted file mode 100644 index faa0980a9b..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/fluidpipe/FluidPipeData.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.fluidpipe; - -import com.gregtechceu.gtceu.api.data.chemical.material.properties.FluidPipeProperties; -import com.gregtechceu.gtceu.api.pipenet.IAttachData; - -import net.minecraft.core.Direction; - -import lombok.Getter; -import lombok.experimental.Accessors; - -import java.util.Objects; - -/** - * @author KilaBash - * @date 2023/3/1 - * @implNote CableData - */ -@Accessors(fluent = true) -public class FluidPipeData implements IAttachData { - - @Getter - FluidPipeProperties properties; - @Getter - byte connections; - - public FluidPipeData(FluidPipeProperties properties, byte connections) { - this.properties = properties; - this.connections = connections; - } - - @Override - public boolean canAttachTo(Direction side) { - return (connections & (1 << side.ordinal())) != 0; - } - - @Override - public boolean setAttached(Direction side, boolean attach) { - var result = canAttachTo(side); - if (result != attach) { - if (attach) { - connections |= (1 << side.ordinal()); - } else { - connections &= ~(1 << side.ordinal()); - } - } - return result != attach; - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof FluidPipeData cableData) { - return cableData.properties.equals(properties) && connections == cableData.connections; - } - return super.equals(obj); - } - - @Override - public int hashCode() { - return Objects.hash(properties, connections); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/fluidpipe/FluidPipeNet.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/fluidpipe/FluidPipeNet.java deleted file mode 100644 index 6402827d30..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/fluidpipe/FluidPipeNet.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.fluidpipe; - -import com.gregtechceu.gtceu.api.data.chemical.material.properties.FluidPipeProperties; -import com.gregtechceu.gtceu.api.pipenet.LevelPipeNet; -import com.gregtechceu.gtceu.api.pipenet.PipeNet; - -import net.minecraft.nbt.CompoundTag; - -/** - * @author KilaBash - * @date 2023/3/11 - * @implNote FluidPipeNet - */ -public class FluidPipeNet extends PipeNet { - - public FluidPipeNet(LevelPipeNet world) { - super(world); - } - - ///////////////////////////////////// - // *********** NBT ***********// - ///////////////////////////////////// - - @Override - protected void writeNodeData(FluidPipeProperties nodeData, CompoundTag tagCompound) { - tagCompound.putInt("max_temperature", nodeData.getMaxFluidTemperature()); - tagCompound.putInt("throughput", nodeData.getThroughput()); - tagCompound.putBoolean("gas_proof", nodeData.isGasProof()); - tagCompound.putBoolean("acid_proof", nodeData.isAcidProof()); - tagCompound.putBoolean("cryo_proof", nodeData.isCryoProof()); - tagCompound.putBoolean("plasma_proof", nodeData.isPlasmaProof()); - tagCompound.putInt("channels", nodeData.getChannels()); - } - - @Override - protected FluidPipeProperties readNodeData(CompoundTag tagCompound) { - int maxTemperature = tagCompound.getInt("max_temperature"); - int throughput = tagCompound.getInt("throughput"); - boolean gasProof = tagCompound.getBoolean("gas_proof"); - boolean acidProof = tagCompound.getBoolean("acid_proof"); - boolean cryoProof = tagCompound.getBoolean("cryo_proof"); - boolean plasmaProof = tagCompound.getBoolean("plasma_proof"); - int channels = tagCompound.getInt("channels"); - return new FluidPipeProperties(maxTemperature, throughput, gasProof, acidProof, cryoProof, plasmaProof, - channels); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/fluidpipe/FluidPipeType.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/fluidpipe/FluidPipeType.java deleted file mode 100644 index 73abe9df8d..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/fluidpipe/FluidPipeType.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.fluidpipe; - -import com.gregtechceu.gtceu.GTCEu; -import com.gregtechceu.gtceu.api.data.chemical.material.Material; -import com.gregtechceu.gtceu.api.data.chemical.material.properties.FluidPipeProperties; -import com.gregtechceu.gtceu.api.data.chemical.material.properties.PropertyKey; -import com.gregtechceu.gtceu.api.data.tag.TagPrefix; -import com.gregtechceu.gtceu.api.pipenet.IMaterialPipeType; -import com.gregtechceu.gtceu.client.model.PipeModel; - -import net.minecraft.resources.ResourceLocation; - -import lombok.Getter; - -import static com.gregtechceu.gtceu.api.data.tag.TagPrefix.*; - -public enum FluidPipeType implements IMaterialPipeType { - - TINY("tiny", 0.25f, 1, pipeTinyFluid), - SMALL("small", 0.375f, 2, pipeSmallFluid), - NORMAL("normal", 0.5f, 6, pipeNormalFluid), - LARGE("large", 0.75f, 12, pipeLargeFluid), - HUGE("huge", 0.875f, 24, pipeHugeFluid), - QUADRUPLE("quadruple", 0.95f, 2, pipeQuadrupleFluid, 4), - NONUPLE("nonuple", 0.95f, 2, pipeNonupleFluid, 9); - - public static final ResourceLocation TYPE_ID = GTCEu.id("fluid"); - - public final String name; - public final float thickness; - public final int capacityMultiplier; - @Getter - public final TagPrefix tagPrefix; - public final int channels; - - FluidPipeType(String name, float thickness, int capacityMultiplier, TagPrefix TagPrefix) { - this(name, thickness, capacityMultiplier, TagPrefix, 1); - } - - FluidPipeType(String name, float thickness, int capacityMultiplier, TagPrefix TagPrefix, int channels) { - this.name = name; - this.thickness = thickness; - this.capacityMultiplier = capacityMultiplier; - this.tagPrefix = TagPrefix; - this.channels = channels; - } - - @Override - public float getThickness() { - return thickness; - } - - @Override - public FluidPipeProperties modifyProperties(FluidPipeProperties fluidPipeData) { - return new FluidPipeProperties( - fluidPipeData.getMaxFluidTemperature(), - fluidPipeData.getThroughput() * capacityMultiplier, - fluidPipeData.isGasProof(), - fluidPipeData.isAcidProof(), - fluidPipeData.isCryoProof(), - fluidPipeData.isPlasmaProof(), - channels); - } - - @Override - public boolean isPaintable() { - return true; - } - - @Override - public ResourceLocation type() { - return TYPE_ID; - } - - public PipeModel createPipeModel(Material material) { - if (material.hasProperty(PropertyKey.WOOD)) { - return new PipeModel(thickness, () -> GTCEu.id("block/pipe/pipe_side_wood"), - () -> GTCEu.id("block/pipe/pipe_%s_in_wood".formatted(name)), null, null); - } - return new PipeModel(thickness, () -> GTCEu.id("block/pipe/pipe_side"), - () -> GTCEu.id("block/pipe/pipe_%s_in".formatted(name)), - null, null/* - * () -> GTCEu.id("block/pipe/pipe_side_secondary"), () -> - * GTCEu.id("block/pipe/pipe_%s_in_secondary".formatted(name)) TODO enable once the textures - * are added - */); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/fluidpipe/LevelFluidPipeNet.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/fluidpipe/LevelFluidPipeNet.java deleted file mode 100644 index 5e9b8084a4..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/fluidpipe/LevelFluidPipeNet.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.fluidpipe; - -import com.gregtechceu.gtceu.api.data.chemical.material.properties.FluidPipeProperties; -import com.gregtechceu.gtceu.api.pipenet.LevelPipeNet; - -import net.minecraft.nbt.CompoundTag; -import net.minecraft.server.level.ServerLevel; - -public class LevelFluidPipeNet extends LevelPipeNet { - - public static LevelFluidPipeNet getOrCreate(ServerLevel serverLevel) { - return serverLevel.getDataStorage().computeIfAbsent(tag -> new LevelFluidPipeNet(serverLevel, tag), - () -> new LevelFluidPipeNet(serverLevel), "gtcue_fluid_pipe_net"); - } - - public LevelFluidPipeNet(ServerLevel serverLevel) { - super(serverLevel); - } - - public LevelFluidPipeNet(ServerLevel serverLevel, CompoundTag tag) { - super(serverLevel, tag); - } - - @Override - protected FluidPipeNet createNetInstance() { - return new FluidPipeNet(this); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/fluidpipe/PipeNetRoutePath.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/fluidpipe/PipeNetRoutePath.java deleted file mode 100644 index 89d94566d4..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/fluidpipe/PipeNetRoutePath.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.fluidpipe; - -import com.gregtechceu.gtceu.api.pipenet.IRoutePath; -import com.gregtechceu.gtceu.utils.GTTransferUtils; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.world.level.Level; -import net.minecraftforge.fluids.capability.IFluidHandler; - -import lombok.Getter; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import oshi.util.tuples.Pair; - -public class PipeNetRoutePath implements IRoutePath { - - @Getter - private final BlockPos pipePos; - @Getter - private final Direction targetFacing; - @Getter - private final int distance; - private final Pair[] path; - - public PipeNetRoutePath(BlockPos pipePos, Direction targetFacing, Pair[] path, - int distance) { - this.pipePos = pipePos; - this.targetFacing = targetFacing; - this.path = path; - this.distance = distance; - } - - public Pair[] getPath() { - return path; - } - - @Override - @NotNull - public BlockPos getTargetPipePos() { - return pipePos.relative(targetFacing); - } - - @Nullable - public IFluidHandler getHandler(Level world) { - return GTTransferUtils.getAdjacentFluidHandler(world, pipePos, targetFacing).resolve().orElse(null); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/fluidpipe/PipeTankList.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/fluidpipe/PipeTankList.java deleted file mode 100644 index ef42b1f842..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/fluidpipe/PipeTankList.java +++ /dev/null @@ -1,135 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.fluidpipe; - -import com.gregtechceu.gtceu.api.transfer.fluid.CustomFluidTank; -import com.gregtechceu.gtceu.api.transfer.fluid.IFluidHandlerModifiable; -import com.gregtechceu.gtceu.common.blockentity.FluidPipeBlockEntity; - -import net.minecraft.core.Direction; -import net.minecraftforge.fluids.FluidStack; - -import org.jetbrains.annotations.NotNull; - -import java.util.Arrays; -import java.util.Iterator; - -@SuppressWarnings("UnstableApiUsage") -public class PipeTankList implements IFluidHandlerModifiable, Iterable { - - private final FluidPipeBlockEntity pipe; - private final CustomFluidTank[] tanks; - private final Direction facing; - - public PipeTankList(FluidPipeBlockEntity pipe, Direction facing, CustomFluidTank... fluidTanks) { - this.tanks = fluidTanks; - this.pipe = pipe; - this.facing = facing; - } - - private int findChannel(FluidStack stack) { - if (stack.isEmpty() || tanks == null) - return -1; - int empty = -1; - for (int i = tanks.length - 1; i >= 0; i--) { - FluidStack f = tanks[i].getFluid(); - if (f.isEmpty()) - empty = i; - else if (f.isFluidEqual(stack)) - return i; - } - return empty; - } - - @Override - public int getTanks() { - return tanks.length; - } - - @NotNull - @Override - public FluidStack getFluidInTank(int tank) { - return tanks[tank].getFluid(); - } - - @Override - public void setFluidInTank(int tank, @NotNull FluidStack fluidStack) { - tanks[tank].setFluid(fluidStack); - } - - @Override - public int getTankCapacity(int tank) { - return tanks[tank].getCapacity(); - } - - @Override - public boolean isFluidValid(int tank, @NotNull FluidStack stack) { - return tanks[tank].isFluidValid(stack); - } - - private int fullCapacity() { - return tanks.length * pipe.getCapacityPerTank(); - } - - @Override - public int fill(FluidStack resource, FluidAction action) { - int channel; - if (pipe.isBlocked(facing) || resource.getAmount() < 0 || (channel = findChannel(resource)) < 0) return 0; - return fill(resource, action, channel); - } - - private int fill(FluidStack resource, FluidAction action, int channel) { - if (channel >= tanks.length) return 0; - CustomFluidTank tank = tanks[channel]; - FluidStack currentFluid = tank.getFluid(); - - if (currentFluid.isEmpty() || currentFluid.getAmount() <= 0) { - FluidStack newFluid = resource.copy(); - newFluid.setAmount(Math.min(pipe.getCapacityPerTank(), newFluid.getAmount())); - if (action.execute()) { - tank.setFluid(newFluid); - pipe.receivedFrom(facing); - pipe.checkAndDestroy(newFluid); - } - return newFluid.getAmount(); - } - if (currentFluid.isFluidEqual(resource)) { - int toAdd = Math.min(tank.getCapacity() - currentFluid.getAmount(), resource.getAmount()); - if (toAdd > 0) { - if (action.execute()) { - currentFluid.setAmount(currentFluid.getAmount() + toAdd); - pipe.receivedFrom(facing); - pipe.checkAndDestroy(currentFluid); - } - return toAdd; - } - } - - return 0; - } - - @Override - public @NotNull FluidStack drain(int maxDrain, FluidAction action) { - if (maxDrain <= 0) return FluidStack.EMPTY; - for (CustomFluidTank tank : tanks) { - FluidStack drained = tank.drain(maxDrain, action); - if (!drained.isEmpty()) return drained; - } - return FluidStack.EMPTY; - } - - @Override - public @NotNull FluidStack drain(FluidStack resource, FluidAction action) { - if (resource.getAmount() <= 0) return FluidStack.EMPTY; - resource = resource.copy(); - for (CustomFluidTank tank : tanks) { - FluidStack drained = tank.drain(resource, action); - if (!drained.isEmpty()) return drained; - } - return FluidStack.EMPTY; - } - - @Override - @NotNull - public Iterator iterator() { - return Arrays.stream(tanks).iterator(); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/handlers/DuctNetHandler.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/handlers/DuctNetHandler.java new file mode 100644 index 0000000000..898fc38dab --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/handlers/DuctNetHandler.java @@ -0,0 +1,62 @@ +package com.gregtechceu.gtceu.common.pipelike.handlers; + +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.IPipeNetNodeHandler; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IPipeStructure; +import com.gregtechceu.gtceu.common.pipelike.block.duct.DuctStructure; +import com.gregtechceu.gtceu.common.pipelike.net.duct.WorldDuctNet; + +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.BlockGetter; + +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +public final class DuctNetHandler implements IPipeNetNodeHandler { + + public static final DuctNetHandler INSTANCE = new DuctNetHandler(); + + @Override + public @NotNull Collection getOrCreateFromNets(ServerLevel world, BlockPos pos, + IPipeStructure structure) { + if (structure instanceof DuctStructure duct) { + WorldPipeNode node = WorldDuctNet.getWorldNet(world).getOrCreateNode(pos); + duct.mutateData(node.getData()); + return Collections.singletonList(node); + } + return Collections.emptyList(); + } + + @Override + public @NotNull Collection getFromNets(ServerLevel world, BlockPos pos, + IPipeStructure structure) { + if (structure instanceof DuctStructure duct) { + WorldPipeNode node = WorldDuctNet.getWorldNet(world).getNode(pos); + if (node != null) return Collections.singletonList(node); + } + return Collections.emptyList(); + } + + @Override + public void removeFromNets(ServerLevel world, BlockPos pos, IPipeStructure structure) { + if (structure instanceof DuctStructure) { + WorldDuctNet net = WorldDuctNet.getWorldNet(world); + NetNode node = net.getNode(pos); + if (node != null) net.removeNode(node); + } + } + + @Override + public void addInformation(@NotNull ItemStack stack, BlockGetter worldIn, @NotNull List tooltip, + @NotNull TooltipFlag flagIn, IPipeStructure structure) { + tooltip.add(Component.translatable("block.gtceu.normal_optical_pipe.tooltip")); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/handlers/LaserNetHandler.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/handlers/LaserNetHandler.java new file mode 100644 index 0000000000..8bb84b8315 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/handlers/LaserNetHandler.java @@ -0,0 +1,63 @@ +package com.gregtechceu.gtceu.common.pipelike.handlers; + +import com.gregtechceu.gtceu.api.graphnet.pipenet.IPipeNetNodeHandler; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IPipeStructure; +import com.gregtechceu.gtceu.common.pipelike.block.laser.LaserStructure; +import com.gregtechceu.gtceu.common.pipelike.net.laser.WorldLaserNet; + +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.BlockGetter; + +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +public final class LaserNetHandler implements IPipeNetNodeHandler { + + public static final LaserNetHandler INSTANCE = new LaserNetHandler(); + + @Override + public @NotNull Collection getOrCreateFromNets(ServerLevel world, BlockPos pos, + IPipeStructure structure) { + if (structure instanceof LaserStructure) { + return Collections.singletonList(WorldLaserNet.getWorldNet(world).getOrCreateNode(pos)); + } + return Collections.emptyList(); + } + + @Override + public @NotNull Collection getFromNets(ServerLevel world, BlockPos pos, + IPipeStructure structure) { + if (structure instanceof LaserStructure) { + WorldPipeNode node = WorldLaserNet.getWorldNet(world).getNode(pos); + if (node != null) return Collections.singletonList(node); + } + return Collections.emptyList(); + } + + @Override + public void removeFromNets(ServerLevel world, BlockPos pos, IPipeStructure structure) { + if (structure instanceof LaserStructure) { + WorldLaserNet net = WorldLaserNet.getWorldNet(world); + WorldPipeNode node = net.getNode(pos); + if (node != null) net.removeNode(node); + } + } + + @Override + public void addInformation(@NotNull ItemStack stack, BlockGetter worldIn, @NotNull List tooltip, + @NotNull TooltipFlag flagIn, IPipeStructure structure) { + if (structure instanceof LaserStructure laser && laser.mirror()) { + tooltip.add(Component.translatable("block.gtceu.laser_pipe_mirror.tooltip")); + return; + } + tooltip.add(Component.translatable("block.gtceu.normal_laser_pipe.tooltip")); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/handlers/OpticalNetHandler.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/handlers/OpticalNetHandler.java new file mode 100644 index 0000000000..2beb0962c2 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/handlers/OpticalNetHandler.java @@ -0,0 +1,59 @@ +package com.gregtechceu.gtceu.common.pipelike.handlers; + +import com.gregtechceu.gtceu.api.graphnet.pipenet.IPipeNetNodeHandler; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IPipeStructure; +import com.gregtechceu.gtceu.common.pipelike.block.optical.OpticalStructure; +import com.gregtechceu.gtceu.common.pipelike.net.optical.WorldOpticalNet; + +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.BlockGetter; + +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +public final class OpticalNetHandler implements IPipeNetNodeHandler { + + public static final DuctNetHandler INSTANCE = new DuctNetHandler(); + + @Override + public @NotNull Collection getOrCreateFromNets(ServerLevel world, BlockPos pos, + IPipeStructure structure) { + if (structure instanceof OpticalStructure) { + return Collections.singletonList(WorldOpticalNet.getWorldNet(world).getOrCreateNode(pos)); + } + return Collections.emptyList(); + } + + @Override + public @NotNull Collection getFromNets(ServerLevel world, BlockPos pos, + IPipeStructure structure) { + if (structure instanceof OpticalStructure) { + WorldPipeNode node = WorldOpticalNet.getWorldNet(world).getNode(pos); + if (node != null) return Collections.singletonList(node); + } + return Collections.emptyList(); + } + + @Override + public void removeFromNets(ServerLevel world, BlockPos pos, IPipeStructure structure) { + if (structure instanceof OpticalStructure) { + WorldOpticalNet net = WorldOpticalNet.getWorldNet(world); + WorldPipeNode node = net.getNode(pos); + if (node != null) net.removeNode(node); + } + } + + @Override + public void addInformation(@NotNull ItemStack stack, BlockGetter worldIn, @NotNull List tooltip, + @NotNull TooltipFlag flagIn, IPipeStructure structure) { + tooltip.add(Component.translatable("block.gtceu.normal_optical_pipe.tooltip")); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/handlers/properties/MaterialEnergyProperties.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/handlers/properties/MaterialEnergyProperties.java new file mode 100644 index 0000000000..09f2ffcdba --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/handlers/properties/MaterialEnergyProperties.java @@ -0,0 +1,244 @@ +package com.gregtechceu.gtceu.common.pipelike.handlers.properties; + +import com.gregtechceu.gtceu.api.GTValues; +import com.gregtechceu.gtceu.api.data.chemical.material.Material; +import com.gregtechceu.gtceu.api.data.chemical.material.properties.FluidProperty; +import com.gregtechceu.gtceu.api.data.chemical.material.properties.MaterialProperties; +import com.gregtechceu.gtceu.api.data.chemical.material.properties.PipeNetProperties; +import com.gregtechceu.gtceu.api.data.chemical.material.properties.PropertyKey; +import com.gregtechceu.gtceu.api.data.tag.TagPrefix; +import com.gregtechceu.gtceu.api.fluids.FluidBuilder; +import com.gregtechceu.gtceu.api.fluids.store.FluidStorageKeys; +import com.gregtechceu.gtceu.api.graphnet.logic.NetLogicData; +import com.gregtechceu.gtceu.api.graphnet.logic.WeightFactorLogic; +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.logic.TemperatureLogic; +import com.gregtechceu.gtceu.api.graphnet.pipenet.logic.TemperatureLossFunction; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IPipeMaterialStructure; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IPipeStructure; +import com.gregtechceu.gtceu.common.pipelike.block.cable.CableStructure; +import com.gregtechceu.gtceu.common.pipelike.block.pipe.MaterialPipeStructure; +import com.gregtechceu.gtceu.common.pipelike.net.energy.*; +import com.gregtechceu.gtceu.utils.GTUtil; + +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.material.Fluid; + +import lombok.Getter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +import static com.gregtechceu.gtceu.api.data.chemical.material.info.MaterialFlags.GENERATE_FOIL; +import static com.gregtechceu.gtceu.api.data.chemical.material.info.MaterialFlags.NO_UNIFICATION; + +public final class MaterialEnergyProperties implements PipeNetProperties.IPipeNetMaterialProperty { + + public static final MaterialPropertyKey KEY = new MaterialPropertyKey<>( + "EnergyProperties"); + + private static final int MINIMUM_MELT_TEMPERATURE = 1500; + + @Getter + private final long voltageLimit; + private final long amperageLimit; + private int materialMeltTemperature; + private final long lossPerAmp; + @Getter + private final boolean superconductor; + + /** + * Generate a MaterialEnergyProperties + * + * @param voltageLimit the voltage limit for the cable + * @param amperageLimit the base amperage for the cable. + * @param lossPerAmp the base loss per amp per block traveled. + * @param superconductor whether the material will be treated as a superconductor. Does not override loss. + */ + public MaterialEnergyProperties(long voltageLimit, long amperageLimit, long lossPerAmp, + boolean superconductor) { + this.voltageLimit = voltageLimit; + this.amperageLimit = amperageLimit; + this.lossPerAmp = lossPerAmp; + this.superconductor = superconductor; + } + + public static MaterialEnergyProperties create(long voltageLimit, long amperageLimit, long lossPerAmp, + boolean superconductor) { + return new MaterialEnergyProperties(voltageLimit, amperageLimit, lossPerAmp, superconductor); + } + + public static MaterialEnergyProperties create(long voltageLimit, long amperageLimit, long lossPerAmp) { + return new MaterialEnergyProperties(voltageLimit, amperageLimit, lossPerAmp, false); + } + + public static TagPrefix.MaterialRecipeHandler registrationHandler(TagPrefix.PropertyMaterialRecipeHandler handler) { + return (orePrefix, material, provider) -> { + if (material.hasProperty(PropertyKey.PIPENET_PROPERTIES) && !material.hasFlag(NO_UNIFICATION) && + material.getProperty(PropertyKey.PIPENET_PROPERTIES).hasProperty(KEY)) { + handler.accept(orePrefix, material, + material.getProperty(PropertyKey.PIPENET_PROPERTIES).getProperty(KEY), provider); + } + }; + } + + @Override + public MaterialPropertyKey getKey() { + return KEY; + } + + @Override + public void addInformation(@NotNull ItemStack stack, BlockGetter worldIn, @NotNull List tooltip, + @NotNull TooltipFlag flagIn, IPipeMaterialStructure structure) { + int tier = GTUtil.getTierByVoltage(voltageLimit); + if (isSuperconductor()) + tooltip.add(Component.translatable("gtceu.cable.superconductor", GTValues.VN[tier])); + tooltip.add(Component.translatable("gtceu.cable.voltage", voltageLimit, GTValues.VNF[tier])); + tooltip.add(Component.translatable("gtceu.cable.amperage", getAmperage(structure))); + + long loss = getLoss(structure); + tooltip.add(Component.translatable("gtceu.cable.loss_per_block", loss)); + } + + @Override + public void verifyProperty(MaterialProperties properties) { + properties.ensureSet(PropertyKey.DUST, true); + if (properties.hasProperty(PropertyKey.INGOT)) { + // Ensure all Materials with Cables and voltage tier IV or above have a Foil for recipe generation + Material thisMaterial = properties.getMaterial(); + if (!isSuperconductor() && voltageLimit >= GTValues.V[GTValues.IV] && + !thisMaterial.hasFlag(GENERATE_FOIL)) { + thisMaterial.addFlags(GENERATE_FOIL); + } + } + this.materialMeltTemperature = computeMaterialMeltTemperature(properties); + } + + private static int computeMaterialMeltTemperature(@NotNull MaterialProperties properties) { + if (properties.hasProperty(PropertyKey.FLUID)) { + // autodetermine melt temperature from registered fluid + FluidProperty prop = properties.getProperty(PropertyKey.FLUID); + Fluid fluid = prop.get(FluidStorageKeys.LIQUID); + if (fluid == null) { + FluidBuilder builder = prop.getQueuedBuilder(FluidStorageKeys.LIQUID); + if (builder != null) { + return Math.max(MINIMUM_MELT_TEMPERATURE, + builder.getDeterminedTemperature(properties.getMaterial(), FluidStorageKeys.LIQUID)); + } + } else { + return Math.max(MINIMUM_MELT_TEMPERATURE, fluid.getFluidType().getTemperature()); + } + } + return MINIMUM_MELT_TEMPERATURE; + } + + @Override + @Nullable + public WorldPipeNode getOrCreateFromNet(ServerLevel world, BlockPos pos, IPipeStructure structure) { + if (structure instanceof CableStructure) { + WorldPipeNode node = WorldEnergyNet.getWorldNet(world).getOrCreateNode(pos); + mutateData(node.getData(), structure); + return node; + } else if (structure instanceof MaterialPipeStructure pipe) { + long amperage = amperageLimit * pipe.material() / 2; + if (amperage == 0) return null; // skip pipes that are too small + WorldPipeNode node = WorldEnergyNet.getWorldNet(world).getOrCreateNode(pos); + mutateData(node.getData(), pipe); + return node; + } + return null; + } + + @Override + public void mutateData(NetLogicData data, IPipeStructure structure) { + if (structure instanceof CableStructure cable) { + long loss = getLoss(structure); + long amperage = getAmperage(structure); + boolean insulated = cable.partialBurnStructure() != null; + // insulated cables cool down half as fast + float coolingFactor = (float) (Math.sqrt(cable.material()) / (insulated ? 8 : 4)); + TemperatureLogic existing = data.getLogicEntryNullable(TemperatureLogic.TYPE); + float energy = existing == null ? 0 : existing.getThermalEnergy(); + data.setLogicEntry(VoltageLossLogic.TYPE.getWith(loss)) + .setLogicEntry(WeightFactorLogic.TYPE.getWith(loss + 0.001 / amperage)) + .setLogicEntry(AmperageLimitLogic.TYPE.getWith(amperage)) + .setLogicEntry(VoltageLimitLogic.TYPE.getWith(voltageLimit)) + .setLogicEntry(TemperatureLogic.TYPE + .getWith(TemperatureLossFunction.getOrCreateCable(coolingFactor), materialMeltTemperature, + 1, + 100 * cable.material(), cable.partialBurnThreshold()) + .setInitialThermalEnergy(energy)); + if (superconductor) { + data.setLogicEntry(SuperconductorLogic.TYPE.getNew()); + } + } else if (structure instanceof MaterialPipeStructure pipe) { + long amperage = getAmperage(structure); + if (amperage == 0) return; // skip pipes that are too small + long loss = getLoss(structure); + float coolingFactor = (float) Math.sqrt((double) pipe.material() / (4 + pipe.channelCount())); + TemperatureLogic existing = data.getLogicEntryNullable(TemperatureLogic.TYPE); + float energy = existing == null ? 0 : existing.getThermalEnergy(); + data.setLogicEntry(VoltageLossLogic.TYPE.getWith(loss)) + .setLogicEntry(WeightFactorLogic.TYPE.getWith(loss + 0.001 / amperage)) + .setLogicEntry(AmperageLimitLogic.TYPE.getWith(amperage)) + .setLogicEntry(VoltageLimitLogic.TYPE.getWith(voltageLimit)) + .setLogicEntry(TemperatureLogic.TYPE + .getWith(TemperatureLossFunction.getOrCreatePipe(coolingFactor), materialMeltTemperature, 1, + 50 * pipe.material(), null) + .setInitialThermalEnergy(energy)); + if (superconductor) { + data.setLogicEntry(SuperconductorLogic.TYPE.getNew()); + } + } + } + + private long getLoss(IPipeStructure structure) { + if (structure instanceof CableStructure cable) { + return lossPerAmp * cable.costFactor(); + } else if (structure instanceof MaterialPipeStructure pipe) { + return lossPerAmp * (pipe.material() > 6 ? 3 : 2); + } else return lossPerAmp; + } + + public long getAmperage(IPipeStructure structure) { + if (structure instanceof CableStructure cable) { + return amperageLimit * cable.material(); + } else if (structure instanceof MaterialPipeStructure pipe) { + return amperageLimit * pipe.material() / 2; + } else return amperageLimit; + } + + @Override + @Nullable + public WorldPipeNode getFromNet(ServerLevel world, BlockPos pos, IPipeStructure structure) { + if (structure instanceof CableStructure || structure instanceof MaterialPipeStructure) + return WorldEnergyNet.getWorldNet(world).getNode(pos); + else return null; + } + + @Override + public void removeFromNet(ServerLevel world, BlockPos pos, IPipeStructure structure) { + if (structure instanceof CableStructure || structure instanceof MaterialPipeStructure) { + WorldEnergyNet net = WorldEnergyNet.getWorldNet(world); + NetNode node = net.getNode(pos); + if (node != null) net.removeNode(node); + } + } + + @Override + public boolean generatesStructure(IPipeStructure structure) { + return structure instanceof CableStructure cable && (!isSuperconductor() || !cable.isInsulated()); + } + + @Override + public boolean supportsStructure(IPipeStructure structure) { + return structure instanceof CableStructure || structure instanceof MaterialPipeStructure; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/handlers/properties/MaterialFluidProperties.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/handlers/properties/MaterialFluidProperties.java new file mode 100644 index 0000000000..1ba56365ce --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/handlers/properties/MaterialFluidProperties.java @@ -0,0 +1,264 @@ +package com.gregtechceu.gtceu.common.pipelike.handlers.properties; + +import com.gregtechceu.gtceu.api.capability.IPropertyFluidFilter; +import com.gregtechceu.gtceu.api.data.chemical.material.properties.FluidProperty; +import com.gregtechceu.gtceu.api.data.chemical.material.properties.MaterialProperties; +import com.gregtechceu.gtceu.api.data.chemical.material.properties.PipeNetProperties; +import com.gregtechceu.gtceu.api.data.chemical.material.properties.PropertyKey; +import com.gregtechceu.gtceu.api.fluids.FluidBuilder; +import com.gregtechceu.gtceu.api.fluids.FluidConstants; +import com.gregtechceu.gtceu.api.fluids.FluidState; +import com.gregtechceu.gtceu.api.fluids.attribute.FluidAttribute; +import com.gregtechceu.gtceu.api.fluids.store.FluidStorageKeys; +import com.gregtechceu.gtceu.api.graphnet.logic.ChannelCountLogic; +import com.gregtechceu.gtceu.api.graphnet.logic.NetLogicData; +import com.gregtechceu.gtceu.api.graphnet.logic.ThroughputLogic; +import com.gregtechceu.gtceu.api.graphnet.logic.WeightFactorLogic; +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.logic.TemperatureLogic; +import com.gregtechceu.gtceu.api.graphnet.pipenet.logic.TemperatureLossFunction; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IPipeMaterialStructure; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IPipeStructure; +import com.gregtechceu.gtceu.common.pipelike.block.pipe.MaterialPipeStructure; +import com.gregtechceu.gtceu.common.pipelike.net.fluid.FluidContainmentLogic; +import com.gregtechceu.gtceu.common.pipelike.net.fluid.WorldFluidNet; +import com.gregtechceu.gtceu.utils.FormattingUtil; + +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.material.Fluid; + +import it.unimi.dsi.fastutil.objects.Object2BooleanMap; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import lombok.Getter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.EnumSet; +import java.util.List; +import java.util.Set; + +public final class MaterialFluidProperties implements PipeNetProperties.IPipeNetMaterialProperty, IPropertyFluidFilter { + + public static final MaterialPropertyKey KEY = new MaterialPropertyKey<>("FluidProperties"); + + @Getter + private final Set containedAttributes = new ObjectOpenHashSet<>(); + private final EnumSet containableStates = EnumSet.of(FluidState.LIQUID); + + @Getter + private final int maxFluidTemperature; + private final int minFluidTemperature; + private int materialMeltTemperature; + + private final long baseThroughput; + private final float priority; + + public MaterialFluidProperties(long baseThroughput, int maxFluidTemperature, int minFluidTemperature, + float priority) { + this.baseThroughput = baseThroughput; + this.maxFluidTemperature = maxFluidTemperature; + this.minFluidTemperature = minFluidTemperature; + this.priority = priority; + } + + public MaterialFluidProperties(long baseThroughput, int maxFluidTemperature, int minFluidTemperature) { + this(baseThroughput, maxFluidTemperature, minFluidTemperature, 2048f / baseThroughput); + } + + public static MaterialFluidProperties createMax(long baseThroughput, int maxFluidTemperature) { + return createMax(baseThroughput, maxFluidTemperature, 2048f / baseThroughput); + } + + public static MaterialFluidProperties createMax(long baseThroughput, int maxFluidTemperature, float priority) { + return new MaterialFluidProperties(baseThroughput, maxFluidTemperature, + FluidConstants.CRYOGENIC_FLUID_THRESHOLD + 1, priority); + } + + public static MaterialFluidProperties createMin(long baseThroughput, int minFluidTemperature) { + return createMin(baseThroughput, minFluidTemperature, 2048f / baseThroughput); + } + + public static MaterialFluidProperties createMin(long baseThroughput, int minFluidTemperature, float priority) { + return new MaterialFluidProperties(baseThroughput, 0, minFluidTemperature, priority); + } + + public static MaterialFluidProperties create(long baseThroughput) { + return create(baseThroughput, 2048f / baseThroughput); + } + + public static MaterialFluidProperties create(long baseThroughput, float priority) { + return new MaterialFluidProperties(baseThroughput, 0, 0, priority); + } + + public MaterialFluidProperties setContain(FluidState state, boolean canContain) { + if (canContain) contain(state); + else notContain(state); + return this; + } + + public MaterialFluidProperties setContain(FluidAttribute attribute, boolean canContain) { + if (canContain) contain(attribute); + else notContain(attribute); + return this; + } + + public MaterialFluidProperties contain(FluidState state) { + this.containableStates.add(state); + return this; + } + + public MaterialFluidProperties contain(FluidAttribute attribute) { + this.containedAttributes.add(attribute); + return this; + } + + public MaterialFluidProperties notContain(FluidState state) { + this.containableStates.remove(state); + return this; + } + + public MaterialFluidProperties notContain(FluidAttribute attribute) { + this.containedAttributes.remove(attribute); + return this; + } + + public boolean canContain(@NotNull FluidState state) { + return this.containableStates.contains(state); + } + + public boolean canContain(@NotNull FluidAttribute attribute) { + return this.containedAttributes.contains(attribute); + } + + @Override + public void setCanContain(@NotNull FluidAttribute attribute, boolean canContain) { + setContain(attribute, canContain); + } + + @Override + public void setCanContain(@NotNull Object2BooleanMap attributes) { + for (var entry : attributes.object2BooleanEntrySet()) { + if (entry.getBooleanValue()) { + contain(entry.getKey()); + } else { + notContain(entry.getKey()); + } + } + } + + public int getMinFluidTemperature() { + return minFluidTemperature; + } + + @Override + public MaterialPropertyKey getKey() { + return KEY; + } + + @Override + public void addInformation(@NotNull ItemStack stack, BlockGetter worldIn, @NotNull List tooltip, + @NotNull TooltipFlag flagIn, IPipeMaterialStructure structure) { + tooltip.add(Component.translatable("gtceu.pipe.fluid_pipe")); + tooltip.add(Component.translatable("gtceu.universal.tooltip.fluid_transfer_rate", getThroughput(structure))); + tooltip.add(Component.translatable("gtceu.pipe.priority", + FormattingUtil.formatNumbers(getFlowPriority(structure)))); + appendTooltips(tooltip); + } + + @Override + public void verifyProperty(MaterialProperties properties) { + if (!properties.hasProperty(PropertyKey.WOOD)) { + properties.ensureSet(PropertyKey.INGOT, true); + } + this.materialMeltTemperature = computeMaterialMeltTemperature(properties, maxFluidTemperature); + } + + public static int computeMaterialMeltTemperature(@NotNull MaterialProperties properties, int fallback) { + if (properties.hasProperty(PropertyKey.FLUID)) { + // autodetermine melt temperature from registered fluid + FluidProperty prop = properties.getProperty(PropertyKey.FLUID); + Fluid fluid = prop.get(FluidStorageKeys.LIQUID); + if (fluid == null) { + FluidBuilder builder = prop.getQueuedBuilder(FluidStorageKeys.LIQUID); + if (builder != null) { + return builder.getDeterminedTemperature(properties.getMaterial(), FluidStorageKeys.LIQUID); + } + } else { + return fluid.getFluidType().getTemperature(); + } + } + return fallback; + } + + @Override + @Nullable + public WorldPipeNode getOrCreateFromNet(ServerLevel world, BlockPos pos, IPipeStructure structure) { + if (structure instanceof MaterialPipeStructure) { + WorldPipeNode node = WorldFluidNet.getWorldNet(world).getOrCreateNode(pos); + mutateData(node.getData(), structure); + return node; + } + return null; + } + + @Override + public void mutateData(NetLogicData data, IPipeStructure structure) { + if (structure instanceof MaterialPipeStructure pipe) { + long throughput = getThroughput(structure); + float coolingFactor = (float) Math.sqrt((double) pipe.material() / (4 + pipe.channelCount())); + data.setLogicEntry(WeightFactorLogic.TYPE.getWith(getFlowPriority(structure))) + .setLogicEntry(ThroughputLogic.TYPE.getWith(throughput)) + .setLogicEntry(FluidContainmentLogic.TYPE.getWith(containableStates, containedAttributes, + maxFluidTemperature)) + .setLogicEntry(TemperatureLogic.TYPE.getWith(TemperatureLossFunction.getOrCreatePipe(coolingFactor), + materialMeltTemperature, minFluidTemperature, 50 * pipe.material(), null)); + if (pipe.channelCount() > 1) { + data.setLogicEntry(ChannelCountLogic.TYPE.getWith(pipe.channelCount())); + } + } + } + + private long getThroughput(IPipeStructure structure) { + if (structure instanceof MaterialPipeStructure pipe) { + return baseThroughput * pipe.material(); + } else return baseThroughput; + } + + private double getFlowPriority(IPipeStructure structure) { + if (structure instanceof MaterialPipeStructure pipe) { + return priority * (pipe.restrictive() ? 100d : 1d) * pipe.channelCount() / pipe.material(); + } else return priority; + } + + @Override + public @Nullable WorldPipeNode getFromNet(ServerLevel world, BlockPos pos, IPipeStructure structure) { + if (structure instanceof MaterialPipeStructure) + return WorldFluidNet.getWorldNet(world).getNode(pos); + else return null; + } + + @Override + public void removeFromNet(ServerLevel world, BlockPos pos, IPipeStructure structure) { + if (structure instanceof MaterialPipeStructure) { + WorldFluidNet net = WorldFluidNet.getWorldNet(world); + NetNode node = net.getNode(pos); + if (node != null) net.removeNode(node); + } + } + + @Override + public boolean generatesStructure(IPipeStructure structure) { + return structure.getClass() == MaterialPipeStructure.class; + } + + @Override + public boolean supportsStructure(IPipeStructure structure) { + return structure instanceof MaterialPipeStructure; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/handlers/properties/MaterialItemProperties.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/handlers/properties/MaterialItemProperties.java new file mode 100644 index 0000000000..2f94faf65c --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/handlers/properties/MaterialItemProperties.java @@ -0,0 +1,132 @@ +package com.gregtechceu.gtceu.common.pipelike.handlers.properties; + +import com.gregtechceu.gtceu.api.data.chemical.material.properties.MaterialProperties; +import com.gregtechceu.gtceu.api.data.chemical.material.properties.PipeNetProperties; +import com.gregtechceu.gtceu.api.data.chemical.material.properties.PropertyKey; +import com.gregtechceu.gtceu.api.graphnet.logic.ChannelCountLogic; +import com.gregtechceu.gtceu.api.graphnet.logic.NetLogicData; +import com.gregtechceu.gtceu.api.graphnet.logic.ThroughputLogic; +import com.gregtechceu.gtceu.api.graphnet.logic.WeightFactorLogic; +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IPipeMaterialStructure; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IPipeStructure; +import com.gregtechceu.gtceu.common.pipelike.block.pipe.MaterialPipeStructure; +import com.gregtechceu.gtceu.common.pipelike.net.item.WorldItemNet; +import com.gregtechceu.gtceu.utils.FormattingUtil; + +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.BlockGetter; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public final class MaterialItemProperties implements PipeNetProperties.IPipeNetMaterialProperty { + + public static final MaterialPropertyKey KEY = new MaterialPropertyKey<>("ItemProperties"); + + private final long baseItemsPer5Ticks; + private final float priority; + + public MaterialItemProperties(long baseItemsPer5Ticks, float priority) { + this.baseItemsPer5Ticks = baseItemsPer5Ticks; + this.priority = priority; + } + + public static MaterialItemProperties create(long baseThroughput) { + return new MaterialItemProperties(baseThroughput, 2048f / baseThroughput); + } + + @Override + public MaterialPropertyKey getKey() { + return KEY; + } + + @Override + public void addInformation(@NotNull ItemStack stack, BlockGetter worldIn, @NotNull List tooltip, + @NotNull TooltipFlag flagIn, IPipeMaterialStructure structure) { + tooltip.add(Component.translatable("gtceu.pipe.item_pipe")); + long items = getThroughput(structure); + if (items % 16 != 0) { + tooltip.add(Component.translatable("gtceu.universal.tooltip.item_transfer_rate", items * 4)); + } else { + tooltip.add(Component.translatable("gtceu.universal.tooltip.item_transfer_rate_stacks", items / 16)); + } + tooltip.add(Component.translatable("gtceu.pipe.priority", + FormattingUtil.formatNumbers(getFlowPriority(structure)))); + } + + private long getThroughput(IPipeStructure structure) { + if (structure instanceof MaterialPipeStructure pipe) { + return baseItemsPer5Ticks * pipe.material(); + } else return baseItemsPer5Ticks; + } + + @Override + public void verifyProperty(MaterialProperties properties) { + if (!properties.hasProperty(PropertyKey.WOOD)) { + properties.ensureSet(PropertyKey.INGOT, true); + } + } + + @Override + @Nullable + public WorldPipeNode getOrCreateFromNet(ServerLevel world, BlockPos pos, IPipeStructure structure) { + if (structure instanceof MaterialPipeStructure) { + WorldPipeNode node = WorldItemNet.getWorldNet(world).getOrCreateNode(pos); + mutateData(node.getData(), structure); + return node; + } + return null; + } + + @Override + public void mutateData(NetLogicData data, IPipeStructure structure) { + if (structure instanceof MaterialPipeStructure pipe) { + long throughput = baseItemsPer5Ticks * pipe.material(); + data.setLogicEntry(WeightFactorLogic.TYPE.getWith(getFlowPriority(structure))) + .setLogicEntry(ThroughputLogic.TYPE.getWith(throughput)); + if (pipe.channelCount() > 1) { + data.setLogicEntry(ChannelCountLogic.TYPE.getWith(pipe.channelCount())); + } + } + } + + private double getFlowPriority(IPipeStructure structure) { + if (structure instanceof MaterialPipeStructure pipe) { + return priority * (pipe.restrictive() ? 100d : 1d) * pipe.channelCount() / pipe.material(); + } else return priority; + } + + @Override + public @Nullable WorldPipeNode getFromNet(ServerLevel world, BlockPos pos, IPipeStructure structure) { + if (structure instanceof MaterialPipeStructure) + return WorldItemNet.getWorldNet(world).getNode(pos); + else return null; + } + + @Override + public void removeFromNet(ServerLevel world, BlockPos pos, IPipeStructure structure) { + if (structure instanceof MaterialPipeStructure) { + WorldItemNet net = WorldItemNet.getWorldNet(world); + NetNode node = net.getNode(pos); + if (node != null) net.removeNode(node); + } + } + + @Override + public boolean generatesStructure(IPipeStructure structure) { + return structure.getClass() == MaterialPipeStructure.class; + } + + @Override + public boolean supportsStructure(IPipeStructure structure) { + return structure instanceof MaterialPipeStructure; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/item/ItemNetHandler.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/item/ItemNetHandler.java deleted file mode 100644 index 3ce3567ee8..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/item/ItemNetHandler.java +++ /dev/null @@ -1,499 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.item; - -import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; -import com.gregtechceu.gtceu.api.capability.ICoverable; -import com.gregtechceu.gtceu.api.capability.recipe.IO; -import com.gregtechceu.gtceu.api.cover.CoverBehavior; -import com.gregtechceu.gtceu.common.blockentity.ItemPipeBlockEntity; -import com.gregtechceu.gtceu.common.cover.ConveyorCover; -import com.gregtechceu.gtceu.common.cover.ItemFilterCover; -import com.gregtechceu.gtceu.common.cover.RobotArmCover; -import com.gregtechceu.gtceu.common.cover.data.DistributionMode; -import com.gregtechceu.gtceu.common.cover.data.FilterMode; -import com.gregtechceu.gtceu.utils.FacingPos; -import com.gregtechceu.gtceu.utils.ItemStackHashStrategy; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraftforge.items.IItemHandler; -import net.minecraftforge.items.IItemHandlerModifiable; -import net.minecraftforge.items.ItemHandlerHelper; -import net.minecraftforge.items.ItemStackHandler; - -import it.unimi.dsi.fastutil.ints.IntArrayList; -import it.unimi.dsi.fastutil.ints.IntList; -import lombok.Getter; -import org.jetbrains.annotations.NotNull; - -import java.util.*; - -public class ItemNetHandler implements IItemHandlerModifiable { - - @Getter - private ItemPipeNet net; - private ItemPipeBlockEntity pipe; - private final Level world; - @Getter - private final Direction facing; - private final Map simulatedTransfersGlobalRoundRobin = new HashMap<>(); - private int simulatedTransfers = 0; - - private final ItemStackHandler testHandler = new ItemStackHandler(1); - - public ItemNetHandler(ItemPipeNet net, ItemPipeBlockEntity pipe, Direction facing) { - this.net = net; - this.pipe = pipe; - this.facing = facing; - this.world = pipe.getPipeLevel(); - } - - private long getLevelTime() { - return net.getLevel().getGameTime(); - } - - public void updateNetwork(ItemPipeNet net) { - this.net = net; - } - - public void updatePipe(ItemPipeBlockEntity pipe) { - this.pipe = pipe; - } - - private void copyTransferred() { - simulatedTransfers = pipe.getTransferredItems(); - simulatedTransfersGlobalRoundRobin.clear(); - simulatedTransfersGlobalRoundRobin.putAll(pipe.getTransferred()); - } - - @NotNull - @Override - public ItemStack insertItem(int slot, @NotNull ItemStack stack, boolean simulate) { - if (stack.isEmpty()) return stack; - - if (net == null || pipe == null || pipe.isInValid() || pipe.isBlocked(facing)) { - return stack; - } - - copyTransferred(); - CoverBehavior pipeCover = pipe.getCoverContainer().getCoverAtSide(facing); - CoverBehavior tileCover = getCoverOnNeighbour(pipe.getPipePos(), facing); - - boolean pipeConveyor = pipeCover instanceof ConveyorCover, tileConveyor = tileCover instanceof ConveyorCover; - // abort if there are two conveyors - if (pipeConveyor && tileConveyor) return stack; - - if (tileCover != null && !checkImportCover(tileCover, false, stack)) - return stack; - - if (!pipeConveyor && !tileConveyor) - return insertFirst(stack, simulate); - - ConveyorCover conveyor = (ConveyorCover) (pipeConveyor ? pipeCover : tileCover); - if (conveyor.getIo() == (pipeConveyor ? IO.IN : IO.OUT)) { - boolean roundRobinGlobal = conveyor.getDistributionMode() == DistributionMode.ROUND_ROBIN_GLOBAL; - if (roundRobinGlobal || conveyor.getDistributionMode() == DistributionMode.ROUND_ROBIN_PRIO) - return insertRoundRobin(stack, simulate, roundRobinGlobal); - } - - return insertFirst(stack, simulate); - } - - public static boolean checkImportCover(CoverBehavior cover, boolean onPipe, ItemStack stack) { - if (cover == null) return true; - if (cover instanceof ItemFilterCover filter) { - return (filter.getFilterMode() != FilterMode.FILTER_BOTH && - (filter.getFilterMode() != FilterMode.FILTER_INSERT || !onPipe) && - (filter.getFilterMode() != FilterMode.FILTER_EXTRACT || onPipe)) || - filter.getItemFilter().test(stack); - } - return true; - } - - public ItemStack insertFirst(ItemStack stack, boolean simulate) { - for (ItemRoutePath inv : net.getNetData(pipe.getPipePos(), facing)) { - stack = insert(inv, stack, simulate); - if (stack.isEmpty()) - return ItemStack.EMPTY; - } - return stack; - } - - public ItemStack insertRoundRobin(ItemStack stack, boolean simulate, boolean global) { - List routePaths = net.getNetData(pipe.getPipePos(), facing); - if (routePaths.isEmpty()) - return stack; - if (routePaths.size() == 1) - return insert(routePaths.get(0), stack, simulate); - List routePathsCopy = new ArrayList<>(routePaths); - - if (global) { - stack = insertToHandlersEnhanced(routePathsCopy, stack, routePaths.size(), simulate); - } else { - stack = insertToHandlers(routePathsCopy, stack, simulate); - if (!stack.isEmpty() && !routePathsCopy.isEmpty()) - stack = insertToHandlers(routePathsCopy, stack, simulate); - } - - return stack; - } - - /** - * Inserts items equally to all handlers - * if it couldn't insert all items, the handler will be removed - * - * @param copy to insert to - * @param stack to insert - * @param simulate simulate - * @return remainder - */ - private ItemStack insertToHandlers(List copy, ItemStack stack, boolean simulate) { - Iterator routePathIterator = copy.listIterator(); - int inserted = 0; - int count = stack.getCount(); - int c = count / copy.size(); - int m = c == 0 ? count % copy.size() : 0; - while (routePathIterator.hasNext()) { - ItemRoutePath routePath = routePathIterator.next(); - - int amount = c; - if (m > 0) { - amount++; - m--; - } - amount = Math.min(amount, stack.getCount() - inserted); - if (amount == 0) break; - ItemStack toInsert = stack.copy(); - toInsert.setCount(amount); - int r = insert(routePath, toInsert, simulate).getCount(); - if (r < amount) { - inserted += (amount - r); - } - if (r == 1 && c == 0 && amount == 1) { - m++; - } - - if (r > 0) - routePathIterator.remove(); - } - - ItemStack remainder = stack.copy(); - remainder.setCount(count - inserted); - return remainder; - } - - private ItemStack insertToHandlersEnhanced(List copy, ItemStack stack, int dest, boolean simulate) { - List transferred = new ArrayList<>(); - IntList steps = new IntArrayList(); - int min = Integer.MAX_VALUE; - ItemStack simStack; - - // find inventories that are not full and get the amount that was inserted in total - for (ItemRoutePath inv : copy) { - simStack = stack.copy(); - int ins = stack.getCount() - insert(inv, simStack, true, true).getCount(); - if (ins <= 0) - continue; - int didTransfer = didTransferTo(inv, simulate); - EnhancedRoundRobinData data = new EnhancedRoundRobinData(inv, ins, didTransfer); - transferred.add(data); - - min = Math.min(min, didTransfer); - - if (!steps.contains(didTransfer)) { - steps.add(didTransfer); - } - } - - if (transferred.isEmpty() || steps.isEmpty()) - return stack; - - if (!simulate && min < Integer.MAX_VALUE) { - decrementBy(min); - } - - transferred.sort(Comparator.comparingInt(data -> data.transferred)); - steps.sort(Integer::compare); - - if (transferred.get(0).transferred != steps.getInt(0)) { - return stack; - } - - int amount = stack.getCount(); - int c = amount / transferred.size(); - int m = amount % transferred.size(); - List transferredCopy = new ArrayList<>(transferred); - int nextStep = steps.removeInt(0); - - // equally distribute items over all inventories - // it takes into account how much was inserted in total - // f.e. if inv1 has 2 inserted and inv2 has 6 inserted, it will first try to insert 4 into inv1 so that both - // have 6 and then it will distribute the rest equally - outer: - while (amount > 0 && !transferredCopy.isEmpty()) { - Iterator iterator = transferredCopy.iterator(); - int i = 0; - while (iterator.hasNext()) { - EnhancedRoundRobinData data = iterator.next(); - if (nextStep >= 0 && data.transferred >= nextStep) - break; - - int toInsert; - if (nextStep <= 0) { - if (amount <= m) { - // break outer; - toInsert = 1; - } else { - toInsert = Math.min(c, amount); - } - } else { - toInsert = Math.min(amount, nextStep - data.transferred); - } - if (data.toTransfer + toInsert >= data.maxInsertable) { - data.toTransfer = data.maxInsertable; - iterator.remove(); - } else { - data.toTransfer += toInsert; - } - - data.transferred += toInsert; - - if ((amount -= toInsert) == 0) { - break outer; - } - i++; - } - - for (EnhancedRoundRobinData data : transferredCopy) { - if (data.transferred < nextStep) - continue outer; - } - if (steps.isEmpty()) { - if (nextStep >= 0) { - c = amount / transferredCopy.size(); - m = amount % transferredCopy.size(); - nextStep = -1; - } - } else { - nextStep = steps.removeInt(0); - } - } - - int inserted = 0; - - // finally actually insert the item - for (EnhancedRoundRobinData data : transferred) { - ItemStack toInsert = stack.copy(); - toInsert.setCount(data.toTransfer); - int ins = data.toTransfer - insert(data.routePath, toInsert, simulate).getCount(); - inserted += ins; - transferTo(data.routePath, simulate, ins); - } - - ItemStack remainder = stack.copy(); - remainder.shrink(inserted); - return remainder; - } - - public ItemStack insert(ItemRoutePath handler, ItemStack stack, boolean simulate) { - return insert(handler, stack, simulate, false); - } - - public ItemStack insert(ItemRoutePath routePath, ItemStack stack, boolean simulate, boolean ignoreLimit) { - int allowed = ignoreLimit ? stack.getCount() : - checkTransferable(routePath.getProperties().getTransferRate(), stack.getCount(), simulate); - if (allowed == 0 || !routePath.matchesFilters(stack)) { - return stack; - } - CoverBehavior pipeCover = routePath.getTargetPipe().getCoverContainer() - .getCoverAtSide(routePath.getTargetFacing()); - CoverBehavior tileCover = getCoverOnNeighbour(routePath.getTargetPipe().getPipePos(), - routePath.getTargetFacing()); - - if (pipeCover != null) { - testHandler.setStackInSlot(0, stack.copy()); - IItemHandlerModifiable itemHandler = pipeCover.getItemHandlerCap(testHandler); - if (itemHandler == null || (itemHandler != testHandler && - (allowed = itemHandler.extractItem(0, allowed, true).getCount()) <= 0)) { - testHandler.setStackInSlot(0, ItemStack.EMPTY); - return stack; - } - testHandler.setStackInSlot(0, ItemStack.EMPTY); - } - IItemHandler neighbourHandler = routePath.getHandler(net.getLevel()); - if (pipeCover instanceof RobotArmCover robotArm && robotArm.getIo() == IO.OUT) { - return insertOverRobotArm(neighbourHandler, robotArm, stack, simulate, allowed, ignoreLimit); - } - if (tileCover instanceof RobotArmCover robotArm && robotArm.getIo() == IO.IN) { - return insertOverRobotArm(neighbourHandler, robotArm, stack, simulate, allowed, ignoreLimit); - } - - return insert(neighbourHandler, stack, simulate, allowed, ignoreLimit); - } - - private ItemStack insert(IItemHandler handler, ItemStack stack, boolean simulate, int allowed, - boolean ignoreLimit) { - if (stack.getCount() == allowed) { - ItemStack re = ItemHandlerHelper.insertItemStacked(handler, stack, simulate); - if (!ignoreLimit) - transfer(simulate, stack.getCount() - re.getCount()); - return re; - } - ItemStack toInsert = stack.copy(); - toInsert.setCount(Math.min(allowed, stack.getCount())); - int r = ItemHandlerHelper.insertItemStacked(handler, toInsert, simulate).getCount(); - if (!ignoreLimit) - transfer(simulate, toInsert.getCount() - r); - ItemStack remainder = stack.copy(); - remainder.setCount(r + (stack.getCount() - toInsert.getCount())); - return remainder; - } - - public CoverBehavior getCoverOnNeighbour(BlockPos pos, Direction handlerFacing) { - BlockEntity tile = pipe.getLevel().getBlockEntity(pos.relative(handlerFacing)); - if (tile != null) { - ICoverable coverable = GTCapabilityHelper.getCoverable(pipe.getLevel(), pos.relative(handlerFacing), - handlerFacing.getOpposite()); - if (coverable == null) return null; - return coverable.getCoverAtSide(handlerFacing.getOpposite()); - } - return null; - } - - public ItemStack insertOverRobotArm(IItemHandler handler, RobotArmCover arm, ItemStack stack, boolean simulate, - int allowed, boolean ignoreLimit) { - int rate; - boolean isStackSpecific = false; - rate = arm.getFilterHandler().getFilter().testItemCount(stack); - int count; - switch (arm.getTransferMode()) { - case TRANSFER_ANY: - return insert(handler, stack, simulate, allowed, ignoreLimit); - case KEEP_EXACT: - count = rate - countStack(handler, stack, arm, isStackSpecific); - if (count <= 0) return stack; - count = Math.min(allowed, Math.min(stack.getCount(), count)); - return insert(handler, stack, simulate, count, ignoreLimit); - case TRANSFER_EXACT: - int max = allowed + arm.getBuffer(); - count = Math.min(max, Math.min(rate, stack.getCount())); - if (count < rate) { - arm.buffer(allowed); - return stack; - } else { - arm.clearBuffer(); - } - if (insert(handler, stack, true, count, ignoreLimit).getCount() != stack.getCount() - count) { - return stack; - } - return insert(handler, stack, simulate, count, ignoreLimit); - } - return stack; - } - - public static int countStack(IItemHandler handler, ItemStack stack, RobotArmCover arm, boolean isStackSpecific) { - if (arm == null) return 0; - int count = 0; - for (int i = 0; i < handler.getSlots(); i++) { - ItemStack slot = handler.getStackInSlot(i); - if (slot.isEmpty()) continue; - if (isStackSpecific ? ItemStackHashStrategy.comparingAllButCount().equals(stack, slot) : - arm.getFilterHandler().getFilter().test(slot)) { - count += slot.getCount(); - } - } - return count; - } - - private int checkTransferable(float rate, int amount, boolean simulate) { - int max = (int) ((rate * 64) + 0.5); - if (simulate) - return Math.max(0, Math.min(max - simulatedTransfers, amount)); - else - return Math.max(0, Math.min(max - pipe.getTransferredItems(), amount)); - } - - private void transfer(boolean simulate, int amount) { - if (simulate) - simulatedTransfers += amount; - else - pipe.addTransferredItems(amount); - } - - @Override - public int getSlots() { - return 1; - } - - @NotNull - @Override - public ItemStack getStackInSlot(int i) { - return ItemStack.EMPTY; - } - - @NotNull - @Override - public ItemStack extractItem(int slot, int amount, boolean simulate) { - return ItemStack.EMPTY; - } - - @Override - public int getSlotLimit(int i) { - return 64; - } - - @Override - public boolean isItemValid(int slot, @NotNull ItemStack stack) { - return true; - } - - private void transferTo(ItemRoutePath handler, boolean simulate, int amount) { - if (simulate) - simulatedTransfersGlobalRoundRobin.merge(handler.toFacingPos(), amount, Integer::sum); - else - pipe.getTransferred().merge(handler.toFacingPos(), amount, Integer::sum); - } - - private boolean contains(ItemRoutePath handler, boolean simulate) { - return simulate ? simulatedTransfersGlobalRoundRobin.containsKey(handler.toFacingPos()) : - pipe.getTransferred().containsKey(handler.toFacingPos()); - } - - private int didTransferTo(ItemRoutePath handler, boolean simulate) { - if (simulate) - return simulatedTransfersGlobalRoundRobin.getOrDefault(handler.toFacingPos(), 0); - return pipe.getTransferred().getOrDefault(handler.toFacingPos(), 0); - } - - private void resetTransferred(boolean simulated) { - if (simulated) - simulatedTransfersGlobalRoundRobin.clear(); - else - pipe.resetTransferred(); - } - - private void decrementBy(int amount) { - for (Map.Entry entry : pipe.getTransferred().entrySet()) { - entry.setValue(entry.getValue() - amount); - } - } - - @Override - public void setStackInSlot(int slot, @NotNull ItemStack stack) {} - - private static class EnhancedRoundRobinData { - - private final ItemRoutePath routePath; - private final int maxInsertable; - private int transferred; - private int toTransfer = 0; - - private EnhancedRoundRobinData(ItemRoutePath routePath, int maxInsertable, int transferred) { - this.maxInsertable = maxInsertable; - this.transferred = transferred; - this.routePath = routePath; - } - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/item/ItemNetWalker.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/item/ItemNetWalker.java deleted file mode 100644 index 8275b1d8a0..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/item/ItemNetWalker.java +++ /dev/null @@ -1,140 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.item; - -import com.gregtechceu.gtceu.GTCEu; -import com.gregtechceu.gtceu.api.cover.CoverBehavior; -import com.gregtechceu.gtceu.api.data.chemical.material.properties.ItemPipeProperties; -import com.gregtechceu.gtceu.api.pipenet.PipeNetWalker; -import com.gregtechceu.gtceu.common.blockentity.ItemPipeBlockEntity; -import com.gregtechceu.gtceu.common.cover.ItemFilterCover; -import com.gregtechceu.gtceu.common.cover.ShutterCover; -import com.gregtechceu.gtceu.common.cover.data.FilterMode; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraftforge.common.capabilities.ForgeCapabilities; -import net.minecraftforge.common.util.LazyOptional; -import net.minecraftforge.items.IItemHandler; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.EnumMap; -import java.util.List; -import java.util.function.Predicate; - -public class ItemNetWalker extends PipeNetWalker { - - public static List createNetData(ItemPipeNet pipeNet, BlockPos sourcePipe, Direction sourceFacing) { - if (!(pipeNet.getLevel().getBlockEntity(sourcePipe) instanceof ItemPipeBlockEntity)) { - return null; - } - try { - ItemNetWalker walker = new ItemNetWalker(pipeNet, sourcePipe, 1, new ArrayList<>(), null); - walker.sourcePipe = sourcePipe; - walker.facingToHandler = sourceFacing; - walker.traversePipeNet(); - return walker.inventories; - } catch (Exception e) { - GTCEu.LOGGER.error("error while create net data for ItemPipeNet", e); - } - return null; - } - - private ItemPipeProperties minProperties; - private final List inventories; - private final List> filters = new ArrayList<>(); - private final EnumMap>> nextFilters = new EnumMap<>(Direction.class); - private BlockPos sourcePipe; - private Direction facingToHandler; - - protected ItemNetWalker(ItemPipeNet world, BlockPos sourcePipe, int distance, List inventories, - ItemPipeProperties properties) { - super(world, sourcePipe, distance); - this.inventories = inventories; - this.minProperties = properties; - } - - @NotNull - @Override - protected PipeNetWalker createSubWalker(ItemPipeNet pipeNet, - Direction facingToNextPos, - BlockPos nextPos, - int walkedBlocks) { - ItemNetWalker walker = new ItemNetWalker(pipeNet, nextPos, walkedBlocks, inventories, minProperties); - walker.facingToHandler = facingToHandler; - walker.sourcePipe = sourcePipe; - walker.filters.addAll(filters); - List> moreFilters = nextFilters.get(facingToNextPos); - if (moreFilters != null && !moreFilters.isEmpty()) { - walker.filters.addAll(moreFilters); - } - return walker; - } - - @Override - protected Class getBasePipeClass() { - return ItemPipeBlockEntity.class; - } - - @Override - protected void checkPipe(ItemPipeBlockEntity pipeTile, BlockPos pos) { - for (List> filters : nextFilters.values()) { - if (!filters.isEmpty()) { - this.filters.addAll(filters); - } - } - nextFilters.clear(); - ItemPipeProperties pipeProperties = pipeTile.getNodeData(); - if (minProperties == null) { - minProperties = pipeProperties; - } else { - minProperties = new ItemPipeProperties(minProperties.getPriority() + pipeProperties.getPriority(), - Math.min(minProperties.getTransferRate(), pipeProperties.getTransferRate())); - } - } - - @Override - protected void checkNeighbour(ItemPipeBlockEntity pipeTile, BlockPos pipePos, Direction faceToNeighbour, - @Nullable BlockEntity neighbourTile) { - if (neighbourTile == null || (pipePos.equals(sourcePipe) && faceToNeighbour == facingToHandler)) { - return; - } - LazyOptional handler = neighbourTile.getCapability(ForgeCapabilities.ITEM_HANDLER, - faceToNeighbour.getOpposite()); - if (handler.isPresent()) { - List> filters = new ArrayList<>(this.filters); - List> moreFilters = nextFilters.get(faceToNeighbour); - if (moreFilters != null && !moreFilters.isEmpty()) { - filters.addAll(moreFilters); - } - inventories.add(new ItemRoutePath(pipeTile, faceToNeighbour, getWalkedBlocks(), minProperties, filters)); - } - } - - @Override - protected boolean isValidPipe(ItemPipeBlockEntity currentPipe, ItemPipeBlockEntity neighbourPipe, BlockPos pipePos, - Direction faceToNeighbour) { - CoverBehavior thisCover = currentPipe.getCoverContainer().getCoverAtSide(faceToNeighbour); - CoverBehavior neighbourCover = neighbourPipe.getCoverContainer().getCoverAtSide(faceToNeighbour.getOpposite()); - List> filters = new ArrayList<>(); - if (thisCover instanceof ShutterCover shutter) { - filters.add(stack -> !shutter.isWorkingEnabled()); - } else if (thisCover instanceof ItemFilterCover itemFilterCover && - itemFilterCover.getFilterMode() != FilterMode.FILTER_INSERT) { - filters.add(itemFilterCover.getItemFilter()); - } - if (neighbourCover instanceof ShutterCover shutter) { - filters.add(stack -> !shutter.isWorkingEnabled()); - } else if (neighbourCover instanceof ItemFilterCover itemFilterCover && - itemFilterCover.getFilterMode() != FilterMode.FILTER_EXTRACT) { - filters.add(itemFilterCover.getItemFilter()); - } - if (!filters.isEmpty()) { - nextFilters.put(faceToNeighbour, filters); - } - return true; - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/item/ItemPipeData.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/item/ItemPipeData.java deleted file mode 100644 index 97f9e0a8e7..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/item/ItemPipeData.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.item; - -import com.gregtechceu.gtceu.api.data.chemical.material.properties.ItemPipeProperties; -import com.gregtechceu.gtceu.api.pipenet.IAttachData; - -import net.minecraft.core.Direction; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.experimental.Accessors; - -import java.util.Objects; - -@Accessors(fluent = true) -@AllArgsConstructor -public class ItemPipeData implements IAttachData { - - @Getter - ItemPipeProperties properties; - @Getter - byte connections; - - @Override - public boolean canAttachTo(Direction side) { - return (connections & (1 << side.ordinal())) != 0; - } - - @Override - public boolean setAttached(Direction side, boolean attach) { - var result = canAttachTo(side); - if (result != attach) { - if (attach) { - connections |= (1 << side.ordinal()); - } else { - connections &= ~(1 << side.ordinal()); - } - } - return result != attach; - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof ItemPipeData cableData) { - return cableData.properties.equals(properties) && connections == cableData.connections; - } - return super.equals(obj); - } - - @Override - public int hashCode() { - return Objects.hash(properties, connections); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/item/ItemPipeNet.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/item/ItemPipeNet.java deleted file mode 100644 index ef5f1d9206..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/item/ItemPipeNet.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.item; - -import com.gregtechceu.gtceu.api.data.chemical.material.properties.ItemPipeProperties; -import com.gregtechceu.gtceu.api.pipenet.LevelPipeNet; -import com.gregtechceu.gtceu.api.pipenet.Node; -import com.gregtechceu.gtceu.api.pipenet.PipeNet; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.nbt.CompoundTag; - -import java.util.*; - -public class ItemPipeNet extends PipeNet { - - private final Map> NET_DATA = new HashMap<>(); - - public ItemPipeNet(LevelPipeNet> world) { - super(world); - } - - public List getNetData(BlockPos pipePos, Direction facing) { - List data = NET_DATA.get(pipePos); - if (data == null) { - data = ItemNetWalker.createNetData(this, pipePos, facing); - if (data == null) { - // walker failed, don't cache so it tries again on next insertion - return Collections.emptyList(); - } - data.sort(Comparator.comparingInt(inv -> inv.getProperties().getPriority())); - NET_DATA.put(pipePos, data); - } - return data; - } - - @Override - public void onNeighbourUpdate(BlockPos fromPos) { - NET_DATA.clear(); - } - - @Override - public void onPipeConnectionsUpdate() { - NET_DATA.clear(); - } - - @Override - protected void transferNodeData(Map> transferredNodes, - PipeNet parentNet) { - super.transferNodeData(transferredNodes, parentNet); - NET_DATA.clear(); - ((ItemPipeNet) parentNet).NET_DATA.clear(); - } - - @Override - protected void writeNodeData(ItemPipeProperties nodeData, CompoundTag tagCompound) { - tagCompound.putInt("Resistance", nodeData.getPriority()); - tagCompound.putFloat("Rate", nodeData.getTransferRate()); - } - - @Override - protected ItemPipeProperties readNodeData(CompoundTag tagCompound) { - return new ItemPipeProperties(tagCompound.getInt("Resistance"), tagCompound.getFloat("Rate")); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/item/ItemPipeType.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/item/ItemPipeType.java deleted file mode 100644 index e37dd7286a..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/item/ItemPipeType.java +++ /dev/null @@ -1,97 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.item; - -import com.gregtechceu.gtceu.GTCEu; -import com.gregtechceu.gtceu.api.data.chemical.material.Material; -import com.gregtechceu.gtceu.api.data.chemical.material.properties.ItemPipeProperties; -import com.gregtechceu.gtceu.api.data.chemical.material.properties.PropertyKey; -import com.gregtechceu.gtceu.api.data.tag.TagPrefix; -import com.gregtechceu.gtceu.api.pipenet.IMaterialPipeType; -import com.gregtechceu.gtceu.client.model.PipeModel; - -import net.minecraft.resources.ResourceLocation; - -import lombok.Getter; - -public enum ItemPipeType implements IMaterialPipeType { - - SMALL("small", 0.375f, TagPrefix.pipeSmallItem, 0.5f, 1.5f), - NORMAL("normal", 0.5f, TagPrefix.pipeNormalItem, 1f, 1f), - LARGE("large", 0.75f, TagPrefix.pipeLargeItem, 2f, 0.75f), - HUGE("huge", 0.875f, TagPrefix.pipeHugeItem, 4f, 0.5f), - - RESTRICTIVE_SMALL("small_restrictive", 0.375f, TagPrefix.pipeSmallRestrictive, 0.5f, 150f), - RESTRICTIVE_NORMAL("normal_restrictive", 0.5f, TagPrefix.pipeNormalRestrictive, 1f, 100f), - RESTRICTIVE_LARGE("large_restrictive", 0.75f, TagPrefix.pipeLargeRestrictive, 2f, 75f), - RESTRICTIVE_HUGE("huge_restrictive", 0.875f, TagPrefix.pipeHugeRestrictive, 4f, 50f); - - public static final ResourceLocation TYPE_ID = GTCEu.id("item"); - public static final ItemPipeType[] VALUES = values(); - - @Getter - public final String name; - @Getter - private final float thickness; - @Getter - private final float rateMultiplier; - private final float resistanceMultiplier; - @Getter - private final TagPrefix tagPrefix; - - ItemPipeType(String name, float thickness, TagPrefix orePrefix, float rateMultiplier, float resistanceMultiplier) { - this.name = name; - this.thickness = thickness; - this.tagPrefix = orePrefix; - this.rateMultiplier = rateMultiplier; - this.resistanceMultiplier = resistanceMultiplier; - } - - public boolean isRestrictive() { - return ordinal() > 3; - } - - public String getSizeForTexture() { - if (!isRestrictive()) - return name; - else - return name.substring(0, name.length() - 12); - } - - @Override - public ItemPipeProperties modifyProperties(ItemPipeProperties baseProperties) { - return new ItemPipeProperties((int) ((baseProperties.getPriority() * resistanceMultiplier) + 0.5), - baseProperties.getTransferRate() * rateMultiplier); - } - - @Override - public boolean isPaintable() { - return true; - } - - @Override - public ResourceLocation type() { - return TYPE_ID; - } - - public PipeModel createPipeModel(Material material) { - PipeModel model; - if (material.hasProperty(PropertyKey.WOOD)) { - model = new PipeModel(thickness, () -> GTCEu.id("block/pipe/pipe_side_wood"), - () -> GTCEu.id("block/pipe/pipe_%s_in_wood" - .formatted(this.isRestrictive() ? values()[this.ordinal() - 4].name : name)), - null, null); - } else { - model = new PipeModel(thickness, () -> GTCEu.id("block/pipe/pipe_side"), - () -> GTCEu.id("block/pipe/pipe_%s_in" - .formatted(this.isRestrictive() ? values()[this.ordinal() - 4].name : name)), - null, null/* - * () -> GTCEu.id("block/pipe/pipe_side_secondary"), () -> - * GTCEu.id("block/pipe/pipe_%s_in_secondary".formatted(this.isRestrictive() ? - * values()[this.ordinal() - 4].name : name)) TODO enable once the textures are added - */); - } - if (isRestrictive()) { - model.setSideOverlayTexture(GTCEu.id("block/pipe/pipe_restrictive")); - } - return model; - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/item/ItemRoutePath.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/item/ItemRoutePath.java deleted file mode 100644 index 3bb60e8655..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/item/ItemRoutePath.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.item; - -import com.gregtechceu.gtceu.api.data.chemical.material.properties.ItemPipeProperties; -import com.gregtechceu.gtceu.api.pipenet.IRoutePath; -import com.gregtechceu.gtceu.common.blockentity.ItemPipeBlockEntity; -import com.gregtechceu.gtceu.utils.FacingPos; -import com.gregtechceu.gtceu.utils.GTTransferUtils; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.Level; -import net.minecraftforge.items.IItemHandler; - -import lombok.Getter; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.List; -import java.util.function.Predicate; - -public class ItemRoutePath implements IRoutePath { - - @Getter - private final ItemPipeBlockEntity targetPipe; - @NotNull - @Getter - private final Direction targetFacing; - @Getter - private final int distance; - @Getter - private final ItemPipeProperties properties; - private final Predicate filters; - - public ItemRoutePath(ItemPipeBlockEntity targetPipe, @NotNull Direction facing, int distance, - ItemPipeProperties properties, - List> filters) { - this.targetPipe = targetPipe; - this.targetFacing = facing; - this.distance = distance; - this.properties = properties; - this.filters = stack -> { - for (Predicate filter : filters) - if (!filter.test(stack)) return false; - return true; - }; - } - - @Override - public @NotNull BlockPos getTargetPipePos() { - return targetPipe.getPipePos(); - } - - @Override - public @Nullable IItemHandler getHandler(Level world) { - return GTTransferUtils.getAdjacentItemHandler(world, getTargetPipePos(), targetFacing).resolve().orElse(null); - } - - public boolean matchesFilters(ItemStack stack) { - return filters.test(stack); - } - - public FacingPos toFacingPos() { - return new FacingPos(getTargetPipePos(), targetFacing); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/item/LevelItemPipeNet.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/item/LevelItemPipeNet.java deleted file mode 100644 index b15987806c..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/item/LevelItemPipeNet.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.item; - -import com.gregtechceu.gtceu.api.data.chemical.material.properties.ItemPipeProperties; -import com.gregtechceu.gtceu.api.pipenet.LevelPipeNet; - -import net.minecraft.nbt.CompoundTag; -import net.minecraft.server.level.ServerLevel; - -public class LevelItemPipeNet extends LevelPipeNet { - - public static LevelItemPipeNet getOrCreate(ServerLevel serverLevel) { - return serverLevel.getDataStorage().computeIfAbsent(tag -> new LevelItemPipeNet(serverLevel, tag), - () -> new LevelItemPipeNet(serverLevel), "gtceu_item_pipe_net"); - } - - public LevelItemPipeNet(ServerLevel serverLevel) { - super(serverLevel); - } - - public LevelItemPipeNet(ServerLevel serverLevel, CompoundTag tag) { - super(serverLevel, tag); - } - - @Override - protected ItemPipeNet createNetInstance() { - return new ItemPipeNet(this); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/laser/LaserNetHandler.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/laser/LaserNetHandler.java deleted file mode 100644 index c83c66c836..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/laser/LaserNetHandler.java +++ /dev/null @@ -1,115 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.laser; - -import com.gregtechceu.gtceu.api.capability.ILaserContainer; -import com.gregtechceu.gtceu.common.blockentity.LaserPipeBlockEntity; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.world.level.Level; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class LaserNetHandler implements ILaserContainer { - - private LaserPipeNet net; - private final LaserPipeBlockEntity pipe; - private final Direction facing; - private final Level world; - - public LaserNetHandler(LaserPipeNet net, @NotNull LaserPipeBlockEntity pipe, @Nullable Direction facing) { - this.net = net; - this.pipe = pipe; - this.facing = facing; - this.world = pipe.getLevel(); - } - - public void updateNetwork(LaserPipeNet net) { - this.net = net; - } - - private void setPipesActive() { - for (BlockPos pos : net.getAllNodes().keySet()) { - if (world.getBlockEntity(pos) instanceof LaserPipeBlockEntity laserPipe) { - laserPipe.setActive(true, 100); - } - } - } - - @Nullable - private ILaserContainer getInnerContainer() { - if (net == null || pipe == null || pipe.isInValid() || (facing == null || pipe.isBlocked(facing))) { - return null; - } - - LaserRoutePath data = net.getNetData(pipe.getPipePos(), facing); - if (data == null) { - return null; - } - - return data.getHandler(net.getLevel()); - } - - @Override - public long acceptEnergyFromNetwork(Direction side, long voltage, long amperage) { - ILaserContainer handler = getInnerContainer(); - if (handler == null) return 0; - setPipesActive(); - return handler.acceptEnergyFromNetwork(side, voltage, amperage); - } - - @Override - public boolean inputsEnergy(Direction side) { - ILaserContainer handler = getInnerContainer(); - if (handler == null) return false; - return handler.inputsEnergy(side); - } - - @Override - public boolean outputsEnergy(Direction side) { - ILaserContainer handler = getInnerContainer(); - if (handler == null) return false; - return handler.outputsEnergy(side); - } - - @Override - public long changeEnergy(long amount) { - ILaserContainer handler = getInnerContainer(); - if (handler == null) return 0; - setPipesActive(); - return handler.changeEnergy(amount); - } - - @Override - public long getEnergyStored() { - ILaserContainer handler = getInnerContainer(); - if (handler == null) return 0; - return handler.getEnergyStored(); - } - - @Override - public long getEnergyCapacity() { - ILaserContainer handler = getInnerContainer(); - if (handler == null) return 0; - return handler.getEnergyCapacity(); - } - - @Override - public long getInputAmperage() { - return 0; - } - - @Override - public long getInputVoltage() { - return 0; - } - - public LaserPipeNet getNet() { - return net; - } - - @Override - public boolean isOneProbeHidden() { - return true; - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/laser/LaserNetWalker.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/laser/LaserNetWalker.java deleted file mode 100644 index 1dc939e15e..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/laser/LaserNetWalker.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.laser; - -import com.gregtechceu.gtceu.api.capability.ILaserContainer; -import com.gregtechceu.gtceu.api.capability.forge.GTCapability; -import com.gregtechceu.gtceu.api.pipenet.PipeNetWalker; -import com.gregtechceu.gtceu.common.blockentity.LaserPipeBlockEntity; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.world.level.block.entity.BlockEntity; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class LaserNetWalker extends PipeNetWalker { - - public static final LaserRoutePath FAILED_MARKER = new LaserRoutePath(null, null, 0); - - @Nullable - public static LaserRoutePath createNetData(LaserPipeNet world, BlockPos sourcePipe, Direction faceToSourceHandler) { - try { - LaserNetWalker walker = new LaserNetWalker(world, sourcePipe, 1); - walker.sourcePipe = sourcePipe; - walker.facingToHandler = faceToSourceHandler; - walker.axis = faceToSourceHandler.getAxis(); - walker.traversePipeNet(); - return walker.routePath; - } catch (Exception e) { - return FAILED_MARKER; - } - } - - private static final Direction[] X_AXIS_FACINGS = { Direction.WEST, Direction.EAST }; - private static final Direction[] Y_AXIS_FACINGS = { Direction.UP, Direction.DOWN }; - private static final Direction[] Z_AXIS_FACINGS = { Direction.NORTH, Direction.SOUTH }; - - private LaserRoutePath routePath; - private BlockPos sourcePipe; - private Direction facingToHandler; - private Direction.Axis axis; - - protected LaserNetWalker(LaserPipeNet world, BlockPos sourcePipe, int distance) { - super(world, sourcePipe, distance); - } - - @NotNull - @Override - protected PipeNetWalker createSubWalker(LaserPipeNet net, - Direction direction, - BlockPos nextPos, - int walkedBlocks) { - LaserNetWalker walker = new LaserNetWalker(net, nextPos, walkedBlocks); - walker.facingToHandler = facingToHandler; - walker.sourcePipe = sourcePipe; - walker.axis = axis; - return walker; - } - - @Override - protected Class getBasePipeClass() { - return LaserPipeBlockEntity.class; - } - - @Override - protected void checkPipe(LaserPipeBlockEntity pipeTile, BlockPos pos) {} - - @Override - protected Direction[] getSurroundingPipeSides() { - return switch (axis) { - case X -> X_AXIS_FACINGS; - case Y -> Y_AXIS_FACINGS; - case Z -> Z_AXIS_FACINGS; - }; - } - - @Override - protected void checkNeighbour(LaserPipeBlockEntity pipeNode, BlockPos pipePos, Direction faceToNeighbour, - @org.jetbrains.annotations.Nullable BlockEntity neighbourTile) { - if (neighbourTile == null || (pipePos.equals(sourcePipe) && faceToNeighbour == facingToHandler)) { - return; - } - - if (((LaserNetWalker) root).routePath == null) { - ILaserContainer handler = neighbourTile - .getCapability(GTCapability.CAPABILITY_LASER, faceToNeighbour.getOpposite()).resolve().orElse(null); - if (handler != null) { - ((LaserNetWalker) root).routePath = new LaserRoutePath(pipePos.immutable(), faceToNeighbour, - getWalkedBlocks()); - stop(); - } - } - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/laser/LaserPipeNet.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/laser/LaserPipeNet.java deleted file mode 100644 index 65c0358977..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/laser/LaserPipeNet.java +++ /dev/null @@ -1,125 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.laser; - -import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; -import com.gregtechceu.gtceu.api.capability.ILaserContainer; -import com.gregtechceu.gtceu.api.pipenet.IAttachData; -import com.gregtechceu.gtceu.api.pipenet.PipeNet; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.world.level.Level; - -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import lombok.AllArgsConstructor; -import lombok.Getter; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Map; - -public class LaserPipeNet extends PipeNet { - - private final Map netData = new Object2ObjectOpenHashMap<>(); - - public LaserPipeNet(LevelLaserPipeNet world) { - super(world); - } - - @Nullable - public LaserRoutePath getNetData(BlockPos pipePos, Direction facing) { - LaserRoutePath data = netData.get(pipePos); - if (data == null) { - data = LaserNetWalker.createNetData(this, pipePos, facing); - if (data == LaserNetWalker.FAILED_MARKER) { - // walker failed, don't cache, so it tries again on next insertion - return null; - } - - netData.put(pipePos, data); - } - return data; - } - - @Override - public void onNeighbourUpdate(BlockPos fromPos) { - netData.clear(); - } - - @Override - public void onPipeConnectionsUpdate() { - netData.clear(); - } - - @Override - protected void writeNodeData(LaserPipeProperties laserPipeProperties, CompoundTag compoundTag) {} - - @Override - protected LaserPipeProperties readNodeData(CompoundTag tagCompound) { - return LaserPipeProperties.INSTANCE; - } - - @AllArgsConstructor - public static class LaserData implements IAttachData { - - /** - * The current position of the pipe - */ - @Getter - private final BlockPos pipePos; - /** - * The current face to handler - */ - @Getter - private final Direction faceToHandler; - /** - * The manhattan distance traveled during walking - */ - @Getter - private final int distance; - /** - * The laser pipe properties of the current pipe - */ - @Getter - private final LaserPipeProperties properties; - @Getter - byte connections; - - @Override - public boolean canAttachTo(Direction side) { - return (connections & (1 << side.ordinal())) != 0 && side.getAxis() == this.faceToHandler.getAxis(); - } - - @Override - public boolean setAttached(Direction side, boolean attach) { - var result = canAttachTo(side); - if (result != attach) { - if (attach) { - connections |= (1 << side.ordinal()); - } else { - connections &= ~(1 << side.ordinal()); - } - } - return result != attach; - } - - /** - * @return The position of where the handler would be - */ - @NotNull - public BlockPos getHandlerPos() { - return pipePos.relative(faceToHandler); - } - - /** - * Gets the handler if it exists - * - * @param world the world to get the handler from - * @return the handler - */ - @Nullable - public ILaserContainer getHandler(@NotNull Level world) { - return GTCapabilityHelper.getLaser(world, getHandlerPos(), faceToHandler.getOpposite()); - } - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/laser/LaserPipeProperties.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/laser/LaserPipeProperties.java deleted file mode 100644 index ec8daffb2b..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/laser/LaserPipeProperties.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.laser; - -import lombok.NoArgsConstructor; - -@NoArgsConstructor -public class LaserPipeProperties { - - public static final LaserPipeProperties INSTANCE = new LaserPipeProperties(); -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/laser/LaserPipeType.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/laser/LaserPipeType.java deleted file mode 100644 index 18fafe18fb..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/laser/LaserPipeType.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.laser; - -import com.gregtechceu.gtceu.GTCEu; -import com.gregtechceu.gtceu.api.pipenet.IPipeType; - -import net.minecraft.resources.ResourceLocation; -import net.minecraft.util.StringRepresentable; - -import java.util.Locale; - -public enum LaserPipeType implements IPipeType, StringRepresentable { - - NORMAL; - - public static final ResourceLocation TYPE_ID = GTCEu.id("laser"); - - @Override - public float getThickness() { - return 0.375f; - } - - @Override - public LaserPipeProperties modifyProperties(LaserPipeProperties baseProperties) { - return baseProperties; - } - - @Override - public boolean isPaintable() { - return true; - } - - @Override - public ResourceLocation type() { - return TYPE_ID; - } - - @Override - public String getSerializedName() { - return name().toLowerCase(Locale.ROOT); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/laser/LaserRoutePath.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/laser/LaserRoutePath.java deleted file mode 100644 index 8b2f095df3..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/laser/LaserRoutePath.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.laser; - -import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; -import com.gregtechceu.gtceu.api.capability.ILaserContainer; -import com.gregtechceu.gtceu.api.pipenet.IAttachData; -import com.gregtechceu.gtceu.api.pipenet.IRoutePath; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.world.level.Level; - -import lombok.Getter; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class LaserRoutePath implements IRoutePath, IAttachData { - - @Getter - private final BlockPos targetPipePos; - /** - * the current face to handler - */ - @NotNull - @Getter - private final Direction targetFacing; - /** - * the manhattan distance traveled during walking - */ - @Getter - private final int distance; - @Getter - byte connections; - - public LaserRoutePath(BlockPos targetPipePos, Direction targetFacing, int distance) { - this.targetPipePos = targetPipePos; - this.targetFacing = targetFacing; - this.distance = distance; - } - - /** - * Gets the handler if it exists - * - * @return the handler - */ - @Nullable - public ILaserContainer getHandler(Level level) { - return GTCapabilityHelper.getLaser(level, getTargetPipePos().relative(targetFacing), - targetFacing.getOpposite()); - } - - @Override - public boolean canAttachTo(Direction side) { - return (connections & (1 << side.ordinal())) != 0 && side.getAxis() == this.targetFacing.getAxis(); - } - - @Override - public boolean setAttached(Direction side, boolean attach) { - var result = canAttachTo(side); - if (result != attach) { - if (attach) { - connections |= (1 << side.ordinal()); - } else { - connections &= ~(1 << side.ordinal()); - } - } - return result != attach; - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/laser/LevelLaserPipeNet.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/laser/LevelLaserPipeNet.java deleted file mode 100644 index a7164186f9..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/laser/LevelLaserPipeNet.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.laser; - -import com.gregtechceu.gtceu.api.pipenet.LevelPipeNet; - -import net.minecraft.nbt.CompoundTag; -import net.minecraft.server.level.ServerLevel; - -public class LevelLaserPipeNet extends LevelPipeNet { - - private static final String DATA_ID = "gtceu_laser_pipe_net"; - - public static LevelLaserPipeNet getOrCreate(ServerLevel serverLevel) { - return serverLevel.getDataStorage().computeIfAbsent(tag -> new LevelLaserPipeNet(serverLevel, tag), - () -> new LevelLaserPipeNet(serverLevel), DATA_ID); - } - - public LevelLaserPipeNet(ServerLevel serverLevel) { - super(serverLevel); - } - - public LevelLaserPipeNet(ServerLevel serverLevel, CompoundTag tag) { - super(serverLevel, tag); - } - - @Override - protected LaserPipeNet createNetInstance() { - return new LaserPipeNet(this); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/fluidpipe/longdistance/LDFluidEndpointMachine.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/longdistance/fluid/LDFluidEndpointMachine.java similarity index 97% rename from src/main/java/com/gregtechceu/gtceu/common/pipelike/fluidpipe/longdistance/LDFluidEndpointMachine.java rename to src/main/java/com/gregtechceu/gtceu/common/pipelike/longdistance/fluid/LDFluidEndpointMachine.java index a13c6ec0e5..9679fd47d3 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/fluidpipe/longdistance/LDFluidEndpointMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/longdistance/fluid/LDFluidEndpointMachine.java @@ -1,4 +1,4 @@ -package com.gregtechceu.gtceu.common.pipelike.fluidpipe.longdistance; +package com.gregtechceu.gtceu.common.pipelike.longdistance.fluid; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; import com.gregtechceu.gtceu.api.transfer.fluid.IFluidHandlerModifiable; diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/fluidpipe/longdistance/LDFluidPipeType.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/longdistance/fluid/LDFluidPipeType.java similarity index 86% rename from src/main/java/com/gregtechceu/gtceu/common/pipelike/fluidpipe/longdistance/LDFluidPipeType.java rename to src/main/java/com/gregtechceu/gtceu/common/pipelike/longdistance/fluid/LDFluidPipeType.java index c4ef7baeb5..3a15326b98 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/fluidpipe/longdistance/LDFluidPipeType.java +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/longdistance/fluid/LDFluidPipeType.java @@ -1,4 +1,4 @@ -package com.gregtechceu.gtceu.common.pipelike.fluidpipe.longdistance; +package com.gregtechceu.gtceu.common.pipelike.longdistance.fluid; import com.gregtechceu.gtceu.api.pipenet.longdistance.LongDistancePipeType; import com.gregtechceu.gtceu.config.ConfigHolder; diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/item/longdistance/LDItemEndpointMachine.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/longdistance/item/LDItemEndpointMachine.java similarity index 88% rename from src/main/java/com/gregtechceu/gtceu/common/pipelike/item/longdistance/LDItemEndpointMachine.java rename to src/main/java/com/gregtechceu/gtceu/common/pipelike/longdistance/item/LDItemEndpointMachine.java index cf239fab56..6c2fff8082 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/item/longdistance/LDItemEndpointMachine.java +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/longdistance/item/LDItemEndpointMachine.java @@ -1,4 +1,4 @@ -package com.gregtechceu.gtceu.common.pipelike.item.longdistance; +package com.gregtechceu.gtceu.common.pipelike.longdistance.item; import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; import com.gregtechceu.gtceu.common.machine.storage.LongDistanceEndpointMachine; @@ -10,8 +10,8 @@ public class LDItemEndpointMachine extends LongDistanceEndpointMachine { - public LDItemEndpointMachine(IMachineBlockEntity metaTileEntityId) { - super(metaTileEntityId, LDItemPipeType.INSTANCE); + public LDItemEndpointMachine(IMachineBlockEntity metaBlockEntityId) { + super(metaBlockEntityId, LDItemPipeType.INSTANCE); } @SuppressWarnings("UnstableApiUsage") diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/item/longdistance/LDItemPipeType.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/longdistance/item/LDItemPipeType.java similarity index 87% rename from src/main/java/com/gregtechceu/gtceu/common/pipelike/item/longdistance/LDItemPipeType.java rename to src/main/java/com/gregtechceu/gtceu/common/pipelike/longdistance/item/LDItemPipeType.java index cedcb69abf..d45e3c200e 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/item/longdistance/LDItemPipeType.java +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/longdistance/item/LDItemPipeType.java @@ -1,4 +1,4 @@ -package com.gregtechceu.gtceu.common.pipelike.item.longdistance; +package com.gregtechceu.gtceu.common.pipelike.longdistance.item; import com.gregtechceu.gtceu.api.pipenet.longdistance.LongDistancePipeType; import com.gregtechceu.gtceu.config.ConfigHolder; diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/SlowActiveWalker.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/SlowActiveWalker.java new file mode 100644 index 0000000000..b05ccf7439 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/SlowActiveWalker.java @@ -0,0 +1,118 @@ +package com.gregtechceu.gtceu.common.pipelike.net; + +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; +import com.gregtechceu.gtceu.api.graphnet.path.NetPath; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.ActivablePipeBlockEntity; +import com.gregtechceu.gtceu.utils.GTUtil; +import com.gregtechceu.gtceu.utils.TaskScheduler; +import com.gregtechceu.gtceu.utils.function.Task; + +import net.minecraft.world.level.Level; + +import com.google.common.collect.ImmutableCollection; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.WeakHashMap; + +public class SlowActiveWalker implements Task { + + private static final int RECENT_WALKER_CUTOFF = 10; + + private static final Map RECENT_DISPATCHES = new WeakHashMap<>(); + + /** + * Dispatches a slow walker along a path with default parameters. + * + * @param world the world to schedule the task in. When this world is unloaded, the task will die no matter + * its state, so be careful! + * @param path the path to walk. + * @param delay the ticks between steps of the walker + */ + public static void dispatch(Level world, NetPath path, int delay) { + dispatch(world, path, delay, 1, 1); + } + + /** + * Dispatches a slow walker along a path. + * + * @param world the world to schedule the task in. When this world is unloaded, the task will die no matter + * its state, so be careful! + * @param path the path to walk. + * @param delay the ticks between steps of the walker + * @param stepSize the number of nodes within the path that the walker progresses every step + * @param activeLength the number of tiles that will be left active behind a progressing walker + */ + public static void dispatch(Level world, NetPath path, int delay, + int stepSize, int activeLength) { + long tick = GTUtil.getCurrentServerTick(); + RECENT_DISPATCHES.compute(path, (k, v) -> { + if (v == null || v < tick) { + SlowActiveWalker walker = new SlowActiveWalker(path, delay, stepSize, activeLength); + TaskScheduler.scheduleTask(world, walker); + return tick + RECENT_WALKER_CUTOFF; + } else return v; + }); + } + + private final NetPath path; + private final int lastStep; + private int index = 0; + + private final int delay; + private final int stepSize; + private final int activeLength; + private int counter; + + protected SlowActiveWalker(NetPath path, int delay, int stepSize, + int activeLength) { + this.path = path; + this.delay = delay; + this.stepSize = stepSize; + this.activeLength = activeLength; + this.lastStep = this.path.getOrderedNodes().size() + activeLength - 1; + this.step(getSafe(-stepSize), getSafe(0)); + } + + @Override + public boolean run() { + counter++; + if (counter >= delay) { + counter = 0; + for (int i = 0; i < stepSize; i++) { + index++; + this.step(getSafe(index - activeLength), getSafe(index)); + if (index >= lastStep) { + return false; + } + } + } + return true; + } + + protected @Nullable WorldPipeNode getSafe(int index) { + if (index >= path.getOrderedNodes().size()) return null; + else if (index < 0) return null; + else { + NetNode n = getNodes().asList().get(index); + return n instanceof WorldPipeNode p ? p : null; + } + } + + protected ImmutableCollection getNodes() { + return path.getOrderedNodes(); + } + + protected void step(@Nullable WorldPipeNode previous, @Nullable WorldPipeNode next) { + if (previous != null) activate(previous, false); + if (next != null) activate(next, true); + } + + protected void activate(@NotNull WorldPipeNode node, boolean active) { + if (node.getBlockEntity() instanceof ActivablePipeBlockEntity activable) { + activable.setActive(active); + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/duct/DuctCapabilityObject.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/duct/DuctCapabilityObject.java new file mode 100644 index 0000000000..8b98c94cba --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/duct/DuctCapabilityObject.java @@ -0,0 +1,302 @@ +package com.gregtechceu.gtceu.common.pipelike.net.duct; + +import com.gregtechceu.gtceu.api.GTValues; +import com.gregtechceu.gtceu.api.capability.IHazardParticleContainer; +import com.gregtechceu.gtceu.api.capability.forge.GTCapability; +import com.gregtechceu.gtceu.api.data.chemical.material.properties.HazardProperty; +import com.gregtechceu.gtceu.api.data.medicalcondition.MedicalCondition; +import com.gregtechceu.gtceu.api.graphnet.GraphNetUtility; +import com.gregtechceu.gtceu.api.graphnet.logic.ChannelCountLogic; +import com.gregtechceu.gtceu.api.graphnet.logic.ThroughputLogic; +import com.gregtechceu.gtceu.api.graphnet.net.NetEdge; +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.NodeExposingCapabilities; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IPipeCapabilityObject; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.NodeManagingPCW; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeCapabilityWrapper; +import com.gregtechceu.gtceu.api.graphnet.traverse.EdgeDirection; +import com.gregtechceu.gtceu.api.graphnet.traverse.EdgeSelector; +import com.gregtechceu.gtceu.api.graphnet.traverse.ResilientNetClosestIterator; +import com.gregtechceu.gtceu.common.capability.EnvironmentalHazardSavedData; +import com.gregtechceu.gtceu.utils.GTMath; +import com.gregtechceu.gtceu.utils.GTUtil; +import com.gregtechceu.gtceu.utils.MapUtil; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.server.level.ServerLevel; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.util.LazyOptional; + +import it.unimi.dsi.fastutil.objects.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayDeque; +import java.util.EnumMap; +import java.util.List; + +public class DuctCapabilityObject implements IPipeCapabilityObject, IHazardParticleContainer { + + public static final int ACTIVE_KEY = 155; + + private @Nullable PipeBlockEntity blockEntity; + private NodeManagingPCW capabilityWrapper; + + private final EnumMap wrappers = new EnumMap<>(Direction.class); + private final @NotNull WorldPipeNode node; + + private boolean transferring = false; + + public DuctCapabilityObject(@NotNull WorldPipeNode node) { + this.node = node; + for (Direction facing : GTUtil.DIRECTIONS) { + wrappers.put(facing, new DuctCapabilityObject.Wrapper(facing)); + } + } + + @Override + public void init(@NotNull PipeBlockEntity tile, @NotNull PipeCapabilityWrapper wrapper) { + this.blockEntity = tile; + if (!(wrapper instanceof NodeManagingPCW p)) + throw new IllegalArgumentException("DuctCapabilityObjects must be initialized to NodeManagingPCWs!"); + this.capabilityWrapper = p; + } + + @Override + public @NotNull LazyOptional getCapability(@NotNull Capability cap, @Nullable Direction side) { + // can't expose the sided capability if there is no node to interact with + if (side != null && capabilityWrapper.getNodeForFacing(side) == null) return LazyOptional.empty(); + return GTCapability.CAPABILITY_HAZARD_CONTAINER.orEmpty(cap, + LazyOptional.of(() -> side == null ? this : wrappers.get(side))); + } + + private boolean inputDisallowed(Direction side) { + if (side == null) return false; + if (blockEntity == null) return true; + else return blockEntity.isBlocked(side); + } + + protected @Nullable NetNode getRelevantNode(Direction facing) { + return facing == null ? node : capabilityWrapper.getNodeForFacing(facing); + } + + @Override + public boolean inputsHazard(Direction side, MedicalCondition condition) { + return !inputDisallowed(side); + } + + @Override + public float changeHazard(MedicalCondition condition, float amount, boolean simulate) { + return changeHazard(condition, amount, simulate, null); + } + + public float changeHazard(MedicalCondition condition, float differenceAmount, boolean simulate, + @Nullable Direction side) { + if (this.transferring || inputDisallowed(side)) return 0; + NetNode node = getRelevantNode(side); + if (node == null) node = this.node; + this.transferring = true; + + float flow = differenceAmount; + DuctTestObject testObject = new DuctTestObject(condition); + ResilientNetClosestIterator iter = new ResilientNetClosestIterator(node, + EdgeSelector.filtered(EdgeDirection.OUTGOING, GraphNetUtility.standardEdgeBlacklist(testObject))); + Object2FloatOpenHashMap availableDemandCache = new Object2FloatOpenHashMap<>(); + Object2FloatOpenHashMap flowLimitCache = new Object2FloatOpenHashMap<>(); + List postActions = new ObjectArrayList<>(); + float total = 0; + main: + while (iter.hasNext()) { + if (flow <= 0) break; + final NetNode next = iter.next(); + float limit = Math.min(MapUtil.computeIfAbsent(flowLimitCache, next, n -> getFlowLimit(n, testObject)), + flow); + if (limit <= 0) { + iter.markInvalid(next); + continue; + } + float supply = MapUtil.computeIfAbsent(availableDemandCache, next, + n -> getSupplyOrDemand(n, testObject, false)); + if (supply <= 0) continue; + supply = Math.min(supply, limit); + NetEdge span; + NetNode trace = next; + ArrayDeque seen = new ArrayDeque<>(); + seen.add(next); + while ((span = iter.getSpanningTreeEdge(trace)) != null) { + trace = span.getOppositeNode(trace); + if (trace == null) continue main; + float l = MapUtil.computeIfAbsent(flowLimitCache, trace, n -> getFlowLimit(n, testObject)); + if (l == 0) { + iter.markInvalid(node); + continue main; + } + supply = Math.min(supply, l); + seen.addFirst(trace); + } + total += supply; + flow -= supply; + final float finalSupply = supply; + for (NetNode n : seen) { + // reporting flow can cause temperature pipe destruction which causes graph modification while + // iterating. + if (!simulate) postActions.add(() -> reportFlow(n, finalSupply, testObject)); + float remaining = flowLimitCache.getFloat(n) - supply; + flowLimitCache.put(n, remaining); + if (remaining <= 0) { + iter.markInvalid(n); + } + } + if (!simulate) reportExtractedInserted(next, supply, testObject, false); + availableDemandCache.put(next, availableDemandCache.getFloat(next) - supply); + } + postActions.forEach(Runnable::run); + this.transferring = false; + return total; + } + + public static float getFlowLimit(NetNode node, DuctTestObject testObject) { + ThroughputLogic throughput = node.getData().getLogicEntryNullable(ThroughputLogic.TYPE); + if (throughput == null) return Float.MAX_VALUE; + DuctFlowLogic history = node.getData().getLogicEntryNullable(DuctFlowLogic.TYPE); + if (history == null) return throughput.getValue() * DuctFlowLogic.MEMORY_TICKS; + Object2FloatMap sum = history.getSum(); + if (sum.isEmpty()) return GTMath.saturatedCast(throughput.getValue() * DuctFlowLogic.MEMORY_TICKS); + if (sum.size() < node.getData().getLogicEntryDefaultable(ChannelCountLogic.TYPE).getValue() || + sum.containsKey(testObject)) { + return throughput.getValue() * DuctFlowLogic.MEMORY_TICKS - sum.getFloat(testObject); + } + return 0; + } + + public static void reportFlow(NetNode node, float flow, DuctTestObject testObject) { + DuctFlowLogic logic = node.getData().getLogicEntryNullable(DuctFlowLogic.TYPE); + if (logic == null) { + logic = DuctFlowLogic.TYPE.getNew(); + node.getData().setLogicEntry(logic); + } + logic.recordFlow(GTUtil.getCurrentServerTick(), testObject, flow); + } + + public static float getSupplyOrDemand(NetNode node, DuctTestObject testObject, boolean supply) { + if (node instanceof NodeExposingCapabilities exposer) { + IHazardParticleContainer handler = exposer.getProvider() + .getCapability(GTCapability.CAPABILITY_HAZARD_CONTAINER, exposer.exposedFacing()) + .resolve().orElse(null); + if (handler != null && instanceOf(handler) == null) { + if (supply) { + return handler.removeHazard(testObject.condition, Float.MAX_VALUE, true); + } else { + return handler.addHazard(testObject.condition, Float.MAX_VALUE, true); + } + } else if (handler == null) { + return Float.MAX_VALUE; + } + } + return 0; + } + + public void reportExtractedInserted(NetNode node, float flow, DuctTestObject testObject, boolean extracted) { + if (flow <= 0) return; + if (node instanceof NodeExposingCapabilities exposer) { + IHazardParticleContainer handler = exposer.getProvider() + .getCapability(GTCapability.CAPABILITY_HAZARD_CONTAINER, exposer.exposedFacing()) + .resolve().orElse(null); + if (handler != null) { + if (extracted) { + handler.removeHazard(testObject.condition, flow, false); + } else { + handler.addHazard(testObject.condition, flow, false); + } + } else if (node instanceof WorldPipeNode pipeNode) { + ServerLevel level = pipeNode.getNet().getLevel(); + BlockPos pos = pipeNode.getEquivalencyData(); + Direction openDir = exposer.exposedFacing(); + var savedData = EnvironmentalHazardSavedData.getOrCreate(level); + savedData.addZone(pos.relative(openDir), flow, + true, HazardProperty.HazardTrigger.INHALATION, testObject.condition); + emitPollutionParticles(level, pos, openDir); + } + } + } + + @Override + public float getHazardStored(MedicalCondition condition) { + return 0; + } + + @Override + public float getHazardCapacity(MedicalCondition condition) { + return Float.MAX_VALUE; + } + + public static void emitPollutionParticles(ServerLevel level, BlockPos pos, Direction frontFacing) { + float xPos = frontFacing.getStepX() * 0.76F + pos.getX() + 0.25F; + float yPos = frontFacing.getStepY() * 0.76F + pos.getY() + 0.25F; + float zPos = frontFacing.getStepZ() * 0.76F + pos.getZ() + 0.25F; + + float ySpd = frontFacing.getStepY() * 0.1F + 0.2F + 0.1F * GTValues.RNG.nextFloat(); + float xSpd; + float zSpd; + + if (frontFacing.getStepY() == -1) { + float temp = GTValues.RNG.nextFloat() * 2 * (float) Math.PI; + xSpd = (float) Math.sin(temp) * 0.1F; + zSpd = (float) Math.cos(temp) * 0.1F; + } else { + xSpd = frontFacing.getStepX() * (0.1F + 0.2F * GTValues.RNG.nextFloat()); + zSpd = frontFacing.getStepZ() * (0.1F + 0.2F * GTValues.RNG.nextFloat()); + } + level.sendParticles(ParticleTypes.LARGE_SMOKE, + xPos + GTValues.RNG.nextFloat() * 0.5F, + yPos + GTValues.RNG.nextFloat() * 0.5F, + zPos + GTValues.RNG.nextFloat() * 0.5F, + 1, + xSpd, ySpd, zSpd, + 0.1); + } + + @Nullable + public static DuctCapabilityObject instanceOf(IHazardParticleContainer container) { + if (container instanceof DuctCapabilityObject f) return f; + if (container instanceof DuctCapabilityObject.Wrapper w) return w.getParent(); + return null; + } + + protected class Wrapper implements IHazardParticleContainer { + + private final Direction facing; + + public Wrapper(Direction facing) { + this.facing = facing; + } + + @Override + public boolean inputsHazard(Direction side, MedicalCondition condition) { + return DuctCapabilityObject.this.inputsHazard(facing, condition); + } + + @Override + public float changeHazard(MedicalCondition condition, float amount, boolean simulate) { + return DuctCapabilityObject.this.changeHazard(condition, amount, simulate, facing); + } + + @Override + public float getHazardStored(MedicalCondition condition) { + return 0; + } + + @Override + public float getHazardCapacity(MedicalCondition condition) { + return Float.MAX_VALUE; + } + + public DuctCapabilityObject getParent() { + return DuctCapabilityObject.this; + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/duct/DuctFlowLogic.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/duct/DuctFlowLogic.java new file mode 100644 index 0000000000..55ed133a8b --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/duct/DuctFlowLogic.java @@ -0,0 +1,71 @@ +package com.gregtechceu.gtceu.common.pipelike.net.duct; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.data.medicalcondition.HazardStack; +import com.gregtechceu.gtceu.api.data.medicalcondition.MedicalCondition; +import com.gregtechceu.gtceu.api.graphnet.logic.AbstractTransientLogicData; +import com.gregtechceu.gtceu.api.graphnet.logic.NetLogicType; +import com.gregtechceu.gtceu.utils.GTUtil; + +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.*; +import lombok.Getter; +import org.jetbrains.annotations.NotNull; + +public class DuctFlowLogic extends AbstractTransientLogicData { + + public static final NetLogicType TYPE = new NetLogicType<>(GTCEu.MOD_ID, "DuctFlow", + DuctFlowLogic::new, new DuctFlowLogic()); + + public static final int MEMORY_TICKS = WorldDuctNet.getBufferTicks(); + + private final Long2ObjectOpenHashMap> memory = new Long2ObjectOpenHashMap<>(); + @Getter + private HazardStack last; + + @Override + public @NotNull NetLogicType getType() { + return TYPE; + } + + public @NotNull Long2ObjectOpenHashMap> getMemory() { + updateMemory(GTUtil.getCurrentServerTick()); + return memory; + } + + public @NotNull Object2FloatMap getSum() { + Object2FloatMap sum = new Object2FloatArrayMap<>(); + for (Object2FloatMap list : getMemory().values()) { + for (var entry : list.object2FloatEntrySet()) { + sum.put(entry.getKey(), sum.getFloat(entry.getKey()) + entry.getFloatValue()); + } + } + return sum; + } + + public @NotNull Object2FloatMap getFlow(long tick) { + updateMemory(tick); + return memory.getOrDefault(tick, Object2FloatMaps.emptyMap()); + } + + public void recordFlow(long tick, @NotNull MedicalCondition condition, float amount) { + recordFlow(tick, new DuctTestObject(condition), amount); + } + + public void recordFlow(long tick, @NotNull DuctTestObject testObject, float amount) { + updateMemory(tick); + Object2FloatMap map = memory.computeIfAbsent(tick, k -> new Object2FloatArrayMap<>()); + map.put(testObject, map.getFloat(testObject) + amount); + last = testObject.recombine(amount); + } + + private void updateMemory(long tick) { + var iter = memory.long2ObjectEntrySet().fastIterator(); + while (iter.hasNext()) { + var entry = iter.next(); + if (entry.getLongKey() + MEMORY_TICKS < tick) { + iter.remove(); + } + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/duct/DuctGroupData.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/duct/DuctGroupData.java new file mode 100644 index 0000000000..e667cc07b2 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/duct/DuctGroupData.java @@ -0,0 +1,47 @@ +package com.gregtechceu.gtceu.common.pipelike.net.duct; + +import com.gregtechceu.gtceu.api.graphnet.group.PathCacheGroupData; +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; +import com.gregtechceu.gtceu.api.graphnet.path.NetPath; +import com.gregtechceu.gtceu.api.graphnet.path.PathBuilder; +import com.gregtechceu.gtceu.api.graphnet.traverse.NetIteratorSupplier; +import com.gregtechceu.gtceu.common.pipelike.net.energy.StandardEnergyPath; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import org.jetbrains.annotations.NotNull; + +import java.util.Set; + +public class DuctGroupData extends PathCacheGroupData { + + public DuctGroupData(NetIteratorSupplier iteratorSupplier) { + super(iteratorSupplier); + } + + public DuctGroupData(NetIteratorSupplier iteratorSupplier, + @NotNull Object2ObjectOpenHashMap cache) { + super(iteratorSupplier, cache); + } + + @Override + protected PathBuilder createBuilder(@NotNull NetNode origin) { + return new StandardEnergyPath.Builder(origin); + } + + @Override + protected NetPath buildSingleton(@NotNull NetNode singleton) { + return new StandardEnergyPath.SingletonEnergyPath(singleton); + } + + @Override + protected @NotNull PathCacheGroupData buildFilteredCache(@NotNull Set filterNodes) { + Object2ObjectOpenHashMap child = new Object2ObjectOpenHashMap<>(this.cache); + child.entrySet().removeIf(entry -> { + if (!filterNodes.contains(entry.getKey())) return true; + SecondaryCache cache = entry.getValue(); + cache.keySet().retainAll(filterNodes); + return cache.isEmpty(); + }); + return new DuctGroupData(iteratorSupplier, child); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/duct/DuctPath.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/duct/DuctPath.java new file mode 100644 index 0000000000..e5b2938f79 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/duct/DuctPath.java @@ -0,0 +1,31 @@ +package com.gregtechceu.gtceu.common.pipelike.net.duct; + +import com.gregtechceu.gtceu.api.data.medicalcondition.MedicalCondition; +import com.gregtechceu.gtceu.api.graphnet.path.NetPath; + +import org.jetbrains.annotations.NotNull; + +public interface DuctPath extends NetPath { + + @NotNull + PathFlowReport traverse(MedicalCondition condition, float differenceAmount); + + interface PathFlowReport { + + /** + * @return the total voltage that was allowed through the path + */ + MedicalCondition conditionOut(); + + /** + * @return the total amperage that was allowed through the path + */ + float amountOut(); + + /** + * Called when this flow report should stop being simulated; + * e.g. flow should be reported and heating should occur. + */ + void report(); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/duct/DuctTestObject.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/duct/DuctTestObject.java new file mode 100644 index 0000000000..7a37da410f --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/duct/DuctTestObject.java @@ -0,0 +1,53 @@ +package com.gregtechceu.gtceu.common.pipelike.net.duct; + +import com.gregtechceu.gtceu.api.data.medicalcondition.HazardStack; +import com.gregtechceu.gtceu.api.data.medicalcondition.MedicalCondition; +import com.gregtechceu.gtceu.api.graphnet.predicate.test.IPredicateTestObject; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; +import java.util.function.Predicate; + +public final class DuctTestObject implements IPredicateTestObject, Predicate { + + public final MedicalCondition condition; + + private final int hash; + + public DuctTestObject(@NotNull MedicalCondition condition) { + this.condition = condition; + this.hash = this.condition.hashCode(); + } + + @Override + @Contract(" -> new") + public @NotNull HazardStack recombine() { + return new HazardStack(condition, 1); + } + + @Contract("_ -> new") + public @NotNull HazardStack recombine(float amount) { + return new HazardStack(condition, amount); + } + + @Override + public boolean test(@Nullable MedicalCondition c) { + return c != null && c == condition; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DuctTestObject that = (DuctTestObject) o; + return Objects.equals(condition, that.condition); + } + + @Override + public int hashCode() { + return hash; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/duct/DuctThroughputLogic.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/duct/DuctThroughputLogic.java new file mode 100644 index 0000000000..4e585af9c1 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/duct/DuctThroughputLogic.java @@ -0,0 +1,25 @@ +package com.gregtechceu.gtceu.common.pipelike.net.duct; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.graphnet.logic.AbstractFloatLogicData; +import com.gregtechceu.gtceu.api.graphnet.logic.NetLogicEntry; + +import org.jetbrains.annotations.NotNull; + +public final class DuctThroughputLogic extends AbstractFloatLogicData { + + public static final FloatLogicType TYPE = new FloatLogicType<>(GTCEu.MOD_ID, "DuctThroughput", + DuctThroughputLogic::new, new DuctThroughputLogic()); + + @Override + public @NotNull FloatLogicType getType() { + return TYPE; + } + + @Override + public DuctThroughputLogic union(NetLogicEntry other) { + if (other instanceof DuctThroughputLogic l) { + return this.getValue() < l.getValue() ? this : l; + } else return this; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/duct/StandardDuctPath.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/duct/StandardDuctPath.java new file mode 100644 index 0000000000..8213e94a8f --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/duct/StandardDuctPath.java @@ -0,0 +1,184 @@ +package com.gregtechceu.gtceu.common.pipelike.net.duct; + +import com.gregtechceu.gtceu.api.data.medicalcondition.MedicalCondition; +import com.gregtechceu.gtceu.api.graphnet.logic.NetLogicData; +import com.gregtechceu.gtceu.api.graphnet.logic.WeightFactorLogic; +import com.gregtechceu.gtceu.api.graphnet.net.NetEdge; +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; +import com.gregtechceu.gtceu.api.graphnet.path.PathBuilder; +import com.gregtechceu.gtceu.api.graphnet.path.SingletonNetPath; +import com.gregtechceu.gtceu.api.graphnet.path.StandardNetPath; +import com.gregtechceu.gtceu.api.graphnet.predicate.test.IPredicateTestObject; +import com.gregtechceu.gtceu.common.pipelike.net.energy.*; + +import com.google.common.collect.ImmutableCollection; +import com.google.common.collect.ImmutableSet; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.List; + +public class StandardDuctPath extends StandardNetPath implements DuctPath { + + public StandardDuctPath(@NotNull ImmutableCollection nodes, @NotNull ImmutableCollection edges, + double weight) { + super(nodes, edges, weight); + } + + public StandardDuctPath(@NotNull StandardDuctPath reverse) { + super(reverse); + } + + @NotNull + @Override + public DuctPath.PathFlowReport traverse(final MedicalCondition condition, float differenceAmount) { + if (differenceAmount <= 0) return EMPTY; + for (NetEdge edge : getOrderedEdges()) { + if (!edge.test(IPredicateTestObject.INSTANCE)) return EMPTY; + } + return new StandardReport(condition, differenceAmount); + } + + @Override + public @NotNull StandardDuctPath reversed() { + if (reversed == null) { + reversed = new StandardDuctPath(this); + } + return (StandardDuctPath) reversed; + } + + public static final class Builder implements PathBuilder { + + public final List nodes = new ObjectArrayList<>(); + public final List edges = new ObjectArrayList<>(); + + public Builder(@NotNull NetNode startingNode) { + nodes.add(startingNode); + handleAdditionalInfo(startingNode); + } + + private void handleAdditionalInfo(@NotNull NetNode node) {} + + @Override + @Contract("_, _ -> this") + public Builder addToEnd(@NotNull NetNode node, @NotNull NetEdge edge) { + NetNode end = nodes.get(nodes.size() - 1); + if (edge.getOppositeNode(node) != end) + throw new IllegalArgumentException("Edge does not link last node and new node!"); + nodes.add(node); + handleAdditionalInfo(node); + edges.add(edge); + return this; + } + + @Override + @Contract("_, _ -> this") + public Builder addToStart(@NotNull NetNode node, @NotNull NetEdge edge) { + NetNode end = nodes.get(0); + if (edge.getOppositeNode(node) != end) + throw new IllegalArgumentException("Edge does not link last node and new node!"); + nodes.add(0, node); + handleAdditionalInfo(node); + edges.add(0, edge); + return this; + } + + @Override + @Contract("-> this") + public Builder reverse() { + Collections.reverse(nodes); + Collections.reverse(edges); + return this; + } + + @Override + public StandardDuctPath build() { + double sum = 0.0; + for (NetEdge edge : edges) { + double edgeWeight = edge.getWeight(); + sum += edgeWeight; + } + return new StandardDuctPath(ImmutableSet.copyOf(nodes), ImmutableSet.copyOf(edges), sum); + } + } + + public static class SingletonDuctPath extends SingletonNetPath implements DuctPath { + + protected final long voltageLimit; + + protected final long loss; + + protected final long amperageLimit; + + public SingletonDuctPath(NetNode node) { + this(node, node.getData().getLogicEntryDefaultable(WeightFactorLogic.TYPE).getValue()); + } + + public SingletonDuctPath(NetNode node, double weight) { + super(node, weight); + NetLogicData data = node.getData(); + this.voltageLimit = data.getLogicEntryDefaultable(VoltageLimitLogic.TYPE).getValue(); + this.loss = (long) Math.ceil(data.getLogicEntryDefaultable(VoltageLossLogic.TYPE).getValue()); + this.amperageLimit = data.getLogicEntryDefaultable(AmperageLimitLogic.TYPE).getValue(); + } + + @Override + public @NotNull PathFlowReport traverse(final MedicalCondition condition, float amount) { + return new StandardReport(condition, amount); + } + } + + protected static final DuctPath.PathFlowReport EMPTY = new DuctPath.PathFlowReport() { + + @Override + public MedicalCondition conditionOut() { + return null; + } + + @Override + public float amountOut() { + return 0; + } + + @Override + public void report() {} + }; + + public static final class StandardReport implements PathFlowReport { + + private final MedicalCondition condition; + private final float amount; + private final Runnable[] report; + + public StandardReport(MedicalCondition condition, float amount, @NotNull Runnable @NotNull... report) { + this.condition = condition; + this.amount = amount; + this.report = report; + } + + public StandardReport(MedicalCondition condition, float amount, @NotNull List<@NotNull Runnable> report) { + this.condition = condition; + this.amount = amount; + this.report = report.toArray(Runnable[]::new); + } + + @Override + public MedicalCondition conditionOut() { + return condition; + } + + @Override + public float amountOut() { + return amount; + } + + @Override + public void report() { + for (Runnable runnable : report) { + runnable.run(); + } + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/duct/WorldDuctNet.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/duct/WorldDuctNet.java new file mode 100644 index 0000000000..251656ea9e --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/duct/WorldDuctNet.java @@ -0,0 +1,55 @@ +package com.gregtechceu.gtceu.common.pipelike.net.duct; + +import com.gregtechceu.gtceu.api.capability.forge.GTCapability; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNet; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IPipeCapabilityObject; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.NodeManagingPCW; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeCapabilityWrapper; + +import net.minecraft.server.level.ServerLevel; +import net.minecraftforge.common.capabilities.Capability; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import org.jetbrains.annotations.NotNull; + +public class WorldDuctNet extends WorldPipeNet { + + private static final String DATA_ID = "gtceu_world_duct_net"; + + public static WorldDuctNet getWorldNet(ServerLevel serverLevel) { + WorldDuctNet net = serverLevel.getDataStorage().computeIfAbsent(tag -> { + WorldDuctNet netx = new WorldDuctNet(); + netx.load(tag); + return netx; + }, WorldDuctNet::new, DATA_ID); + net.setLevel(serverLevel); + return net; + } + + public WorldDuctNet() { + super(false); + } + + @Override + public PipeCapabilityWrapper buildCapabilityWrapper(@NotNull PipeBlockEntity owner, @NotNull WorldPipeNode node) { + Object2ObjectOpenHashMap, IPipeCapabilityObject> map = new Object2ObjectOpenHashMap<>(); + map.put(GTCapability.CAPABILITY_HAZARD_CONTAINER, new DuctCapabilityObject(node)); + return new NodeManagingPCW(owner, node, map, 0, DuctCapabilityObject.ACTIVE_KEY); + } + + public static int getBufferTicks() { + return 10; + } + + @Override + public int getNetworkID() { + return 5; + } + + @Override + public void setDirty() { + super.setDirty(); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/energy/AmperageLimitLogic.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/energy/AmperageLimitLogic.java new file mode 100644 index 0000000000..4c80bb41db --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/energy/AmperageLimitLogic.java @@ -0,0 +1,25 @@ +package com.gregtechceu.gtceu.common.pipelike.net.energy; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.graphnet.logic.AbstractLongLogicData; +import com.gregtechceu.gtceu.api.graphnet.logic.NetLogicEntry; + +import org.jetbrains.annotations.NotNull; + +public final class AmperageLimitLogic extends AbstractLongLogicData { + + public static final LongLogicType TYPE = new LongLogicType<>(GTCEu.MOD_ID, "AmperageLimit", + AmperageLimitLogic::new, new AmperageLimitLogic()); + + @Override + public @NotNull LongLogicType getType() { + return TYPE; + } + + @Override + public AmperageLimitLogic union(NetLogicEntry other) { + if (other instanceof AmperageLimitLogic l) { + return this.getValue() < l.getValue() ? this : l; + } else return this; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/cable/AveragingPerTickCounter.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/energy/AveragingPerTickCounter.java similarity index 78% rename from src/main/java/com/gregtechceu/gtceu/common/pipelike/cable/AveragingPerTickCounter.java rename to src/main/java/com/gregtechceu/gtceu/common/pipelike/net/energy/AveragingPerTickCounter.java index 41fac33087..888512749e 100644 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/cable/AveragingPerTickCounter.java +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/energy/AveragingPerTickCounter.java @@ -1,6 +1,4 @@ -package com.gregtechceu.gtceu.common.pipelike.cable; - -import net.minecraft.world.level.Level; +package com.gregtechceu.gtceu.common.pipelike.net.energy; import java.util.Arrays; @@ -29,9 +27,7 @@ public AveragingPerTickCounter(long defaultValue, int length) { Arrays.fill(values, defaultValue); } - private void checkValueState(Level world) { - if (world == null) return; - long currentWorldTime = world.getGameTime(); + private void checkValueState(long currentWorldTime) { if (currentWorldTime != lastUpdatedWorldTime) { long dif = currentWorldTime - lastUpdatedWorldTime; if (dif >= values.length || dif < 0) { @@ -53,16 +49,16 @@ private void checkValueState(Level world) { /** * @return the value from the current tick */ - public long getLast(Level world) { - checkValueState(world); + public long getLast(long currentTick) { + checkValueState(currentTick); return values[currentIndex]; } /** * @return the average of all values */ - public double getAverage(Level world) { - checkValueState(world); + public double getAverage(long currentTick) { + checkValueState(currentTick); if (!dirty) return lastAverage; dirty = false; @@ -72,16 +68,16 @@ public double getAverage(Level world) { /** * @param value the value to increment the current value by */ - public void increment(Level world, long value) { - checkValueState(world); + public void increment(long currentTick, long value) { + checkValueState(currentTick); values[currentIndex] += value; } /** * @param value the value to set current value to */ - public void set(Level world, long value) { - checkValueState(world); + public void set(long currentTick, long value) { + checkValueState(currentTick); values[currentIndex] = value; } } diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/energy/EnergyCapabilityObject.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/energy/EnergyCapabilityObject.java new file mode 100644 index 0000000000..eed1b1443d --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/energy/EnergyCapabilityObject.java @@ -0,0 +1,173 @@ +package com.gregtechceu.gtceu.common.pipelike.net.energy; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.capability.IEnergyContainer; +import com.gregtechceu.gtceu.api.capability.forge.GTCapability; +import com.gregtechceu.gtceu.api.graphnet.group.GroupData; +import com.gregtechceu.gtceu.api.graphnet.group.NetGroup; +import com.gregtechceu.gtceu.api.graphnet.group.PathCacheGroupData; +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IPipeCapabilityObject; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeCapabilityWrapper; +import com.gregtechceu.gtceu.common.cover.ShutterCover; +import com.gregtechceu.gtceu.utils.GTUtil; + +import net.minecraft.core.Direction; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.util.LazyOptional; + +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class EnergyCapabilityObject implements IPipeCapabilityObject, IEnergyContainer { + + public static final int ACTIVE_KEY = 122; + + private @Nullable PipeBlockEntity blockEntity; + + private final @NotNull WorldPipeNode node; + + private boolean transferring = false; + + public EnergyCapabilityObject(@NotNull WorldPipeNode node) { + this.node = node; + } + + private boolean inputDisallowed(Direction side) { + if (side == null) return false; + if (blockEntity == null) return true; + else return blockEntity.isBlocked(side); + } + + @Override + public long acceptEnergyFromNetwork(Direction side, long voltage, long amperage, boolean simulate) { + if (blockEntity == null || this.transferring || inputDisallowed(side)) return 0; + NetGroup group = node.getGroupSafe(); + if (!(group.getData() instanceof EnergyGroupData data)) return 0; + + this.transferring = true; + + PathCacheGroupData.SecondaryCache cache = data.getOrCreate(node); + List paths = new ObjectArrayList<>(group.getNodesUnderKey(ACTIVE_KEY).size()); + for (NetNode dest : group.getNodesUnderKey(ACTIVE_KEY)) { + EnergyPath path = (EnergyPath) cache.getOrCompute(dest); + if (path == null) continue; + // construct the path list in order of ascending weight + int i = 0; + while (i < paths.size()) { + if (paths.get(i).getWeight() >= path.getWeight()) break; + else i++; + } + paths.add(i, path); + } + long available = amperage; + for (EnergyPath path : paths) { + NetNode target = path.getTargetNode(); + if (!(target instanceof WorldPipeNode n)) continue; + for (var capability : n.getBlockEntity().getTargetsWithCapabilities(n).entrySet()) { + if (n == node && capability.getKey() == side) continue; // anti insert-to-our-source logic + + IEnergyContainer container = capability.getValue().getCapability( + GTCapability.CAPABILITY_ENERGY_CONTAINER, capability.getKey().getOpposite()) + .resolve().orElse(null); + if (container != null && !(n.getBlockEntity().getCoverHolder() + .getCoverAtSide(capability.getKey()) instanceof ShutterCover)) { + long allowed = container.acceptEnergyFromNetwork(capability.getKey(), voltage, amperage, true); + EnergyPath.PathFlowReport flow = path.traverse(voltage, allowed); + if (flow.euOut() > 0) { + available -= allowed; + if (!simulate) { + flow.report(); + container.acceptEnergyFromNetwork(capability.getKey(), flow.voltageOut(), + flow.amperageOut(), false); + } + } + } + } + } + + this.transferring = false; + return amperage - available; + } + + @Nullable + private EnergyGroupData getGroupData() { + NetGroup group = node.getGroupUnsafe(); + if (group == null) return null; + GroupData data = group.getData(); + if (!(data instanceof EnergyGroupData e)) return null; + return e; + } + + @Override + public long getInputAmperage() { + return node.getData().getLogicEntryDefaultable(AmperageLimitLogic.TYPE).getValue(); + } + + @Override + public long getInputVoltage() { + return node.getData().getLogicEntryDefaultable(VoltageLimitLogic.TYPE).getValue(); + } + + @Override + public void init(@NotNull PipeBlockEntity tile, @NotNull PipeCapabilityWrapper wrapper) { + this.blockEntity = tile; + } + + @Override + public @NotNull LazyOptional getCapability(Capability capability, @Nullable Direction facing) { + return GTCapability.CAPABILITY_ENERGY_CONTAINER.orEmpty(capability, LazyOptional.of(() -> this)); + } + + @Override + public long getInputPerSec() { + EnergyGroupData data = getGroupData(); + if (data == null) return 0; + else return data.getEnergyInPerSec(GTUtil.getCurrentServerTick()); + } + + @Override + public long getOutputPerSec() { + EnergyGroupData data = getGroupData(); + if (data == null) return 0; + else return data.getEnergyOutPerSec(GTUtil.getCurrentServerTick()); + } + + @Override + public boolean inputsEnergy(Direction side) { + return !inputDisallowed(side); + } + + @Override + public boolean outputsEnergy(Direction side) { + return true; + } + + @Override + public long changeEnergy(long differenceAmount) { + GTCEu.LOGGER.error("Do not use changeEnergy() for cables! Use acceptEnergyFromNetwork()"); + return acceptEnergyFromNetwork(null, + differenceAmount / getInputAmperage(), + differenceAmount / getInputVoltage(), false) * getInputVoltage(); + } + + @Override + public long getEnergyStored() { + return 0; + } + + @Override + public long getEnergyCapacity() { + return getInputAmperage() * getInputVoltage(); + } + + @Override + public boolean isOneProbeHidden() { + return true; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/energy/EnergyFlowData.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/energy/EnergyFlowData.java new file mode 100644 index 0000000000..01105b2e99 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/energy/EnergyFlowData.java @@ -0,0 +1,8 @@ +package com.gregtechceu.gtceu.common.pipelike.net.energy; + +public record EnergyFlowData(long amperage, long voltage) { + + public long getEU() { + return amperage * voltage; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/energy/EnergyFlowLogic.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/energy/EnergyFlowLogic.java new file mode 100644 index 0000000000..b160ba9794 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/energy/EnergyFlowLogic.java @@ -0,0 +1,68 @@ +package com.gregtechceu.gtceu.common.pipelike.net.energy; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.graphnet.logic.AbstractTransientLogicData; +import com.gregtechceu.gtceu.api.graphnet.logic.NetLogicType; + +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.List; + +public class EnergyFlowLogic extends AbstractTransientLogicData { + + public static final NetLogicType TYPE = new NetLogicType<>(GTCEu.MOD_ID, "EnergyFlow", + EnergyFlowLogic::new, new EnergyFlowLogic()); + + private final AveragingPerTickCounter averageVoltageCounter = new AveragingPerTickCounter(); + private final AveragingPerTickCounter averageAmperageCounter = new AveragingPerTickCounter(); + + public static final int MEMORY_TICKS = 10; + + @NotNull + private final Long2ObjectOpenHashMap> memory = new Long2ObjectOpenHashMap<>(); + + @Override + public @NotNull NetLogicType getType() { + return TYPE; + } + + public @NotNull Long2ObjectOpenHashMap> getMemory() { + updateMemory(GTCEu.getMinecraftServer().getTickCount()); + return memory; + } + + public @NotNull List getFlow(long tick) { + updateMemory(tick); + return memory.getOrDefault(tick, Collections.emptyList()); + } + + public void recordFlow(long tick, EnergyFlowData flow) { + averageVoltageCounter.increment(tick, flow.getEU()); + averageAmperageCounter.increment(tick, flow.amperage()); + + updateMemory(tick); + memory.computeIfAbsent(tick, k -> new ObjectArrayList<>()).add(flow); + } + + private void updateMemory(long tick) { + var iter = memory.long2ObjectEntrySet().fastIterator(); + while (iter.hasNext()) { + Long2ObjectMap.Entry> entry = iter.next(); + if (entry.getLongKey() + MEMORY_TICKS < tick) { + iter.remove(); + } + } + } + + public double getAverageAmperage(long currentTick) { + return averageAmperageCounter.getAverage(currentTick); + } + + public double getAverageVoltage(long currentTick) { + return averageVoltageCounter.getAverage(currentTick); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/energy/EnergyGroupData.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/energy/EnergyGroupData.java new file mode 100644 index 0000000000..74c8c1285b --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/energy/EnergyGroupData.java @@ -0,0 +1,86 @@ +package com.gregtechceu.gtceu.common.pipelike.net.energy; + +import com.gregtechceu.gtceu.api.graphnet.group.PathCacheGroupData; +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; +import com.gregtechceu.gtceu.api.graphnet.path.NetPath; +import com.gregtechceu.gtceu.api.graphnet.path.PathBuilder; +import com.gregtechceu.gtceu.api.graphnet.traverse.NetIteratorSupplier; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import org.jetbrains.annotations.NotNull; + +import java.util.Set; + +public class EnergyGroupData extends PathCacheGroupData { + + private long lastEnergyInPerSec; + private long lastEnergyOutPerSec; + private long energyInPerSec; + private long energyOutPerSec; + private long updateTime; + + public EnergyGroupData(NetIteratorSupplier iteratorSupplier) { + super(iteratorSupplier); + } + + public EnergyGroupData(NetIteratorSupplier iteratorSupplier, + @NotNull Object2ObjectOpenHashMap cache) { + super(iteratorSupplier, cache); + } + + public long getEnergyInPerSec(long queryTick) { + updateCache(queryTick); + return lastEnergyInPerSec; + } + + public long getEnergyOutPerSec(long queryTick) { + updateCache(queryTick); + return lastEnergyOutPerSec; + } + + public void addEnergyInPerSec(long energy, long queryTick) { + updateCache(queryTick); + energyInPerSec += energy; + } + + public void addEnergyOutPerSec(long energy, long queryTick) { + updateCache(queryTick); + energyOutPerSec += energy; + } + + private void updateCache(long queryTick) { + if (queryTick > updateTime) { + updateTime = updateTime + 20; + clearCache(); + } + } + + public void clearCache() { + lastEnergyInPerSec = energyInPerSec; + lastEnergyOutPerSec = energyOutPerSec; + energyInPerSec = 0; + energyOutPerSec = 0; + } + + @Override + protected PathBuilder createBuilder(@NotNull NetNode origin) { + return new StandardEnergyPath.Builder(origin); + } + + @Override + protected NetPath buildSingleton(@NotNull NetNode singleton) { + return new StandardEnergyPath.SingletonEnergyPath(singleton); + } + + @Override + protected @NotNull PathCacheGroupData buildFilteredCache(@NotNull Set filterNodes) { + Object2ObjectOpenHashMap child = new Object2ObjectOpenHashMap<>(this.cache); + child.entrySet().removeIf(entry -> { + if (!filterNodes.contains(entry.getKey())) return true; + SecondaryCache cache = entry.getValue(); + cache.keySet().retainAll(filterNodes); + return cache.isEmpty(); + }); + return new EnergyGroupData(iteratorSupplier, child); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/energy/EnergyPath.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/energy/EnergyPath.java new file mode 100644 index 0000000000..d34302f83f --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/energy/EnergyPath.java @@ -0,0 +1,44 @@ +package com.gregtechceu.gtceu.common.pipelike.net.energy; + +import com.gregtechceu.gtceu.api.graphnet.path.NetPath; + +import org.jetbrains.annotations.NotNull; + +public interface EnergyPath extends NetPath { + + /** + * Does the calculations to traverse the path. + * + * @param voltage the input voltage. + * @param amperage the input amperage. + * @return the flow report for the traversal. + */ + @NotNull + PathFlowReport traverse(long voltage, long amperage); + + interface PathFlowReport { + + /** + * @return the total voltage that was allowed through the path + */ + long voltageOut(); + + /** + * @return the total amperage that was allowed through the path + */ + long amperageOut(); + + /** + * @return the total EU that was allowed through the path + */ + default long euOut() { + return voltageOut() * amperageOut(); + } + + /** + * Called when this flow report should stop being simulated; + * e.g. flow should be reported and heating should occur. + */ + void report(); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/energy/StandardEnergyPath.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/energy/StandardEnergyPath.java new file mode 100644 index 0000000000..d3b7448413 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/energy/StandardEnergyPath.java @@ -0,0 +1,324 @@ +package com.gregtechceu.gtceu.common.pipelike.net.energy; + +import com.gregtechceu.gtceu.api.graphnet.group.NetGroup; +import com.gregtechceu.gtceu.api.graphnet.logic.NetLogicData; +import com.gregtechceu.gtceu.api.graphnet.logic.WeightFactorLogic; +import com.gregtechceu.gtceu.api.graphnet.net.NetEdge; +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; +import com.gregtechceu.gtceu.api.graphnet.path.PathBuilder; +import com.gregtechceu.gtceu.api.graphnet.path.SingletonNetPath; +import com.gregtechceu.gtceu.api.graphnet.path.StandardNetPath; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.logic.TemperatureLogic; +import com.gregtechceu.gtceu.api.graphnet.predicate.test.IPredicateTestObject; +import com.gregtechceu.gtceu.utils.GTUtil; + +import com.google.common.collect.ImmutableCollection; +import com.google.common.collect.ImmutableSet; +import it.unimi.dsi.fastutil.longs.Long2ObjectAVLTreeMap; +import it.unimi.dsi.fastutil.longs.LongComparator; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.List; +import java.util.Set; + +public class StandardEnergyPath extends StandardNetPath implements EnergyPath { + + // reverse of natural order so that larger longs come first in iteration order. + public static final LongComparator voltageLimitComparator = new LongComparator() { + + @Override + public int compare(long k1, long k2) { + return Long.compare(k2, k1); + } + + @Override + public int compare(Long o1, Long o2) { + return Long.compare(o2, o1); + } + }; + + protected final @NotNull Long2ObjectAVLTreeMap> voltageLimitInfo; + + protected final long loss; + + public StandardEnergyPath(@NotNull ImmutableCollection nodes, @NotNull ImmutableCollection edges, + double weight, @NotNull Long2ObjectAVLTreeMap> voltageLimitInfo, long loss) { + super(nodes, edges, weight); + this.voltageLimitInfo = voltageLimitInfo; + this.loss = loss; + } + + public StandardEnergyPath(@NotNull StandardEnergyPath reverse) { + super(reverse); + this.voltageLimitInfo = reverse.voltageLimitInfo; + this.loss = reverse.loss; + } + + @NotNull + @Override + public PathFlowReport traverse(final long voltage, final long amperage) { + long resultVoltage = voltage - loss; + if (resultVoltage <= 0) return EMPTY; + for (NetEdge edge : getOrderedEdges()) { + if (!edge.test(IPredicateTestObject.INSTANCE)) return EMPTY; + } + + var set = voltageLimitInfo.tailMap(resultVoltage).long2ObjectEntrySet(); + for (var entry : set) { + long key = entry.getLongKey(); + if (key >= resultVoltage) continue; + // move 90% of the way towards the limiting voltage for every node with this limit + int count = entry.getValue().size(); + resultVoltage = (long) (key + (resultVoltage - key) * Math.pow(0.1, count)); + } + long tick = GTUtil.getCurrentServerTick(); + long resultAmperage = amperage; + List postActions = new ObjectArrayList<>(); + for (NetNode node : getOrderedNodes()) { + NetLogicData data = node.getData(); + EnergyFlowLogic energyFlow = data.getLogicEntryNullable(EnergyFlowLogic.TYPE); + if (energyFlow == null) { + energyFlow = new EnergyFlowLogic(); + data.setLogicEntry(energyFlow); + } + long sum = 0L; + for (EnergyFlowData energyFlowData : energyFlow.getFlow(tick)) { + long amperaged = energyFlowData.amperage(); + sum += amperaged; + } + long correctedAmperage = Math.min(data.getLogicEntryDefaultable(AmperageLimitLogic.TYPE).getValue() - + sum, resultAmperage); + + EnergyFlowLogic finalEnergyFlow = energyFlow; + long finalResultVoltage = resultVoltage; + long finalResultAmperage = resultAmperage; + postActions.add(() -> { + TemperatureLogic tempLogic = data.getLogicEntryNullable(TemperatureLogic.TYPE); + if (tempLogic != null) { + long endVoltage = Math.min(voltage, + data.getLogicEntryDefaultable(VoltageLimitLogic.TYPE).getValue()); + float heat = (float) computeHeat(voltage, endVoltage, finalResultAmperage, correctedAmperage); + if (heat > 0) tempLogic.applyThermalEnergy(heat, tick); + if (node instanceof WorldPipeNode n) { + tempLogic.defaultHandleTemperature(n.getNet().getLevel(), n.getEquivalencyData()); + } + } + finalEnergyFlow.recordFlow(tick, new EnergyFlowData(correctedAmperage, finalResultVoltage)); + }); + resultAmperage = correctedAmperage; + } + long finalResultVoltage = resultVoltage; + long finalResultAmperage = resultAmperage; + postActions.add(() -> { + NetGroup group = getSourceNode().getGroupUnsafe(); + if (group != null && group.getData() instanceof EnergyGroupData data) { + data.addEnergyInPerSec(voltage * amperage, tick); + data.addEnergyOutPerSec(finalResultVoltage * finalResultAmperage, tick); + } + }); + return new StandardReport(resultAmperage, resultVoltage, postActions); + } + + public static double computeHeat(long startVoltage, long endVoltage, long startAmperage, long endAmperage) { + return Math.pow((double) startVoltage * startAmperage - (double) endVoltage * endAmperage, 0.6); + } + + @Override + public @NotNull StandardEnergyPath reversed() { + if (reversed == null) { + reversed = new StandardEnergyPath(this); + } + return (StandardEnergyPath) reversed; + } + + public static final class Builder implements PathBuilder { + + public final List nodes = new ObjectArrayList<>(); + public final List edges = new ObjectArrayList<>(); + public final Long2ObjectAVLTreeMap> voltageLimitInfo = new Long2ObjectAVLTreeMap<>( + voltageLimitComparator); + public double loss = 0; + + public Builder(@NotNull NetNode startingNode) { + nodes.add(startingNode); + handleAdditionalInfo(startingNode); + } + + private void handleAdditionalInfo(@NotNull NetNode node) { + long value = node.getData().getLogicEntryDefaultable(VoltageLimitLogic.TYPE).getValue(); + Set set = voltageLimitInfo.get(value); + if (set == null) { + set = new ObjectOpenHashSet<>(); + voltageLimitInfo.put(value, set); + } + set.add(node); + loss += node.getData().getLogicEntryDefaultable(VoltageLossLogic.TYPE).getValue(); + } + + @Override + @Contract("_, _ -> this") + public Builder addToEnd(@NotNull NetNode node, @NotNull NetEdge edge) { + NetNode end = nodes.get(nodes.size() - 1); + if (edge.getOppositeNode(node) != end) + throw new IllegalArgumentException("Edge does not link last node and new node!"); + nodes.add(node); + handleAdditionalInfo(node); + edges.add(edge); + return this; + } + + @Override + @Contract("_, _ -> this") + public Builder addToStart(@NotNull NetNode node, @NotNull NetEdge edge) { + NetNode end = nodes.get(0); + if (edge.getOppositeNode(node) != end) + throw new IllegalArgumentException("Edge does not link last node and new node!"); + nodes.add(0, node); + handleAdditionalInfo(node); + edges.add(0, edge); + return this; + } + + @Override + @Contract("-> this") + public Builder reverse() { + Collections.reverse(nodes); + Collections.reverse(edges); + return this; + } + + @Override + public StandardEnergyPath build() { + double sum = 0.0; + for (NetEdge edge : edges) { + double edgeWeight = edge.getWeight(); + sum += edgeWeight; + } + return new StandardEnergyPath(ImmutableSet.copyOf(nodes), ImmutableSet.copyOf(edges), + sum, voltageLimitInfo, (long) Math.ceil(loss)); + } + } + + public static class SingletonEnergyPath extends SingletonNetPath implements EnergyPath { + + protected final long voltageLimit; + + protected final long loss; + + protected final long amperageLimit; + + public SingletonEnergyPath(NetNode node) { + this(node, node.getData().getLogicEntryDefaultable(WeightFactorLogic.TYPE).getValue()); + } + + public SingletonEnergyPath(NetNode node, double weight) { + super(node, weight); + NetLogicData data = node.getData(); + this.voltageLimit = data.getLogicEntryDefaultable(VoltageLimitLogic.TYPE).getValue(); + this.loss = (long) Math.ceil(data.getLogicEntryDefaultable(VoltageLossLogic.TYPE).getValue()); + this.amperageLimit = data.getLogicEntryDefaultable(AmperageLimitLogic.TYPE).getValue(); + } + + @Override + public @NotNull EnergyPath.PathFlowReport traverse(long voltage, long amperage) { + long resultVoltage = voltage - loss; + if (resultVoltage <= 0) return EMPTY; + else if (resultVoltage > voltageLimit) { + resultVoltage = (long) (voltageLimit + (resultVoltage - voltageLimit) * 0.1); + } + + NetLogicData data = node.getData(); + EnergyFlowLogic energyFlow = data.getLogicEntryNullable(EnergyFlowLogic.TYPE); + if (energyFlow == null) { + energyFlow = new EnergyFlowLogic(); + data.setLogicEntry(energyFlow); + } + long tick = GTUtil.getCurrentServerTick(); + long sum = 0L; + for (EnergyFlowData energyFlowData : energyFlow.getFlow(tick)) { + long amperaged = energyFlowData.amperage(); + sum += amperaged; + } + long resultAmperage = Math.min(amperageLimit - sum, amperage); + + EnergyFlowLogic finalEnergyFlow = energyFlow; + long finalResultVoltage = resultVoltage; + return new StandardReport(resultAmperage, resultVoltage, () -> { + TemperatureLogic tempLogic = data.getLogicEntryNullable(TemperatureLogic.TYPE); + if (tempLogic != null) { + long endVoltage = Math.min(voltage, + data.getLogicEntryDefaultable(VoltageLimitLogic.TYPE).getValue()); + float heat = (float) computeHeat(voltage, endVoltage, resultAmperage, resultAmperage); + if (heat > 0) tempLogic.applyThermalEnergy(heat, tick); + if (node instanceof WorldPipeNode n) { + tempLogic.defaultHandleTemperature(n.getNet().getLevel(), n.getEquivalencyData()); + } + } + finalEnergyFlow.recordFlow(tick, new EnergyFlowData(resultAmperage, finalResultVoltage)); + + NetGroup group = getSourceNode().getGroupUnsafe(); + if (group != null && group.getData() instanceof EnergyGroupData gData) { + gData.addEnergyInPerSec(voltage * amperage, tick); + gData.addEnergyOutPerSec(finalResultVoltage * resultAmperage, tick); + } + }); + } + } + + protected static final EnergyPath.PathFlowReport EMPTY = new PathFlowReport() { + + @Override + public long voltageOut() { + return 0; + } + + @Override + public long amperageOut() { + return 0; + } + + @Override + public void report() {} + }; + + public static final class StandardReport implements PathFlowReport { + + private final long amperage; + private final long voltage; + private final Runnable[] report; + + public StandardReport(long amperage, long voltage, @NotNull Runnable @NotNull... report) { + this.amperage = amperage; + this.voltage = voltage; + this.report = report; + } + + public StandardReport(long amperage, long voltage, @NotNull List<@NotNull Runnable> report) { + this.amperage = amperage; + this.voltage = voltage; + this.report = report.toArray(new Runnable[0]); + } + + @Override + public long voltageOut() { + return voltage; + } + + @Override + public long amperageOut() { + return amperage; + } + + @Override + public void report() { + for (Runnable runnable : report) { + runnable.run(); + } + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/energy/SuperconductorLogic.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/energy/SuperconductorLogic.java new file mode 100644 index 0000000000..3231e2b0a1 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/energy/SuperconductorLogic.java @@ -0,0 +1,19 @@ +package com.gregtechceu.gtceu.common.pipelike.net.energy; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.graphnet.logic.AbstractByteLogicData; + +import org.jetbrains.annotations.NotNull; + +public final class SuperconductorLogic extends AbstractByteLogicData { + + public static final SuperconductorLogic INSTANCE = new SuperconductorLogic(); + + public static final ByteLogicType TYPE = new ByteLogicType<>(GTCEu.MOD_ID, "Superconductor", + () -> INSTANCE, INSTANCE); + + @Override + public @NotNull ByteLogicType getType() { + return TYPE; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/energy/VoltageLimitLogic.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/energy/VoltageLimitLogic.java new file mode 100644 index 0000000000..d93c765d93 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/energy/VoltageLimitLogic.java @@ -0,0 +1,25 @@ +package com.gregtechceu.gtceu.common.pipelike.net.energy; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.graphnet.logic.AbstractLongLogicData; +import com.gregtechceu.gtceu.api.graphnet.logic.NetLogicEntry; + +import org.jetbrains.annotations.NotNull; + +public final class VoltageLimitLogic extends AbstractLongLogicData { + + public static final LongLogicType TYPE = new LongLogicType<>(GTCEu.MOD_ID, "VoltageLimit", + VoltageLimitLogic::new, new VoltageLimitLogic()); + + @Override + public @NotNull LongLogicType getType() { + return TYPE; + } + + @Override + public VoltageLimitLogic union(NetLogicEntry other) { + if (other instanceof VoltageLimitLogic l) { + return this.getValue() < l.getValue() ? this : l; + } else return this; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/energy/VoltageLossLogic.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/energy/VoltageLossLogic.java new file mode 100644 index 0000000000..e2cb32c165 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/energy/VoltageLossLogic.java @@ -0,0 +1,25 @@ +package com.gregtechceu.gtceu.common.pipelike.net.energy; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.graphnet.logic.AbstractDoubleLogicData; +import com.gregtechceu.gtceu.api.graphnet.logic.NetLogicEntry; + +import org.jetbrains.annotations.NotNull; + +public final class VoltageLossLogic extends AbstractDoubleLogicData { + + public static final DoubleLogicType TYPE = new DoubleLogicType<>(GTCEu.MOD_ID, "VoltageLoss", + VoltageLossLogic::new, new VoltageLossLogic()); + + @Override + public @NotNull DoubleLogicType getType() { + return TYPE; + } + + @Override + public VoltageLossLogic union(NetLogicEntry other) { + if (other instanceof VoltageLossLogic l) { + return this.getWith(this.getValue() + l.getValue()); + } else return this; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/energy/WorldEnergyNet.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/energy/WorldEnergyNet.java new file mode 100644 index 0000000000..844afede7d --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/energy/WorldEnergyNet.java @@ -0,0 +1,57 @@ +package com.gregtechceu.gtceu.common.pipelike.net.energy; + +import com.gregtechceu.gtceu.api.capability.forge.GTCapability; +import com.gregtechceu.gtceu.api.graphnet.group.GroupData; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNet; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IPipeCapabilityObject; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeCapabilityWrapper; +import com.gregtechceu.gtceu.api.graphnet.traverse.NetClosestIterator; + +import net.minecraft.server.level.ServerLevel; +import net.minecraftforge.common.capabilities.Capability; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import org.jetbrains.annotations.NotNull; + +public final class WorldEnergyNet extends WorldPipeNet { + + private static final String DATA_ID = "gtceu_world_energy_net"; + + public static @NotNull WorldEnergyNet getWorldNet(ServerLevel serverLevel) { + WorldEnergyNet net = serverLevel.getDataStorage().computeIfAbsent(tag -> { + WorldEnergyNet netx = new WorldEnergyNet(); + netx.load(tag); + return netx; + }, WorldEnergyNet::new, DATA_ID); + net.setLevel(serverLevel); + return net; + } + + public WorldEnergyNet() { + super(false); + } + + @Override + public PipeCapabilityWrapper buildCapabilityWrapper(@NotNull PipeBlockEntity owner, @NotNull WorldPipeNode node) { + Object2ObjectOpenHashMap, IPipeCapabilityObject> map = new Object2ObjectOpenHashMap<>(); + map.put(GTCapability.CAPABILITY_ENERGY_CONTAINER, new EnergyCapabilityObject(node)); + return new PipeCapabilityWrapper(owner, node, map, 0, EnergyCapabilityObject.ACTIVE_KEY); + } + + @Override + public GroupData getBlankGroupData() { + return new EnergyGroupData(NetClosestIterator::new); + } + + @Override + public void setDirty() { + super.setDirty(); + } + + @Override + public int getNetworkID() { + return 0; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/fluid/FluidCapabilityObject.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/fluid/FluidCapabilityObject.java new file mode 100644 index 0000000000..20000231c3 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/fluid/FluidCapabilityObject.java @@ -0,0 +1,418 @@ +package com.gregtechceu.gtceu.common.pipelike.net.fluid; + +import com.gregtechceu.gtceu.api.fluids.FluidState; +import com.gregtechceu.gtceu.api.fluids.attribute.FluidAttribute; +import com.gregtechceu.gtceu.api.graphnet.GraphNetUtility; +import com.gregtechceu.gtceu.api.graphnet.logic.ChannelCountLogic; +import com.gregtechceu.gtceu.api.graphnet.logic.ThroughputLogic; +import com.gregtechceu.gtceu.api.graphnet.net.NetEdge; +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.NodeExposingCapabilities; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.logic.TemperatureLogic; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IPipeCapabilityObject; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.IWorldPipeNetTile; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.NodeManagingPCW; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeCapabilityWrapper; +import com.gregtechceu.gtceu.api.graphnet.predicate.test.FluidTestObject; +import com.gregtechceu.gtceu.api.graphnet.traverse.EdgeDirection; +import com.gregtechceu.gtceu.api.graphnet.traverse.EdgeSelector; +import com.gregtechceu.gtceu.api.graphnet.traverse.ResilientNetClosestIterator; +import com.gregtechceu.gtceu.utils.GTMath; +import com.gregtechceu.gtceu.utils.GTUtil; +import com.gregtechceu.gtceu.utils.MapUtil; + +import net.minecraft.core.Direction; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.ForgeCapabilities; +import net.minecraftforge.common.util.LazyOptional; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.capability.IFluidHandler; + +import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2LongMap; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import lombok.Getter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayDeque; +import java.util.EnumMap; +import java.util.List; + +public class FluidCapabilityObject implements IPipeCapabilityObject, IFluidHandler { + + private PipeBlockEntity blockEntity; + private NodeManagingPCW capabilityWrapper; + + private final EnumMap wrappers = new EnumMap<>(Direction.class); + private final WorldPipeNode node; + @Getter + private final int tanks; + + private boolean transferring = false; + + public FluidCapabilityObject(WorldPipeNode node) { + this.node = node; + this.tanks = node.getData().getLogicEntryDefaultable(ChannelCountLogic.TYPE) + .getValue(); + for (Direction facing : GTUtil.DIRECTIONS) { + wrappers.put(facing, new Wrapper(facing)); + } + } + + public WorldPipeNode getNode() { + return node; + } + + @Override + public void init(@NotNull PipeBlockEntity tile, @NotNull PipeCapabilityWrapper wrapper) { + this.blockEntity = tile; + if (!(wrapper instanceof NodeManagingPCW p)) + throw new IllegalArgumentException("FluidCapabilityObjects must be initialized to NodeManagingPCWs!"); + this.capabilityWrapper = p; + } + + private boolean inputDisallowed(Direction side) { + if (side == null) return false; + else return blockEntity.isBlocked(side); + } + + @Override + public @NotNull LazyOptional getCapability(@NotNull Capability cap, @Nullable Direction facing) { + // can't expose the sided capability if there is no node to interact with + if (facing != null && capabilityWrapper.getNodeForFacing(facing) == null) return LazyOptional.empty(); + return ForgeCapabilities.FLUID_HANDLER.orEmpty(cap, + LazyOptional.of(() -> facing == null ? this : wrappers.get(facing))); + } + + protected @Nullable NetNode getRelevantNode(Direction facing) { + return facing == null ? node : capabilityWrapper.getNodeForFacing(facing); + } + + protected int fill(FluidStack resource, FluidAction action, Direction side) { + if (this.transferring || inputDisallowed(side)) return 0; + NetNode node = getRelevantNode(side); + if (node == null) node = this.node; + this.transferring = true; + + int flow = resource.getAmount(); + FluidTestObject testObject = new FluidTestObject(resource); + ResilientNetClosestIterator iter = new ResilientNetClosestIterator(node, + EdgeSelector.filtered(EdgeDirection.OUTGOING, GraphNetUtility.standardEdgeBlacklist(testObject))); + Object2IntOpenHashMap availableDemandCache = new Object2IntOpenHashMap<>(); + Object2IntOpenHashMap flowLimitCache = new Object2IntOpenHashMap<>(); + Object2BooleanOpenHashMap lossyCache = new Object2BooleanOpenHashMap<>(); + List postActions = new ObjectArrayList<>(); + int total = 0; + main: + while (iter.hasNext()) { + if (flow <= 0) break; + final NetNode next = iter.next(); + int limit = Math.min(MapUtil.computeIfAbsent(flowLimitCache, next, n -> getFlowLimit(n, testObject)), flow); + if (limit <= 0) { + iter.markInvalid(next); + continue; + } + int supply = MapUtil.computeIfAbsent(availableDemandCache, next, + n -> getSupplyOrDemand(n, testObject, false)); + if (supply <= 0) continue; + supply = Math.min(supply, limit); + NetEdge span; + NetNode trace = next; + ArrayDeque seen = new ArrayDeque<>(); + seen.add(next); + while ((span = iter.getSpanningTreeEdge(trace)) != null) { + trace = span.getOppositeNode(trace); + if (trace == null) continue main; + int l = MapUtil.computeIfAbsent(flowLimitCache, trace, n -> getFlowLimit(n, testObject)); + if (l == 0) { + iter.markInvalid(node); + continue main; + } + supply = Math.min(supply, l); + seen.addFirst(trace); + } + total += supply; + flow -= supply; + int finalSupply = supply; + for (NetNode n : seen) { + // reporting flow can cause temperature pipe destruction which causes graph modification while + // iterating. + if (action.execute()) postActions.add(() -> reportFlow(n, finalSupply, testObject)); + int remaining = flowLimitCache.getInt(n) - supply; + flowLimitCache.put(n, remaining); + if (remaining <= 0) { + iter.markInvalid(n); + } + if (MapUtil.computeIfAbsent(lossyCache, n, a -> isLossyNode(a, testObject))) { + // reporting loss can cause misc pipe destruction which causes graph modification while iterating. + if (action.execute()) postActions.add(() -> handleLoss(n, finalSupply, testObject)); + continue main; + } + } + if (action.execute()) reportExtractedInserted(next, supply, testObject, false); + availableDemandCache.put(next, availableDemandCache.getInt(next) - supply); + } + postActions.forEach(Runnable::run); + this.transferring = false; + return total; + } + + protected FluidStack drain(int maxDrain, FluidAction action, Direction side) { + FluidStack stack = getNetworkView().handler().drain(maxDrain, FluidAction.SIMULATE); + if (stack == null) return null; + return drain(stack, action, side); + } + + protected FluidStack drain(FluidStack resource, FluidAction action, Direction side) { + if (this.transferring) return null; + NetNode node = getRelevantNode(side); + if (node == null) node = this.node; + this.transferring = true; + + int flow = resource.getAmount(); + FluidTestObject testObject = new FluidTestObject(resource); + ResilientNetClosestIterator iter = new ResilientNetClosestIterator(node, + EdgeSelector.filtered(EdgeDirection.INCOMING, GraphNetUtility.standardEdgeBlacklist(testObject))); + Object2IntOpenHashMap availableSupplyCache = new Object2IntOpenHashMap<>(); + Object2IntOpenHashMap flowLimitCache = new Object2IntOpenHashMap<>(); + Object2BooleanOpenHashMap lossyCache = new Object2BooleanOpenHashMap<>(); + List postActions = new ObjectArrayList<>(); + int total = 0; + main: + while (iter.hasNext()) { + if (flow <= 0) break; + final NetNode next = iter.next(); + int limit = Math.min(MapUtil.computeIfAbsent(flowLimitCache, next, n -> getFlowLimit(n, testObject)), flow); + if (limit <= 0) { + iter.markInvalid(next); + continue; + } + int supply = MapUtil.computeIfAbsent(availableSupplyCache, next, + n -> getSupplyOrDemand(n, testObject, true)); + if (supply <= 0) continue; + supply = Math.min(supply, limit); + NetEdge span; + NetNode trace = next; + ArrayDeque seen = new ArrayDeque<>(); + seen.add(next); + while ((span = iter.getSpanningTreeEdge(trace)) != null) { + trace = span.getOppositeNode(trace); + if (trace == null) continue main; + int l = MapUtil.computeIfAbsent(flowLimitCache, trace, n -> getFlowLimit(n, testObject)); + if (l == 0) { + iter.markInvalid(node); + continue main; + } + supply = Math.min(supply, l); + seen.addFirst(trace); + } + total += supply; + flow -= supply; + int finalSupply = supply; + for (NetNode n : seen) { + // reporting flow can cause temperature pipe destruction which causes graph modification while + // iterating. + if (action.execute()) postActions.add(() -> reportFlow(n, finalSupply, testObject)); + int remaining = flowLimitCache.getInt(n) - supply; + flowLimitCache.put(n, remaining); + if (remaining <= 0) { + iter.markInvalid(n); + } + if (MapUtil.computeIfAbsent(lossyCache, n, a -> isLossyNode(a, testObject))) { + // reporting loss can cause misc pipe destruction which causes graph modification while iterating. + if (action.execute()) postActions.add(() -> handleLoss(n, finalSupply, testObject)); + continue main; + } + } + if (action.execute()) reportExtractedInserted(next, supply, testObject, true); + availableSupplyCache.put(next, availableSupplyCache.getInt(next) - supply); + } + postActions.forEach(Runnable::run); + this.transferring = false; + return testObject.recombine(total); + } + + public static int getFlowLimit(NetNode node, FluidTestObject testObject) { + ThroughputLogic throughput = node.getData().getLogicEntryNullable(ThroughputLogic.TYPE); + if (throughput == null) return Integer.MAX_VALUE; + FluidFlowLogic history = node.getData().getLogicEntryNullable(FluidFlowLogic.TYPE); + if (history == null) return GTMath.saturatedCast(throughput.getValue() * FluidFlowLogic.MEMORY_TICKS); + Object2LongMap sum = history.getSum(); + if (sum.isEmpty()) return GTMath.saturatedCast(throughput.getValue() * FluidFlowLogic.MEMORY_TICKS); + if (sum.size() < node.getData().getLogicEntryDefaultable(ChannelCountLogic.TYPE).getValue() || + sum.containsKey(testObject)) { + return GTMath.saturatedCast(throughput.getValue() * FluidFlowLogic.MEMORY_TICKS - sum.getLong(testObject)); + } + return 0; + } + + public static boolean isLossyNode(NetNode node, FluidTestObject testObject) { + FluidContainmentLogic containmentLogic = node.getData().getLogicEntryNullable(FluidContainmentLogic.TYPE); + return containmentLogic != null && !containmentLogic.handles(testObject); + } + + public static void reportFlow(NetNode node, int flow, FluidTestObject testObject) { + FluidFlowLogic logic = node.getData().getLogicEntryNullable(FluidFlowLogic.TYPE); + if (logic == null) { + logic = FluidFlowLogic.TYPE.getNew(); + node.getData().setLogicEntry(logic); + } + logic.recordFlow(GTUtil.getCurrentServerTick(), testObject, flow); + TemperatureLogic temp = node.getData().getLogicEntryNullable(TemperatureLogic.TYPE); + if (temp != null) { + FluidStack stack = testObject.recombine(flow); + FluidContainmentLogic cont = node.getData().getLogicEntryDefaultable(FluidContainmentLogic.TYPE); + int t = stack.getFluid().getFluidType().getTemperature(stack); + temp.moveTowardsTemperature(t, GTUtil.getCurrentServerTick(), stack.getAmount(), + cont.getMaximumTemperature() >= t); + if (node instanceof WorldPipeNode n) { + temp.defaultHandleTemperature(n.getNet().getLevel(), n.getEquivalencyData()); + } + } + } + + public static void reportExtractedInserted(NetNode node, int flow, FluidTestObject testObject, boolean extracted) { + if (flow == 0) return; + if (node instanceof NodeExposingCapabilities exposer) { + IFluidHandler handler = exposer.getProvider().getCapability(ForgeCapabilities.FLUID_HANDLER, + exposer.exposedFacing()) + .resolve().orElse(null); + if (handler != null) { + if (extracted) { + handler.drain(testObject.recombine(flow), FluidAction.EXECUTE); + } else { + handler.fill(testObject.recombine(flow), FluidAction.EXECUTE); + } + } + } + } + + public static void handleLoss(NetNode node, int flow, FluidTestObject testObject) { + if (flow == 0) return; + FluidContainmentLogic logic = node.getData().getLogicEntryDefaultable(FluidContainmentLogic.TYPE); + if (node instanceof WorldPipeNode n) { + IWorldPipeNetTile tile = n.getBlockEntity(); + FluidStack stack = testObject.recombine(flow); + // failing attributes take priority over state + for (FluidAttribute attribute : FluidAttribute.inferAttributes(stack)) { + if (!logic.contains(attribute)) { + attribute.handleFailure(tile.getLevel(), tile.getBlockPos(), stack); + return; + } + } + FluidState state = FluidState.inferState(stack); + if (!logic.contains(state)) state.handleFailure(tile.getLevel(), tile.getBlockPos(), stack); + } + } + + public static int getSupplyOrDemand(NetNode node, FluidTestObject testObject, boolean supply) { + if (node instanceof NodeExposingCapabilities exposer) { + IFluidHandler handler = exposer.getProvider().getCapability(ForgeCapabilities.FLUID_HANDLER, + exposer.exposedFacing()) + .resolve().orElse(null); + if (handler != null && instanceOf(handler) == null) { + if (supply) { + FluidStack s = handler.drain(testObject.recombine(Integer.MAX_VALUE), FluidAction.SIMULATE); + return s.isEmpty() ? 0 : s.getAmount(); + } else { + return handler.fill(testObject.recombine(Integer.MAX_VALUE), FluidAction.SIMULATE); + } + } + } + return 0; + } + + public @NotNull FluidNetworkView getNetworkView() { + if (node.getGroupSafe().getData() instanceof FluidNetworkViewGroupData data) { + return data.getOrCreate(node); + } + return FluidNetworkView.EMPTY; + } + + @Override + public @NotNull FluidStack getFluidInTank(int tank) { + return FluidStack.EMPTY; + } + + @Override + public int getTankCapacity(int tank) { + return Integer.MAX_VALUE; + } + + @Override + public boolean isFluidValid(int tank, @NotNull FluidStack stack) { + return true; + } + + @Override + public int fill(FluidStack resource, FluidAction action) { + return fill(resource, action, null); + } + + @Override + public @NotNull FluidStack drain(int maxDrain, FluidAction action) { + return drain(maxDrain, action, null); + } + + @Override + public @NotNull FluidStack drain(FluidStack resource, FluidAction action) { + return drain(resource, action, null); + } + + @Nullable + public static FluidCapabilityObject instanceOf(IFluidHandler handler) { + if (handler instanceof FluidCapabilityObject f) return f; + if (handler instanceof Wrapper w) return w.getParent(); + return null; + } + + protected class Wrapper implements IFluidHandler { + + private final Direction facing; + @Getter + private final int tanks; + + public Wrapper(Direction facing) { + this.facing = facing; + this.tanks = FluidCapabilityObject.this.tanks; + } + + @Override + public int fill(FluidStack resource, FluidAction action) { + return FluidCapabilityObject.this.fill(resource, action, facing); + } + + @Override + public FluidStack drain(FluidStack resource, FluidAction action) { + return FluidCapabilityObject.this.drain(resource, action, facing); + } + + @Override + public FluidStack drain(int maxDrain, FluidAction action) { + return FluidCapabilityObject.this.drain(maxDrain, action, facing); + } + + @Override + public @NotNull FluidStack getFluidInTank(int tank) { + return FluidStack.EMPTY; + } + + @Override + public int getTankCapacity(int tank) { + return Integer.MAX_VALUE; + } + + @Override + public boolean isFluidValid(int tank, @NotNull FluidStack stack) { + return true; + } + + public FluidCapabilityObject getParent() { + return FluidCapabilityObject.this; + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/fluid/FluidContainmentLogic.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/fluid/FluidContainmentLogic.java new file mode 100644 index 0000000000..a8cc4b33ed --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/fluid/FluidContainmentLogic.java @@ -0,0 +1,170 @@ +package com.gregtechceu.gtceu.common.pipelike.net.fluid; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.fluids.FluidState; +import com.gregtechceu.gtceu.api.fluids.attribute.FluidAttribute; +import com.gregtechceu.gtceu.api.graphnet.logic.NetLogicEntry; +import com.gregtechceu.gtceu.api.graphnet.logic.NetLogicType; +import com.gregtechceu.gtceu.api.graphnet.predicate.test.FluidTestObject; +import com.gregtechceu.gtceu.utils.GTUtil; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.StringTag; +import net.minecraft.nbt.Tag; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +import net.minecraftforge.fluids.FluidStack; + +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.BitSet; +import java.util.Collection; +import java.util.EnumSet; +import java.util.Set; + +public final class FluidContainmentLogic extends NetLogicEntry { + + public static final FluidContainmentLogicType TYPE = new FluidContainmentLogicType(); + + private int maximumTemperature; + + private final Set containableAttributes = new ObjectOpenHashSet<>(); + private @NotNull EnumSet containableStates = EnumSet.noneOf(FluidState.class); + + @Override + public @NotNull FluidContainmentLogicType getType() { + return TYPE; + } + + @Contract("_ -> this") + public FluidContainmentLogic contain(FluidState state) { + this.containableStates.add(state); + return this; + } + + @Contract("_ -> this") + public FluidContainmentLogic contain(@NotNull FluidAttribute attribute) { + this.containableAttributes.add(attribute.getResourceLocation()); + return this; + } + + @Contract("_ -> this") + public FluidContainmentLogic notContain(FluidState state) { + this.containableStates.remove(state); + return this; + } + + @Contract("_ -> this") + public FluidContainmentLogic notContain(@NotNull FluidAttribute attribute) { + this.containableAttributes.remove(attribute.getResourceLocation()); + return this; + } + + public boolean contains(FluidState state) { + return this.containableStates.contains(state); + } + + public boolean contains(@NotNull FluidAttribute attribute) { + return this.containableAttributes.contains(attribute.getResourceLocation()); + } + + public boolean handles(FluidTestObject testObject) { + return handles(testObject.recombine()); + } + + public boolean handles(FluidStack stack) { + if (!contains(FluidState.inferState(stack))) return false; + for (FluidAttribute attribute : FluidAttribute.inferAttributes(stack)) { + if (!contains(attribute)) return false; + } + return true; + } + + public void setMaximumTemperature(int maximumTemperature) { + this.maximumTemperature = maximumTemperature; + } + + public int getMaximumTemperature() { + return maximumTemperature; + } + + @Override + public @NotNull FluidContainmentLogic union(NetLogicEntry other) { + if (other instanceof FluidContainmentLogic logic) { + if (this.containableAttributes.equals(logic.containableAttributes) && + this.containableStates.equals(logic.containableStates)) { + return this; + } else { + FluidContainmentLogic returnable = new FluidContainmentLogic(); + returnable.containableStates = EnumSet.copyOf(this.containableStates); + returnable.containableStates.retainAll(logic.containableStates); + returnable.containableAttributes.addAll(this.containableAttributes); + returnable.containableAttributes.retainAll(logic.containableAttributes); + return returnable; + } + } + return this; + } + + @Override + public CompoundTag serializeNBT() { + CompoundTag tag = new CompoundTag(); + ListTag list = new ListTag(); + for (ResourceLocation loc : containableAttributes) { + list.add(StringTag.valueOf(loc.toString())); + } + tag.put("Attributes", list); + tag.putByteArray("States", GTUtil.setToMask(containableStates).toByteArray()); + return tag; + } + + @Override + public void deserializeNBT(CompoundTag nbt) { + ListTag list = nbt.getList("Attributes", Tag.TAG_STRING); + for (int i = 0; i < list.size(); i++) { + containableAttributes.add(new ResourceLocation(list.getString(i))); + } + containableStates = GTUtil.maskToSet(FluidState.class, BitSet.valueOf(nbt.getByteArray("States"))); + } + + @Override + public void encode(FriendlyByteBuf buf, boolean fullChange) { + buf.writeVarInt(containableAttributes.size()); + for (ResourceLocation loc : containableAttributes) { + buf.writeUtf(loc.toString()); + } + buf.writeByteArray(GTUtil.setToMask(containableStates).toByteArray()); + } + + @Override + public void decode(FriendlyByteBuf buf, boolean fullChange) { + int attributes = buf.readVarInt(); + for (int i = 0; i < attributes; i++) { + containableAttributes.add(new ResourceLocation(buf.readUtf(255))); + } + containableStates = GTUtil.maskToSet(FluidState.class, BitSet.valueOf(buf.readByteArray(255))); + } + + public static final class FluidContainmentLogicType extends NetLogicType { + + public FluidContainmentLogicType() { + super(GTCEu.MOD_ID, "FluidContainment", FluidContainmentLogic::new, + new FluidContainmentLogic().contain(FluidState.LIQUID)); + } + + public @NotNull FluidContainmentLogic getWith(Collection states, + @NotNull Collection attributes, + int maximumTemperature) { + FluidContainmentLogic logic = getNew(); + logic.containableStates.addAll(states); + for (FluidAttribute attribute : attributes) { + logic.contain(attribute); + } + logic.maximumTemperature = maximumTemperature; + return logic; + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/fluid/FluidFlowLogic.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/fluid/FluidFlowLogic.java new file mode 100644 index 0000000000..edbe4d22e2 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/fluid/FluidFlowLogic.java @@ -0,0 +1,74 @@ +package com.gregtechceu.gtceu.common.pipelike.net.fluid; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.graphnet.logic.AbstractTransientLogicData; +import com.gregtechceu.gtceu.api.graphnet.logic.NetLogicType; +import com.gregtechceu.gtceu.api.graphnet.predicate.test.FluidTestObject; +import com.gregtechceu.gtceu.utils.GTUtil; + +import net.minecraftforge.fluids.FluidStack; + +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2LongArrayMap; +import it.unimi.dsi.fastutil.objects.Object2LongMap; +import it.unimi.dsi.fastutil.objects.Object2LongMaps; +import lombok.Getter; +import org.jetbrains.annotations.NotNull; + +public class FluidFlowLogic extends AbstractTransientLogicData { + + public static final NetLogicType TYPE = new NetLogicType<>(GTCEu.MOD_ID, "FluidFlow", + FluidFlowLogic::new, new FluidFlowLogic()); + + public static final int MEMORY_TICKS = WorldFluidNet.getBufferTicks(); + + private final Long2ObjectOpenHashMap> memory = new Long2ObjectOpenHashMap<>(); + @Getter + private FluidStack last; + + @Override + public @NotNull NetLogicType getType() { + return TYPE; + } + + public @NotNull Long2ObjectOpenHashMap> getMemory() { + updateMemory(GTUtil.getCurrentServerTick()); + return memory; + } + + public @NotNull Object2LongMap getSum() { + Object2LongMap sum = new Object2LongArrayMap<>(); + for (Object2LongMap list : getMemory().values()) { + for (var entry : list.object2LongEntrySet()) { + sum.put(entry.getKey(), sum.getLong(entry.getKey()) + entry.getLongValue()); + } + } + return sum; + } + + public @NotNull Object2LongMap getFlow(long tick) { + updateMemory(tick); + return memory.getOrDefault(tick, Object2LongMaps.emptyMap()); + } + + public void recordFlow(long tick, @NotNull FluidStack flow) { + recordFlow(tick, new FluidTestObject(flow), flow.getAmount()); + } + + public void recordFlow(long tick, @NotNull FluidTestObject testObject, int amount) { + updateMemory(tick); + Object2LongMap map = memory.computeIfAbsent(tick, k -> new Object2LongArrayMap<>()); + map.put(testObject, map.getLong(testObject) + amount); + last = testObject.recombine(amount); + } + + private void updateMemory(long tick) { + var iter = memory.long2ObjectEntrySet().fastIterator(); + while (iter.hasNext()) { + var entry = iter.next(); + if (entry.getLongKey() + MEMORY_TICKS < tick) { + iter.remove(); + } + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/fluid/FluidNetworkView.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/fluid/FluidNetworkView.java new file mode 100644 index 0000000000..6155ed6fef --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/fluid/FluidNetworkView.java @@ -0,0 +1,18 @@ +package com.gregtechceu.gtceu.common.pipelike.net.fluid; + +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; +import com.gregtechceu.gtceu.api.transfer.fluid.FluidHandlerList; + +import net.minecraftforge.fluids.capability.IFluidHandler; + +import com.google.common.collect.BiMap; +import com.google.common.collect.ImmutableBiMap; + +public record FluidNetworkView(FluidHandlerList handler, BiMap handlerNetNodeBiMap) { + + public static final FluidNetworkView EMPTY = FluidNetworkView.of(ImmutableBiMap.of()); + + public static FluidNetworkView of(BiMap handlerNetNodeBiMap) { + return new FluidNetworkView(new FluidHandlerList(handlerNetNodeBiMap.keySet()), handlerNetNodeBiMap); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/fluid/FluidNetworkViewGroupData.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/fluid/FluidNetworkViewGroupData.java new file mode 100644 index 0000000000..b23d4280d2 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/fluid/FluidNetworkViewGroupData.java @@ -0,0 +1,79 @@ +package com.gregtechceu.gtceu.common.pipelike.net.fluid; + +import com.gregtechceu.gtceu.api.graphnet.group.GroupData; +import com.gregtechceu.gtceu.api.graphnet.group.NodeCacheGroupData; +import com.gregtechceu.gtceu.api.graphnet.net.NetEdge; +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.NodeExposingCapabilities; +import com.gregtechceu.gtceu.api.graphnet.traverse.EdgeDirection; +import com.gregtechceu.gtceu.api.graphnet.traverse.NetClosestIterator; +import com.gregtechceu.gtceu.api.graphnet.traverse.NetIterator; +import com.gregtechceu.gtceu.api.transfer.fluid.FluidHandlerList; + +import net.minecraftforge.common.capabilities.ForgeCapabilities; +import net.minecraftforge.fluids.capability.IFluidHandler; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Set; + +public class FluidNetworkViewGroupData extends NodeCacheGroupData { + + @Override + protected FluidNetworkView getNew(@NotNull NetNode node) { + // use a list to preserve 'found order' from the iterator, + // so closer handlers are earlier in our handler list's extraction/insertion preference + List handlerList = new ObjectArrayList<>(); + BiMap map = HashBiMap.create(); + NetIterator iter = new NetClosestIterator(node, EdgeDirection.ALL); + while (iter.hasNext()) { + NetNode next = iter.next(); + if (next instanceof NodeExposingCapabilities exposer) { + IFluidHandler handler = exposer.getProvider().getCapability( + ForgeCapabilities.FLUID_HANDLER, + exposer.exposedFacing()) + .resolve().orElse(null); + if (handler != null && FluidCapabilityObject.instanceOf(handler) == null) { + map.put(handler, next); + handlerList.add(handler); + } + } + } + return new FluidNetworkView(new FluidHandlerList(handlerList), map); + } + + @Override + public void notifyOfBridgingEdge(@NotNull NetEdge edge) { + invalidateAll(); + } + + @Override + public void notifyOfRemovedEdge(@NotNull NetEdge edge) { + invalidateAll(); + } + + @Override + protected @Nullable GroupData mergeAcross(@Nullable GroupData other, @NotNull NetEdge edge) { + invalidateAll(); + return this; + } + + @Override + public @NotNull Pair splitAcross(@NotNull Set sourceNodes, + @NotNull Set targetNodes) { + invalidateAll(); + return Pair.of(this, new FluidNetworkViewGroupData()); + } + + // unused since we override splitAcross + @Override + protected @NotNull NodeCacheGroupData buildFilteredCache(@NotNull Set filterNodes) { + return this; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/fluid/WorldFluidNet.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/fluid/WorldFluidNet.java new file mode 100644 index 0000000000..722d5cd270 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/fluid/WorldFluidNet.java @@ -0,0 +1,101 @@ +package com.gregtechceu.gtceu.common.pipelike.net.fluid; + +import com.gregtechceu.gtceu.api.cover.CoverBehavior; +import com.gregtechceu.gtceu.api.cover.filter.CoverWithFluidFilter; +import com.gregtechceu.gtceu.api.graphnet.group.GroupData; +import com.gregtechceu.gtceu.api.graphnet.net.IGraphNet; +import com.gregtechceu.gtceu.api.graphnet.net.NetEdge; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNet; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IPipeCapabilityObject; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.NodeManagingPCW; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeCapabilityWrapper; +import com.gregtechceu.gtceu.api.graphnet.pipenet.predicate.BlockedPredicate; +import com.gregtechceu.gtceu.api.graphnet.pipenet.predicate.FilterPredicate; +import com.gregtechceu.gtceu.common.cover.data.FilterMode; +import com.gregtechceu.gtceu.common.cover.data.ManualIOMode; +import com.gregtechceu.gtceu.common.pipelike.net.item.WorldItemNet; + +import net.minecraft.server.level.ServerLevel; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.ForgeCapabilities; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class WorldFluidNet extends WorldPipeNet { + + private static final String DATA_ID = "gtceu_world_fluid_net"; + + public static WorldFluidNet getWorldNet(ServerLevel serverLevel) { + WorldFluidNet net = serverLevel.getDataStorage().computeIfAbsent(tag -> { + WorldFluidNet netx = new WorldFluidNet(); + netx.load(tag); + return netx; + }, WorldFluidNet::new, DATA_ID); + net.setLevel(serverLevel); + return net; + } + + public WorldFluidNet() { + super(true); + } + + @Override + protected void coverPredication(@NotNull NetEdge edge, @Nullable CoverBehavior a, @Nullable CoverBehavior b) { + super.coverPredication(edge, a, b); + if (edge.getPredicateHandler().hasPredicate(BlockedPredicate.TYPE)) return; + FilterPredicate predicate = null; + if (a instanceof CoverWithFluidFilter filter) { + if (filter.getManualIOMode() == ManualIOMode.DISABLED) { + edge.getPredicateHandler().clearPredicates(); + edge.getPredicateHandler().setPredicate(BlockedPredicate.TYPE.getNew()); + return; + } else if (filter.getManualIOMode() == ManualIOMode.FILTERED && + filter.getFilterMode() != FilterMode.FILTER_INSERT) { + predicate = FilterPredicate.TYPE.getNew(); + predicate.setSourceFilter(filter.getFilterHandler().getFilter()); + } + } + if (b instanceof CoverWithFluidFilter filter) { + if (filter.getManualIOMode() == ManualIOMode.DISABLED) { + edge.getPredicateHandler().clearPredicates(); + edge.getPredicateHandler().setPredicate(BlockedPredicate.TYPE.getNew()); + return; + } else if (filter.getManualIOMode() == ManualIOMode.FILTERED && + filter.getFilterMode() != FilterMode.FILTER_EXTRACT) { + if (predicate == null) predicate = FilterPredicate.TYPE.getNew(); + predicate.setTargetFilter(filter.getFilterHandler().getFilter()); + } + } + if (predicate != null) edge.getPredicateHandler().setPredicate(predicate); + } + + @Override + public boolean clashesWith(IGraphNet net) { + return net instanceof WorldItemNet; + } + + @Override + public PipeCapabilityWrapper buildCapabilityWrapper(@NotNull PipeBlockEntity owner, @NotNull WorldPipeNode node) { + Object2ObjectOpenHashMap, IPipeCapabilityObject> map = new Object2ObjectOpenHashMap<>(); + map.put(ForgeCapabilities.FLUID_HANDLER, new FluidCapabilityObject(node)); + return new NodeManagingPCW(owner, node, map, 0, 0); + } + + public static int getBufferTicks() { + return 10; + } + + @Override + public int getNetworkID() { + return 1; + } + + @Override + public @Nullable GroupData getBlankGroupData() { + return new FluidNetworkViewGroupData(); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/item/ItemCapabilityObject.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/item/ItemCapabilityObject.java new file mode 100644 index 0000000000..5e8f5ee481 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/item/ItemCapabilityObject.java @@ -0,0 +1,251 @@ +package com.gregtechceu.gtceu.common.pipelike.net.item; + +import com.gregtechceu.gtceu.api.graphnet.GraphNetUtility; +import com.gregtechceu.gtceu.api.graphnet.logic.ChannelCountLogic; +import com.gregtechceu.gtceu.api.graphnet.logic.ThroughputLogic; +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IPipeCapabilityObject; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.NodeManagingPCW; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeCapabilityWrapper; +import com.gregtechceu.gtceu.api.graphnet.predicate.test.ItemTestObject; +import com.gregtechceu.gtceu.api.graphnet.traverse.EdgeDirection; +import com.gregtechceu.gtceu.api.graphnet.traverse.EdgeSelector; +import com.gregtechceu.gtceu.api.graphnet.traverse.ResilientNetClosestIterator; +import com.gregtechceu.gtceu.utils.GTMath; +import com.gregtechceu.gtceu.utils.GTUtil; + +import net.minecraft.core.Direction; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.ForgeCapabilities; +import net.minecraftforge.common.util.LazyOptional; +import net.minecraftforge.items.IItemHandler; + +import it.unimi.dsi.fastutil.objects.Object2LongMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.EnumMap; +import java.util.function.Predicate; + +public class ItemCapabilityObject implements IPipeCapabilityObject, IItemHandler { + + private PipeBlockEntity tile; + private NodeManagingPCW capabilityWrapper; + + private final EnumMap wrappers = new EnumMap<>(Direction.class); + private final WorldPipeNode node; + + private boolean transferring = false; + + public ItemCapabilityObject(WorldPipeNode node) { + this.node = node; + for (Direction facing : GTUtil.DIRECTIONS) { + wrappers.put(facing, new Wrapper(facing)); + } + } + + public WorldPipeNode getNode() { + return node; + } + + @Override + public void init(@NotNull PipeBlockEntity tile, @NotNull PipeCapabilityWrapper wrapper) { + this.tile = tile; + if (!(wrapper instanceof NodeManagingPCW p)) + throw new IllegalArgumentException("ItemCapabilityObjects must be initialized to NodeManagingPCWs!"); + this.capabilityWrapper = p; + } + + private boolean inputDisallowed(Direction side) { + if (side == null) return false; + else return tile.isBlocked(side); + } + + @Override + public @NotNull LazyOptional getCapability(Capability cap, @Nullable Direction facing) { + return ForgeCapabilities.ITEM_HANDLER.orEmpty(cap, + LazyOptional.of(() -> facing == null ? this : wrappers.get(facing))); + } + + protected @Nullable NetNode getRelevantNode(Direction facing) { + return facing == null ? node : capabilityWrapper.getNodeForFacing(facing); + } + + protected @NotNull ItemStack insertItem(int slot, @NotNull ItemStack stack, boolean simulate, Direction side) { + @NotNull + ItemStack result = stack; + if (!this.transferring && !inputDisallowed(side)) { + NetNode node = getRelevantNode(side); + if (node == null) node = this.node; + this.transferring = true; + ItemNetworkView networkView = getNetworkView(); + IItemHandler targetHandler = networkView.handler().getHandlerBySlot(slot); + NetNode targetNode = networkView.handlerNetNodeBiMap().get(targetHandler); + if (targetNode != null) { + int handlerSlot = slot - networkView.handler().getOffsetByHandler(targetHandler); + int insertable = stack.getCount() - targetHandler.insertItem(handlerSlot, stack, true).getCount(); + if (insertable > 0) { + final ItemTestObject testObject = new ItemTestObject(stack); + Predicate filter = GraphNetUtility.standardEdgeBlacklist(testObject); + ResilientNetClosestIterator forwardFrontier = new ResilientNetClosestIterator(node, + EdgeSelector.filtered(EdgeDirection.OUTGOING, filter)); + ResilientNetClosestIterator backwardFrontier = new ResilientNetClosestIterator(targetNode, + EdgeSelector.filtered(EdgeDirection.INCOMING, filter)); + insertable = GraphNetUtility.p2pWalk(simulate, insertable, n -> getFlowLimit(n, testObject), + (n, i) -> reportFlow(n, i, testObject), forwardFrontier, backwardFrontier); + if (!simulate) targetHandler.insertItem(handlerSlot, testObject.recombine(insertable), false); + result = testObject.recombine(stack.getCount() - insertable); + } + } + this.transferring = false; + } + + return result; + } + + protected @NotNull ItemStack extractItem(int slot, int amount, boolean simulate, Direction side) { + @NotNull + ItemStack result = ItemStack.EMPTY; + if (!this.transferring && !inputDisallowed(side)) { + NetNode node = getRelevantNode(side); + if (node == null) node = this.node; + this.transferring = true; + ItemNetworkView networkView = getNetworkView(); + IItemHandler targetHandler = networkView.handler().getHandlerBySlot(slot); + NetNode targetNode = networkView.handlerNetNodeBiMap().get(targetHandler); + if (targetNode != null) { + int handlerSlot = slot - networkView.handler().getOffsetByHandler(targetHandler); + ItemStack stack = targetHandler.extractItem(handlerSlot, amount, true); + int extractable = stack.getCount(); + if (extractable > 0) { + final ItemTestObject testObject = new ItemTestObject(stack); + Predicate filter = GraphNetUtility.standardEdgeBlacklist(testObject); + ResilientNetClosestIterator forwardFrontier = new ResilientNetClosestIterator(node, + EdgeSelector.filtered(EdgeDirection.INCOMING, filter)); + ResilientNetClosestIterator backwardFrontier = new ResilientNetClosestIterator(targetNode, + EdgeSelector.filtered(EdgeDirection.OUTGOING, filter)); + extractable = GraphNetUtility.p2pWalk(simulate, extractable, n -> getFlowLimit(n, testObject), + (n, i) -> reportFlow(n, i, testObject), forwardFrontier, backwardFrontier); + if (!simulate) targetHandler.extractItem(handlerSlot, extractable, false); + result = testObject.recombine(extractable); + } + } + this.transferring = false; + } + + return result; + } + + public static int getFlowLimit(NetNode node, ItemTestObject testObject) { + ThroughputLogic throughput = node.getData().getLogicEntryNullable(ThroughputLogic.TYPE); + if (throughput == null) return Integer.MAX_VALUE; + ItemFlowLogic history = node.getData().getLogicEntryNullable(ItemFlowLogic.TYPE); + if (history == null) return GTMath.saturatedCast(throughput.getValue() * ItemFlowLogic.BUFFER_MULT); + Object2LongMap sum = history.getSum(); + if (sum.isEmpty()) return GTMath.saturatedCast(throughput.getValue() * ItemFlowLogic.BUFFER_MULT); + if (sum.size() < node.getData().getLogicEntryDefaultable(ChannelCountLogic.TYPE).getValue() || + sum.containsKey(testObject)) { + return GTMath.saturatedCast(throughput.getValue() * ItemFlowLogic.BUFFER_MULT - sum.getLong(testObject)); + } + return 0; + } + + public static void reportFlow(NetNode node, int flow, ItemTestObject testObject) { + ItemFlowLogic logic = node.getData().getLogicEntryNullable(ItemFlowLogic.TYPE); + if (logic == null) { + logic = ItemFlowLogic.TYPE.getNew(); + node.getData().setLogicEntry(logic); + } + logic.recordFlow(GTUtil.getCurrentServerTick(), testObject.recombine(flow)); + } + + public @NotNull ItemNetworkView getNetworkView() { + if (node.getGroupSafe().getData() instanceof ItemNetworkViewGroupData data) { + return data.getOrCreate(node); + } + return ItemNetworkView.EMPTY; + } + + @Override + public int getSlots() { + return getNetworkView().handler().getSlots(); + } + + @Override + public @NotNull ItemStack getStackInSlot(int slot) { + return getNetworkView().handler().getStackInSlot(slot); + } + + @Override + public @NotNull ItemStack insertItem(int slot, @NotNull ItemStack stack, boolean simulate) { + return insertItem(slot, stack, simulate, null); + } + + @Override + public @NotNull ItemStack extractItem(int slot, int amount, boolean simulate) { + return extractItem(slot, amount, simulate, null); + } + + @Override + public int getSlotLimit(int slot) { + return getNetworkView().handler().getSlotLimit(slot); + } + + @Override + public boolean isItemValid(int slot, @NotNull ItemStack stack) { + return getNetworkView().handler().isItemValid(slot, stack); + } + + @Nullable + public static ItemCapabilityObject instanceOf(IItemHandler handler) { + if (handler instanceof ItemCapabilityObject i) return i; + if (handler instanceof Wrapper w) return w.getParent(); + return null; + } + + protected class Wrapper implements IItemHandler { + + private final Direction facing; + + public Wrapper(Direction facing) { + this.facing = facing; + } + + @Override + public int getSlots() { + return ItemCapabilityObject.this.getSlots(); + } + + @Override + public @NotNull ItemStack getStackInSlot(int slot) { + return ItemCapabilityObject.this.getStackInSlot(slot); + } + + @Override + public @NotNull ItemStack insertItem(int slot, @NotNull ItemStack stack, boolean simulate) { + return ItemCapabilityObject.this.insertItem(slot, stack, simulate, facing); + } + + @Override + public @NotNull ItemStack extractItem(int slot, int amount, boolean simulate) { + return ItemCapabilityObject.this.extractItem(slot, amount, simulate, facing); + } + + @Override + public int getSlotLimit(int slot) { + return ItemCapabilityObject.this.getSlotLimit(slot); + } + + @Override + public boolean isItemValid(int slot, @NotNull ItemStack stack) { + return ItemCapabilityObject.this.isItemValid(slot, stack); + } + + public ItemCapabilityObject getParent() { + return ItemCapabilityObject.this; + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/item/ItemFlowLogic.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/item/ItemFlowLogic.java new file mode 100644 index 0000000000..ddc8137a5e --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/item/ItemFlowLogic.java @@ -0,0 +1,77 @@ +package com.gregtechceu.gtceu.common.pipelike.net.item; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.graphnet.logic.AbstractTransientLogicData; +import com.gregtechceu.gtceu.api.graphnet.logic.NetLogicType; +import com.gregtechceu.gtceu.api.graphnet.predicate.test.ItemTestObject; +import com.gregtechceu.gtceu.utils.GTUtil; + +import net.minecraft.world.item.ItemStack; + +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2LongArrayMap; +import it.unimi.dsi.fastutil.objects.Object2LongMap; +import it.unimi.dsi.fastutil.objects.Object2LongMaps; +import org.jetbrains.annotations.NotNull; + +public class ItemFlowLogic extends AbstractTransientLogicData { + + public static final NetLogicType TYPE = new NetLogicType<>(GTCEu.MOD_ID, "ItemFlow", + ItemFlowLogic::new, new ItemFlowLogic()); + + public static final int MEMORY_TICKS = WorldItemNet.getBufferTicks(); + public static final int BUFFER_MULT = MEMORY_TICKS / WorldItemNet.getBufferRegenerationFactor(); + + private final Long2ObjectOpenHashMap> memory = new Long2ObjectOpenHashMap<>(); + private ItemStack last; + + @Override + public @NotNull NetLogicType getType() { + return TYPE; + } + + public @NotNull Long2ObjectOpenHashMap> getMemory() { + updateMemory(GTUtil.getCurrentServerTick()); + return memory; + } + + public @NotNull Object2LongMap getSum() { + Object2LongMap sum = new Object2LongArrayMap<>(); + for (Object2LongMap list : getMemory().values()) { + for (var entry : list.object2LongEntrySet()) { + sum.put(entry.getKey(), sum.getLong(entry.getKey()) + entry.getLongValue()); + } + } + return sum; + } + + public @NotNull Object2LongMap getFlow(long tick) { + updateMemory(tick); + return memory.getOrDefault(tick, Object2LongMaps.emptyMap()); + } + + public void recordFlow(long tick, @NotNull ItemStack flow) { + recordFlow(tick, new ItemTestObject(flow), flow.getCount()); + } + + public void recordFlow(long tick, @NotNull ItemTestObject testObject, int amount) { + updateMemory(tick); + Object2LongMap map = memory.computeIfAbsent(tick, k -> new Object2LongArrayMap<>()); + map.put(testObject, map.getLong(testObject) + amount); + last = testObject.recombine(amount); + } + + public ItemStack getLast() { + return last; + } + + private void updateMemory(long tick) { + var iter = memory.long2ObjectEntrySet().fastIterator(); + while (iter.hasNext()) { + var entry = iter.next(); + if (entry.getLongKey() + MEMORY_TICKS < tick) { + iter.remove(); + } + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/item/ItemNetworkView.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/item/ItemNetworkView.java new file mode 100644 index 0000000000..5985d33887 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/item/ItemNetworkView.java @@ -0,0 +1,18 @@ +package com.gregtechceu.gtceu.common.pipelike.net.item; + +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; +import com.gregtechceu.gtceu.api.transfer.item.ItemHandlerList; + +import net.minecraftforge.items.IItemHandler; + +import com.google.common.collect.BiMap; +import com.google.common.collect.ImmutableBiMap; + +public record ItemNetworkView(ItemHandlerList handler, BiMap handlerNetNodeBiMap) { + + public static final ItemNetworkView EMPTY = ItemNetworkView.of(ImmutableBiMap.of()); + + public static ItemNetworkView of(BiMap handlerNetNodeBiMap) { + return new ItemNetworkView(new ItemHandlerList(handlerNetNodeBiMap.keySet()), handlerNetNodeBiMap); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/item/ItemNetworkViewGroupData.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/item/ItemNetworkViewGroupData.java new file mode 100644 index 0000000000..6cd776b8a3 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/item/ItemNetworkViewGroupData.java @@ -0,0 +1,79 @@ +package com.gregtechceu.gtceu.common.pipelike.net.item; + +import com.gregtechceu.gtceu.api.graphnet.group.GroupData; +import com.gregtechceu.gtceu.api.graphnet.group.NodeCacheGroupData; +import com.gregtechceu.gtceu.api.graphnet.net.NetEdge; +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.NodeExposingCapabilities; +import com.gregtechceu.gtceu.api.graphnet.traverse.EdgeDirection; +import com.gregtechceu.gtceu.api.graphnet.traverse.NetClosestIterator; +import com.gregtechceu.gtceu.api.graphnet.traverse.NetIterator; +import com.gregtechceu.gtceu.api.transfer.item.ItemHandlerList; + +import net.minecraftforge.common.capabilities.ForgeCapabilities; +import net.minecraftforge.items.IItemHandler; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Set; + +public class ItemNetworkViewGroupData extends NodeCacheGroupData { + + @Override + protected ItemNetworkView getNew(@NotNull NetNode node) { + // use a list to preserve 'found order' from the iterator, + // so closer handlers are lower in our handler list's slot order. + List handlerList = new ObjectArrayList<>(); + BiMap map = HashBiMap.create(); + NetIterator iter = new NetClosestIterator(node, EdgeDirection.ALL); + while (iter.hasNext()) { + NetNode next = iter.next(); + if (next instanceof NodeExposingCapabilities exposer) { + IItemHandler handler = exposer.getProvider().getCapability( + ForgeCapabilities.ITEM_HANDLER, + exposer.exposedFacing()) + .resolve().orElse(null); + if (handler != null && ItemCapabilityObject.instanceOf(handler) == null) { + handlerList.add(handler); + map.put(handler, next); + } + } + } + return new ItemNetworkView(new ItemHandlerList(handlerList), map); + } + + @Override + public void notifyOfBridgingEdge(@NotNull NetEdge edge) { + invalidateAll(); + } + + @Override + public void notifyOfRemovedEdge(@NotNull NetEdge edge) { + invalidateAll(); + } + + @Override + protected @Nullable GroupData mergeAcross(@Nullable GroupData other, @NotNull NetEdge edge) { + invalidateAll(); + return this; + } + + @Override + public @NotNull Pair splitAcross(@NotNull Set sourceNodes, + @NotNull Set targetNodes) { + invalidateAll(); + return Pair.of(this, new ItemNetworkViewGroupData()); + } + + // unused since we override splitAcross + @Override + protected @NotNull NodeCacheGroupData buildFilteredCache(@NotNull Set filterNodes) { + return this; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/item/WorldItemNet.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/item/WorldItemNet.java new file mode 100644 index 0000000000..0c70054ee3 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/item/WorldItemNet.java @@ -0,0 +1,105 @@ +package com.gregtechceu.gtceu.common.pipelike.net.item; + +import com.gregtechceu.gtceu.api.cover.CoverBehavior; +import com.gregtechceu.gtceu.api.cover.filter.CoverWithItemFilter; +import com.gregtechceu.gtceu.api.graphnet.group.GroupData; +import com.gregtechceu.gtceu.api.graphnet.net.IGraphNet; +import com.gregtechceu.gtceu.api.graphnet.net.NetEdge; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNet; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IPipeCapabilityObject; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.NodeManagingPCW; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeCapabilityWrapper; +import com.gregtechceu.gtceu.api.graphnet.pipenet.predicate.BlockedPredicate; +import com.gregtechceu.gtceu.api.graphnet.pipenet.predicate.FilterPredicate; +import com.gregtechceu.gtceu.common.cover.data.FilterMode; +import com.gregtechceu.gtceu.common.cover.data.ManualIOMode; +import com.gregtechceu.gtceu.common.pipelike.net.fluid.WorldFluidNet; + +import net.minecraft.server.level.ServerLevel; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.ForgeCapabilities; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class WorldItemNet extends WorldPipeNet { + + private static final String DATA_ID = "gtceu_world_item_net"; + + public static @NotNull WorldItemNet getWorldNet(ServerLevel serverLevel) { + WorldItemNet net = serverLevel.getDataStorage().computeIfAbsent(tag -> { + WorldItemNet netx = new WorldItemNet(); + netx.load(tag); + return netx; + }, WorldItemNet::new, DATA_ID); + net.setLevel(serverLevel); + return net; + } + + public WorldItemNet() { + super(true); + } + + @Override + protected void coverPredication(@NotNull NetEdge edge, @Nullable CoverBehavior a, @Nullable CoverBehavior b) { + super.coverPredication(edge, a, b); + if (edge.getPredicateHandler().hasPredicate(BlockedPredicate.TYPE)) return; + FilterPredicate predicate = null; + if (a instanceof CoverWithItemFilter filter) { + if (filter.getManualIOMode() == ManualIOMode.DISABLED) { + edge.getPredicateHandler().clearPredicates(); + edge.getPredicateHandler().setPredicate(BlockedPredicate.TYPE.getNew()); + return; + } else if (filter.getManualIOMode() == ManualIOMode.FILTERED && + filter.getFilterMode() != FilterMode.FILTER_INSERT) { + predicate = FilterPredicate.TYPE.getNew(); + predicate.setSourceFilter(filter.getFilterHandler().getFilter()); + } + } + if (b instanceof CoverWithItemFilter filter) { + if (filter.getManualIOMode() == ManualIOMode.DISABLED) { + edge.getPredicateHandler().clearPredicates(); + edge.getPredicateHandler().setPredicate(BlockedPredicate.TYPE.getNew()); + return; + } else if (filter.getManualIOMode() == ManualIOMode.FILTERED && + filter.getFilterMode() != FilterMode.FILTER_EXTRACT) { + if (predicate == null) predicate = FilterPredicate.TYPE.getNew(); + predicate.setTargetFilter(filter.getFilterHandler().getFilter()); + } + } + if (predicate != null) edge.getPredicateHandler().setPredicate(predicate); + } + + @Override + public boolean clashesWith(IGraphNet net) { + return net instanceof WorldFluidNet; + } + + @Override + public PipeCapabilityWrapper buildCapabilityWrapper(@NotNull PipeBlockEntity owner, @NotNull WorldPipeNode node) { + Object2ObjectOpenHashMap, IPipeCapabilityObject> map = new Object2ObjectOpenHashMap<>(); + map.put(ForgeCapabilities.ITEM_HANDLER, new ItemCapabilityObject(node)); + return new NodeManagingPCW(owner, node, map, 0, 0); + } + + public static int getBufferTicks() { + return 10; + } + + public static int getBufferRegenerationFactor() { + return 5; + } + + @Override + public int getNetworkID() { + return 2; + } + + @Override + public @Nullable GroupData getBlankGroupData() { + return new ItemNetworkViewGroupData(); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/laser/LaserCapabilityObject.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/laser/LaserCapabilityObject.java new file mode 100644 index 0000000000..486880007a --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/laser/LaserCapabilityObject.java @@ -0,0 +1,144 @@ +package com.gregtechceu.gtceu.common.pipelike.net.laser; + +import com.gregtechceu.gtceu.api.capability.ILaserRelay; +import com.gregtechceu.gtceu.api.capability.forge.GTCapability; +import com.gregtechceu.gtceu.api.graphnet.group.PathCacheGroupData; +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; +import com.gregtechceu.gtceu.api.graphnet.path.NetPath; +import com.gregtechceu.gtceu.api.graphnet.path.SingletonNetPath; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IPipeCapabilityObject; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeCapabilityWrapper; +import com.gregtechceu.gtceu.common.cover.ShutterCover; +import com.gregtechceu.gtceu.common.pipelike.net.SlowActiveWalker; +import com.gregtechceu.gtceu.utils.GTUtil; + +import net.minecraft.core.Direction; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.util.LazyOptional; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.EnumMap; +import java.util.Set; + +public class LaserCapabilityObject implements IPipeCapabilityObject, ILaserRelay { + + public static final int ACTIVE_KEY = 122; + + protected final WorldPipeNode node; + private @Nullable PipeBlockEntity tile; + + private final EnumMap wrappers = new EnumMap<>(Direction.class); + + private boolean transmitting; + + public LaserCapabilityObject(@NotNull WorldPipeNode node) { + this.node = node; + for (Direction facing : GTUtil.DIRECTIONS) { + wrappers.put(facing, new Wrapper(facing)); + } + } + + @Override + public void init(@NotNull PipeBlockEntity tile, @NotNull PipeCapabilityWrapper wrapper) { + this.tile = tile; + } + + @Override + public long receiveLaser(long laserVoltage, long laserAmperage) { + return receiveLaser(laserVoltage, laserAmperage, null); + } + + protected long receiveLaser(long laserVoltage, long laserAmperage, Direction facing) { + long result = 0; + boolean earlyReturn = false; + if (tile != null && !this.transmitting) { + this.transmitting = true; + NetPath path = null; + if (node.getGroupUnsafe() == null || node.getGroupSafe().getNodes().size() == 1) + path = new SingletonNetPath(node); + else if (node.getGroupSafe().getData() instanceof PathCacheGroupData cache) { + Set actives = node.getGroupSafe().getNodesUnderKey(ACTIVE_KEY); + if (actives.size() > 2) { + earlyReturn = true;// single-destination contract violated + } else { + var iter = actives.iterator(); + NetNode target = iter.next(); + if (target == node) { + if (!iter.hasNext()) { + earlyReturn = true;// no destinations + } else { + target = iter.next(); + } + } + if (!earlyReturn) { + if (!(target instanceof WorldPipeNode)) { + earlyReturn = true;// useless target + } else { + path = cache.getOrCreate(node).getOrCompute(target); + if (path == null) { + earlyReturn = true;// no path + } + } + } + } + } else { + earlyReturn = true;// no cache to lookup with + } + if (!earlyReturn) { + long available = laserAmperage; + WorldPipeNode destination = (WorldPipeNode) path.getTargetNode(); + for (var capability : destination.getBlockEntity().getTargetsWithCapabilities(destination).entrySet()) { + if (destination == node && capability.getKey() == facing) + continue; // anti insert-to-our-source logic + ILaserRelay laser = capability.getValue() + .getCapability(GTCapability.CAPABILITY_LASER, + capability.getKey().getOpposite()) + .resolve().orElse(null); + if (laser != null && !(destination.getBlockEntity().getCoverHolder() + .getCoverAtSide(capability.getKey()) instanceof ShutterCover)) { + long transmitted = laser.receiveLaser(laserVoltage, laserAmperage); + if (transmitted > 0) { + SlowActiveWalker.dispatch(tile.getLevel(), path, 1, 2, 2); + available -= transmitted; + if (available <= 0) { + result = laserAmperage; + earlyReturn = true; + break; + } + } + } + } + if (!earlyReturn) { + result = laserAmperage - available; + } + } + this.transmitting = false; + } + + return result; + } + + @Override + public @NotNull LazyOptional getCapability(Capability cap, @Nullable Direction facing) { + return GTCapability.CAPABILITY_LASER.orEmpty(cap, + LazyOptional.of(() -> facing == null ? this : wrappers.get(facing))); + } + + protected class Wrapper implements ILaserRelay { + + private final Direction facing; + + public Wrapper(Direction facing) { + this.facing = facing; + } + + @Override + public long receiveLaser(long laserVoltage, long laserAmperage) { + return LaserCapabilityObject.this.receiveLaser(laserVoltage, laserAmperage, facing); + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/laser/WorldLaserNet.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/laser/WorldLaserNet.java new file mode 100644 index 0000000000..79b9d7824f --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/laser/WorldLaserNet.java @@ -0,0 +1,54 @@ +package com.gregtechceu.gtceu.common.pipelike.net.laser; + +import com.gregtechceu.gtceu.api.capability.forge.GTCapability; +import com.gregtechceu.gtceu.api.graphnet.group.GroupData; +import com.gregtechceu.gtceu.api.graphnet.group.PathCacheGroupData; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNet; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IPipeCapabilityObject; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeCapabilityWrapper; +import com.gregtechceu.gtceu.api.graphnet.traverse.NetBreadthIterator; + +import net.minecraft.server.level.ServerLevel; +import net.minecraftforge.common.capabilities.Capability; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class WorldLaserNet extends WorldPipeNet { + + private static final String DATA_ID = "gtceu_world_laser_net"; + + public static @NotNull WorldLaserNet getWorldNet(ServerLevel serverLevel) { + WorldLaserNet net = serverLevel.getDataStorage().computeIfAbsent(tag -> { + WorldLaserNet netx = new WorldLaserNet(); + netx.load(tag); + return netx; + }, WorldLaserNet::new, DATA_ID); + net.setLevel(serverLevel); + return net; + } + + public WorldLaserNet() { + super(false); + } + + @Override + public PipeCapabilityWrapper buildCapabilityWrapper(@NotNull PipeBlockEntity owner, @NotNull WorldPipeNode node) { + Object2ObjectOpenHashMap, IPipeCapabilityObject> map = new Object2ObjectOpenHashMap<>(); + map.put(GTCapability.CAPABILITY_LASER, new LaserCapabilityObject(node)); + return new PipeCapabilityWrapper(owner, node, map, 0, LaserCapabilityObject.ACTIVE_KEY); + } + + @Override + public @Nullable GroupData getBlankGroupData() { + return new PathCacheGroupData(NetBreadthIterator::new); + } + + @Override + public int getNetworkID() { + return 3; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/optical/DataCapabilityObject.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/optical/DataCapabilityObject.java new file mode 100644 index 0000000000..24284ec218 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/optical/DataCapabilityObject.java @@ -0,0 +1,131 @@ +package com.gregtechceu.gtceu.common.pipelike.net.optical; + +import com.gregtechceu.gtceu.api.capability.data.IDataAccess; +import com.gregtechceu.gtceu.api.capability.data.query.DataAccessFormat; +import com.gregtechceu.gtceu.api.capability.data.query.DataQueryObject; +import com.gregtechceu.gtceu.api.capability.forge.GTCapability; +import com.gregtechceu.gtceu.api.graphnet.group.PathCacheGroupData; +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; +import com.gregtechceu.gtceu.api.graphnet.path.NetPath; +import com.gregtechceu.gtceu.api.graphnet.path.SingletonNetPath; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IPipeCapabilityObject; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeCapabilityWrapper; +import com.gregtechceu.gtceu.common.cover.ShutterCover; +import com.gregtechceu.gtceu.common.pipelike.net.SlowActiveWalker; +import com.gregtechceu.gtceu.utils.GTUtil; + +import net.minecraft.core.Direction; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.util.LazyOptional; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.EnumMap; +import java.util.Set; + +public class DataCapabilityObject implements IPipeCapabilityObject, IDataAccess { + + public static final int ACTIVE_KEY = 122; + + private final WorldPipeNode node; + + private @Nullable PipeBlockEntity tile; + + private final EnumMap wrappers = new EnumMap<>(Direction.class); + + public DataCapabilityObject(@NotNull WorldPipeNode node) { + this.node = node; + for (Direction facing : GTUtil.DIRECTIONS) { + wrappers.put(facing, new Wrapper(facing)); + } + } + + @Override + public void init(@NotNull PipeBlockEntity tile, @NotNull PipeCapabilityWrapper wrapper) { + this.tile = tile; + } + + @Override + public boolean accessData(@NotNull DataQueryObject queryObject) { + return accessData(queryObject, null); + } + + private boolean accessData(@NotNull DataQueryObject queryObject, @Nullable Direction facing) { + if (tile == null) return false; + + NetPath path; + if (node.getGroupUnsafe() == null || node.getGroupSafe().getNodes().size() == 1) + path = new SingletonNetPath(node); + else if (node.getGroupSafe().getData() instanceof PathCacheGroupData cache) { + Set actives = node.getGroupSafe().getNodesUnderKey(ACTIVE_KEY); + if (actives.size() > 2) return false; // single-destination contract violated + var iter = actives.iterator(); + NetNode target = iter.next(); + if (target == node) { + if (!iter.hasNext()) return false; // no destinations + target = iter.next(); + } + if (!(target instanceof WorldPipeNode)) return false; // useless target + path = cache.getOrCreate(node).getOrCompute(target); + if (path == null) return false; // no path + } else return false; // no cache to lookup with + + WorldPipeNode destination = path.getTargetNode(); + for (var capability : destination.getBlockEntity().getTargetsWithCapabilities(destination).entrySet()) { + if (destination == node && capability.getKey() == facing) continue; // anti insert-to-our-source logic + IDataAccess access = capability.getValue() + .getCapability(GTCapability.CAPABILITY_DATA_ACCESS, + capability.getKey().getOpposite()) + .resolve().orElse(null); + if (access != null && !(destination.getBlockEntity().getCoverHolder() + .getCoverAtSide(capability.getKey()) instanceof ShutterCover)) { + queryObject.setShouldTriggerWalker(false); + boolean cancelled = access.accessData(queryObject); + if (queryObject.isShouldTriggerWalker()) { + // since we are a pull-based system, we need to reverse the path for it to look correct + SlowActiveWalker.dispatch(tile.getLevel(), path.reversed(), 1, 1, 5); + } + if (cancelled) return true; + } + } + return false; + } + + @Override + public @NotNull DataAccessFormat getFormat() { + return DataAccessFormat.UNIVERSAL; + } + + @Override + public @NotNull LazyOptional getCapability(Capability cap, @Nullable Direction facing) { + return GTCapability.CAPABILITY_DATA_ACCESS.orEmpty(cap, + LazyOptional.of(() -> facing == null ? this : wrappers.get(facing))); + } + + protected class Wrapper implements IDataAccess { + + private final Direction facing; + + public Wrapper(Direction facing) { + this.facing = facing; + } + + @Override + public boolean accessData(@NotNull DataQueryObject queryObject) { + return DataCapabilityObject.this.accessData(queryObject, facing); + } + + @Override + public @NotNull DataAccessFormat getFormat() { + return DataCapabilityObject.this.getFormat(); + } + + @Override + public boolean supportsQuery(@NotNull DataQueryObject queryObject) { + return DataCapabilityObject.this.supportsQuery(queryObject); + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/optical/WorldOpticalNet.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/optical/WorldOpticalNet.java new file mode 100644 index 0000000000..4075fd055c --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/common/pipelike/net/optical/WorldOpticalNet.java @@ -0,0 +1,54 @@ +package com.gregtechceu.gtceu.common.pipelike.net.optical; + +import com.gregtechceu.gtceu.api.capability.forge.GTCapability; +import com.gregtechceu.gtceu.api.graphnet.group.GroupData; +import com.gregtechceu.gtceu.api.graphnet.group.PathCacheGroupData; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNet; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNode; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.IPipeCapabilityObject; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeCapabilityWrapper; +import com.gregtechceu.gtceu.api.graphnet.traverse.NetBreadthIterator; + +import net.minecraft.server.level.ServerLevel; +import net.minecraftforge.common.capabilities.Capability; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class WorldOpticalNet extends WorldPipeNet { + + private static final String DATA_ID = "gtceu_world_optical_net"; + + public static WorldOpticalNet getWorldNet(ServerLevel serverLevel) { + WorldOpticalNet net = serverLevel.getDataStorage().computeIfAbsent(tag -> { + WorldOpticalNet netx = new WorldOpticalNet(); + netx.load(tag); + return netx; + }, WorldOpticalNet::new, DATA_ID); + net.setLevel(serverLevel); + return net; + } + + public WorldOpticalNet() { + super(false); + } + + @Override + public PipeCapabilityWrapper buildCapabilityWrapper(@NotNull PipeBlockEntity owner, @NotNull WorldPipeNode node) { + Object2ObjectOpenHashMap, IPipeCapabilityObject> map = new Object2ObjectOpenHashMap<>(); + map.put(GTCapability.CAPABILITY_DATA_ACCESS, new DataCapabilityObject(node)); + return new PipeCapabilityWrapper(owner, node, map, 0, DataCapabilityObject.ACTIVE_KEY); + } + + @Override + public @Nullable GroupData getBlankGroupData() { + return new PathCacheGroupData(NetBreadthIterator::new); + } + + @Override + public int getNetworkID() { + return 4; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/optical/LevelOpticalPipeNet.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/optical/LevelOpticalPipeNet.java deleted file mode 100644 index b3f35e4a7f..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/optical/LevelOpticalPipeNet.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.optical; - -import com.gregtechceu.gtceu.api.pipenet.LevelPipeNet; - -import net.minecraft.nbt.CompoundTag; -import net.minecraft.server.level.ServerLevel; - -public class LevelOpticalPipeNet extends LevelPipeNet { - - private static final String DATA_ID = "gtceu_optical_pipe_net"; - - public static LevelOpticalPipeNet getOrCreate(ServerLevel serverLevel) { - return serverLevel.getDataStorage().computeIfAbsent(tag -> new LevelOpticalPipeNet(serverLevel, tag), - () -> new LevelOpticalPipeNet(serverLevel), DATA_ID); - } - - public LevelOpticalPipeNet(ServerLevel level) { - super(level); - } - - public LevelOpticalPipeNet(ServerLevel serverLevel, CompoundTag tag) { - super(serverLevel, tag); - } - - @Override - protected OpticalPipeNet createNetInstance() { - return new OpticalPipeNet(this); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/optical/OpticalNetHandler.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/optical/OpticalNetHandler.java deleted file mode 100644 index 9ea4e426f2..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/optical/OpticalNetHandler.java +++ /dev/null @@ -1,126 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.optical; - -import com.gregtechceu.gtceu.api.capability.IDataAccessHatch; -import com.gregtechceu.gtceu.api.capability.IOpticalComputationProvider; -import com.gregtechceu.gtceu.api.capability.IOpticalDataAccessHatch; -import com.gregtechceu.gtceu.api.recipe.GTRecipe; -import com.gregtechceu.gtceu.common.blockentity.OpticalPipeBlockEntity; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.world.level.Level; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Collection; - -public class OpticalNetHandler implements IDataAccessHatch, IOpticalComputationProvider { - - private final OpticalPipeBlockEntity pipe; - private final Level world; - private final Direction facing; - - private OpticalPipeNet net; - - public OpticalNetHandler(OpticalPipeNet net, @NotNull OpticalPipeBlockEntity pipe, @Nullable Direction facing) { - this.net = net; - this.pipe = pipe; - this.facing = facing; - this.world = pipe.getLevel(); - } - - public void updateNetwork(OpticalPipeNet net) { - this.net = net; - } - - public OpticalPipeNet getNet() { - return net; - } - - @Override - public boolean isRecipeAvailable(@NotNull GTRecipe recipe, @NotNull Collection seen) { - boolean isAvailable = traverseRecipeAvailable(recipe, seen); - if (isAvailable) setPipesActive(); - return isAvailable; - } - - @Override - public boolean isCreative() { - return false; - } - - @Override - public int requestCWUt(int cwut, boolean simulate, @NotNull Collection seen) { - int provided = traverseRequestCWUt(cwut, simulate, seen); - if (provided > 0) setPipesActive(); - return provided; - } - - @Override - public int getMaxCWUt(@NotNull Collection seen) { - return traverseMaxCWUt(seen); - } - - @Override - public boolean canBridge(@NotNull Collection seen) { - return traverseCanBridge(seen); - } - - private void setPipesActive() { - for (BlockPos pos : net.getAllNodes().keySet()) { - if (world.getBlockEntity(pos) instanceof OpticalPipeBlockEntity opticalPipe) { - opticalPipe.setActive(true, 100); - } - } - } - - private boolean isNetInvalidForTraversal() { - return net == null || pipe == null || pipe.isInValid(); - } - - private boolean traverseRecipeAvailable(@NotNull GTRecipe recipe, @NotNull Collection seen) { - if (isNetInvalidForTraversal()) return false; - - OpticalRoutePath inv = net.getNetData(pipe.getPipePos(), facing); - if (inv == null) return false; - - IOpticalDataAccessHatch hatch = inv.getDataHatch(); - if (hatch == null || seen.contains(hatch)) return false; - - if (hatch.isTransmitter()) { - return hatch.isRecipeAvailable(recipe, seen); - } - return false; - } - - private int traverseRequestCWUt(int cwut, boolean simulate, @NotNull Collection seen) { - IOpticalComputationProvider provider = getComputationProvider(seen); - if (provider == null) return 0; - return provider.requestCWUt(cwut, simulate, seen); - } - - private int traverseMaxCWUt(@NotNull Collection seen) { - IOpticalComputationProvider provider = getComputationProvider(seen); - if (provider == null) return 0; - return provider.getMaxCWUt(seen); - } - - private boolean traverseCanBridge(@NotNull Collection seen) { - IOpticalComputationProvider provider = getComputationProvider(seen); - if (provider == null) return true; // nothing found, so don't report a problem, just pass quietly - return provider.canBridge(seen); - } - - @Nullable - private IOpticalComputationProvider getComputationProvider(@NotNull Collection seen) { - if (isNetInvalidForTraversal()) return null; - - OpticalRoutePath inv = net.getNetData(pipe.getPipePos(), facing); - if (inv == null) return null; - - IOpticalComputationProvider hatch = inv.getComputationHatch(); - if (hatch == null || seen.contains(hatch)) return null; - return hatch; - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/optical/OpticalNetWalker.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/optical/OpticalNetWalker.java deleted file mode 100644 index 16711b1e8a..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/optical/OpticalNetWalker.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.optical; - -import com.gregtechceu.gtceu.api.capability.forge.GTCapability; -import com.gregtechceu.gtceu.api.pipenet.PipeNetWalker; -import com.gregtechceu.gtceu.common.blockentity.OpticalPipeBlockEntity; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.world.level.block.entity.BlockEntity; - -import org.jetbrains.annotations.Nullable; - -public class OpticalNetWalker extends PipeNetWalker { - - public static final OpticalRoutePath FAILED_MARKER = new OpticalRoutePath(null, null, 0); - - @Nullable - public static OpticalRoutePath createNetData(OpticalPipeNet world, BlockPos sourcePipe, - Direction faceToSourceHandler) { - OpticalNetWalker walker = new OpticalNetWalker(world, sourcePipe, 1); - walker.sourcePipe = sourcePipe; - walker.facingToHandler = faceToSourceHandler; - walker.traversePipeNet(); - return walker.isFailed() ? FAILED_MARKER : walker.routePath; - } - - private OpticalRoutePath routePath; - private BlockPos sourcePipe; - private Direction facingToHandler; - - protected OpticalNetWalker(OpticalPipeNet world, BlockPos sourcePipe, int distance) { - super(world, sourcePipe, distance); - } - - @Override - protected PipeNetWalker createSubWalker(OpticalPipeNet world, - Direction facingToNextPos, - BlockPos nextPos, - int walkedBlocks) { - OpticalNetWalker walker = new OpticalNetWalker(world, nextPos, walkedBlocks); - walker.facingToHandler = facingToHandler; - walker.sourcePipe = sourcePipe; - return walker; - } - - @Override - protected void checkPipe(OpticalPipeBlockEntity pipeTile, BlockPos pos) {} - - @Override - protected void checkNeighbour(OpticalPipeBlockEntity pipeTile, BlockPos pipePos, Direction faceToNeighbour, - @Nullable BlockEntity neighbourTile) { - if (neighbourTile == null || - (pipePos.equals(sourcePipe) && faceToNeighbour == facingToHandler)) { - return; - } - - if (((OpticalNetWalker) root).routePath == null) { - if (neighbourTile.getCapability(GTCapability.CAPABILITY_DATA_ACCESS, - faceToNeighbour.getOpposite()).isPresent() || - neighbourTile.getCapability(GTCapability.CAPABILITY_COMPUTATION_PROVIDER, - faceToNeighbour.getOpposite()).isPresent()) { - ((OpticalNetWalker) root).routePath = new OpticalRoutePath(pipeTile, faceToNeighbour, - getWalkedBlocks()); - stop(); - } - } - } - - @Override - protected Class getBasePipeClass() { - return OpticalPipeBlockEntity.class; - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/optical/OpticalPipeNet.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/optical/OpticalPipeNet.java deleted file mode 100644 index f4b52a2a06..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/optical/OpticalPipeNet.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.optical; - -import com.gregtechceu.gtceu.api.pipenet.LevelPipeNet; -import com.gregtechceu.gtceu.api.pipenet.Node; -import com.gregtechceu.gtceu.api.pipenet.PipeNet; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.nbt.CompoundTag; - -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import org.jetbrains.annotations.Nullable; - -import java.util.Map; - -public class OpticalPipeNet extends PipeNet { - - private final Map NET_DATA = new Object2ObjectOpenHashMap<>(); - - public OpticalPipeNet(LevelPipeNet> world) { - super(world); - } - - @Nullable - public OpticalRoutePath getNetData(BlockPos pipePos, Direction facing) { - if (NET_DATA.containsKey(pipePos)) { - return NET_DATA.get(pipePos); - } - OpticalRoutePath data = OpticalNetWalker.createNetData(this, pipePos, facing); - if (data == OpticalNetWalker.FAILED_MARKER) { - // walker failed, don't cache, so it tries again on next insertion - return null; - } - - NET_DATA.put(pipePos, data); - return data; - } - - @Override - public void onNeighbourUpdate(BlockPos fromPos) { - NET_DATA.clear(); - } - - @Override - public void onPipeConnectionsUpdate() { - NET_DATA.clear(); - } - - @Override - protected void transferNodeData(Map> transferredNodes, - PipeNet parentNet) { - super.transferNodeData(transferredNodes, parentNet); - NET_DATA.clear(); - ((OpticalPipeNet) parentNet).NET_DATA.clear(); - } - - @Override - protected void writeNodeData(OpticalPipeProperties nodeData, CompoundTag tagCompound) {} - - @Override - protected OpticalPipeProperties readNodeData(CompoundTag tagCompound) { - return OpticalPipeProperties.INSTANCE; - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/optical/OpticalPipeProperties.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/optical/OpticalPipeProperties.java deleted file mode 100644 index 8bdd971470..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/optical/OpticalPipeProperties.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.optical; - -public class OpticalPipeProperties { - - public static final OpticalPipeProperties INSTANCE = new OpticalPipeProperties(); -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/optical/OpticalPipeType.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/optical/OpticalPipeType.java deleted file mode 100644 index bb64d6a98e..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/optical/OpticalPipeType.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.optical; - -import com.gregtechceu.gtceu.GTCEu; -import com.gregtechceu.gtceu.api.pipenet.IPipeType; - -import net.minecraft.resources.ResourceLocation; -import net.minecraft.util.StringRepresentable; - -import java.util.Locale; - -public enum OpticalPipeType implements IPipeType, StringRepresentable { - - NORMAL; - - public static final ResourceLocation TYPE = GTCEu.id("optical"); - - @Override - public float getThickness() { - return 0.375F; - } - - @Override - public OpticalPipeProperties modifyProperties(OpticalPipeProperties baseProperties) { - return baseProperties; - } - - @Override - public boolean isPaintable() { - return true; - } - - @Override - public ResourceLocation type() { - return TYPE; - } - - @Override - public String getSerializedName() { - return name().toLowerCase(Locale.ROOT); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/common/pipelike/optical/OpticalRoutePath.java b/src/main/java/com/gregtechceu/gtceu/common/pipelike/optical/OpticalRoutePath.java deleted file mode 100644 index 07ccff2fe6..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/common/pipelike/optical/OpticalRoutePath.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.gregtechceu.gtceu.common.pipelike.optical; - -import com.gregtechceu.gtceu.api.capability.GTCapabilityHelper; -import com.gregtechceu.gtceu.api.capability.IDataAccessHatch; -import com.gregtechceu.gtceu.api.capability.IOpticalComputationProvider; -import com.gregtechceu.gtceu.api.capability.IOpticalDataAccessHatch; -import com.gregtechceu.gtceu.api.capability.forge.GTCapability; -import com.gregtechceu.gtceu.api.pipenet.IRoutePath; -import com.gregtechceu.gtceu.common.blockentity.OpticalPipeBlockEntity; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.world.level.Level; - -import lombok.Getter; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class OpticalRoutePath implements IRoutePath { - - @Getter - private final OpticalPipeBlockEntity targetPipe; - @Getter - private final Direction targetFacing; - @Getter - private final int distance; - - public OpticalRoutePath(OpticalPipeBlockEntity targetPipe, Direction targetFacing, int distance) { - this.targetPipe = targetPipe; - this.targetFacing = targetFacing; - this.distance = distance; - } - - @Nullable - public IOpticalDataAccessHatch getDataHatch() { - IDataAccessHatch dataAccessHatch = getTargetCapability(GTCapability.CAPABILITY_DATA_ACCESS, - targetPipe.getPipeLevel()); - return dataAccessHatch instanceof IOpticalDataAccessHatch opticalHatch ? opticalHatch : null; - } - - @Nullable - public IOpticalComputationProvider getComputationHatch() { - return getTargetCapability(GTCapability.CAPABILITY_COMPUTATION_PROVIDER, targetPipe.getPipeLevel()); - } - - @Override - public @NotNull BlockPos getTargetPipePos() { - return targetPipe.getPipePos(); - } - - @Nullable - @Override - public IOpticalComputationProvider getHandler(Level world) { - return GTCapabilityHelper.getOpticalComputationProvider(world, getTargetPipePos().relative(targetFacing), - targetFacing.getOpposite()); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/config/ConfigHolder.java b/src/main/java/com/gregtechceu/gtceu/config/ConfigHolder.java index 068aba8c5e..266d377a5a 100644 --- a/src/main/java/com/gregtechceu/gtceu/config/ConfigHolder.java +++ b/src/main/java/com/gregtechceu/gtceu/config/ConfigHolder.java @@ -136,6 +136,12 @@ public static class RecipeConfigs { public static class CompatibilityConfigs { + @Configurable + @Configurable.Comment({ "Whether to run datafixers on world load.", + "Do note that mods like ModernFix will likely interfere with this.", + "Default: true" }) + public boolean doDatafixers = true; + @Configurable @Configurable.Comment("Config options regarding GTEU compatibility with other energy systems") public EnergyCompatConfig energy = new EnergyCompatConfig(); @@ -738,6 +744,11 @@ public static class ClientConfigs { @Configurable.Comment({ "Duration of UI animations in ms", "Default: 300" }) @Configurable.Range(min = 1) public int animationTime = 300; + + @Configurable + @Configurable.Comment({ "Prevent optical and laser cables from animating when active.", "Default: false" }) + public boolean preventAnimatedCables = false; + @Configurable public ArmorHud armorHud = new ArmorHud(); @Configurable diff --git a/src/main/java/com/gregtechceu/gtceu/core/MixinHelpers.java b/src/main/java/com/gregtechceu/gtceu/core/MixinHelpers.java index 871c95c47b..a59e8a2a7c 100644 --- a/src/main/java/com/gregtechceu/gtceu/core/MixinHelpers.java +++ b/src/main/java/com/gregtechceu/gtceu/core/MixinHelpers.java @@ -2,6 +2,7 @@ import com.gregtechceu.gtceu.api.GTCEuAPI; import com.gregtechceu.gtceu.api.GTValues; +import com.gregtechceu.gtceu.api.addon.AddonFinder; import com.gregtechceu.gtceu.api.data.chemical.ChemicalHelper; import com.gregtechceu.gtceu.api.data.chemical.material.Material; import com.gregtechceu.gtceu.api.data.chemical.material.properties.FluidProperty; @@ -58,8 +59,8 @@ public class MixinHelpers { - public static void generateGTDynamicTags(Map> tagMap, - Registry registry) { + public static void generateGTDynamicTags(final Map> tagMap, + final Registry registry) { if (registry == BuiltInRegistries.ITEM) { ChemicalHelper.UNIFICATION_ENTRY_ITEM.forEach((entry, itemLikes) -> { if (itemLikes.isEmpty()) return; @@ -95,10 +96,7 @@ public static void generateGTDynamicTags(Map { MixinHelpers.addMaterialBlockTags(tagMap, prefix, map); }); - GTMaterialBlocks.FLUID_PIPE_BLOCKS.rowMap().forEach((prefix, map) -> { - MixinHelpers.addMaterialBlockTags(tagMap, prefix, map); - }); - GTMaterialBlocks.ITEM_PIPE_BLOCKS.rowMap().forEach((prefix, map) -> { + GTMaterialBlocks.MATERIAL_PIPE_BLOCKS.rowMap().forEach((prefix, map) -> { MixinHelpers.addMaterialBlockTags(tagMap, prefix, map); }); GTRegistries.MACHINES.forEach(machine -> { @@ -150,6 +148,7 @@ public static void generateGTDynamicTags(Map addon.loadDynamicTags(tagMap, registry)); } public static void addMaterialBlockTags(Map> tagMap, @@ -190,7 +189,7 @@ public static void addMaterialBlockTags(Map lootTables) { + public static void generateGTDynamicLoot(final Map lootTables) { GTMaterialBlocks.MATERIAL_BLOCKS.rowMap().forEach((prefix, map) -> { if (TagPrefix.ORES.containsKey(prefix)) { final TagPrefix.OreType type = TagPrefix.ORES.get(prefix); @@ -245,10 +244,7 @@ public static void generateGTDynamicLoot(Map lootTa GTMaterialBlocks.CABLE_BLOCKS.rowMap().forEach((prefix, map) -> { MixinHelpers.addMaterialBlockLootTables(lootTables, prefix, map); }); - GTMaterialBlocks.FLUID_PIPE_BLOCKS.rowMap().forEach((prefix, map) -> { - MixinHelpers.addMaterialBlockLootTables(lootTables, prefix, map); - }); - GTMaterialBlocks.ITEM_PIPE_BLOCKS.rowMap().forEach((prefix, map) -> { + GTMaterialBlocks.MATERIAL_PIPE_BLOCKS.rowMap().forEach((prefix, map) -> { MixinHelpers.addMaterialBlockLootTables(lootTables, prefix, map); }); GTMaterialBlocks.SURFACE_ROCK_BLOCKS.forEach((material, blockEntry) -> { @@ -269,6 +265,7 @@ public static void generateGTDynamicLoot(Map lootTa lootTables.put(lootTableId, BLOCK_LOOT.createSingleItemTable(block).setParamSet(LootContextParamSets.BLOCK).build()); }); + AddonFinder.getAddons().forEach(addon -> addon.loadDynamicLoot(lootTables, BLOCK_LOOT)); } public static void addMaterialBlockLootTables(Map lootTables, TagPrefix prefix, diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/BlockEntityMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/BlockEntityMixin.java new file mode 100644 index 0000000000..8a9558b3a9 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/BlockEntityMixin.java @@ -0,0 +1,27 @@ +package com.gregtechceu.gtceu.core.mixins; + +import com.gregtechceu.gtceu.common.data.GTBlockEntities; + +import net.minecraft.world.level.block.entity.BlockEntity; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyArg; + +// TODO figure out better solution??? forge doesn't want to remap BEs for w/e reason +@Mixin(BlockEntity.class) +public class BlockEntityMixin { + + @ModifyArg(method = "loadStatic", + at = @At(value = "INVOKE", + target = "Lnet/minecraft/resources/ResourceLocation;tryParse(Ljava/lang/String;)Lnet/minecraft/resources/ResourceLocation;")) + private static String gtceu$remapBlockEntityLoadBecauseDataFixDoesntWantToWork(String s) { + switch (s) { + case "gtceu:cable", "gtceu:fluid_pipe", "gtceu:item_pipe" -> s = GTBlockEntities.MATERIAL_PIPE.getId() + .toString(); + case "gtceu:laser_pipe", "gtceu:optical_pipe" -> s = GTBlockEntities.ACTIVATABLE_PIPE.getId().toString(); + case "gtceu:duct_pipe" -> s = GTBlockEntities.PIPE.getId().toString(); + } + return s; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/ChunkSerializerMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/ChunkSerializerMixin.java new file mode 100644 index 0000000000..845ab6103e --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/ChunkSerializerMixin.java @@ -0,0 +1,23 @@ +package com.gregtechceu.gtceu.core.mixins; + +import com.gregtechceu.gtceu.api.datafixer.DataFixesInternals; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.level.chunk.storage.ChunkSerializer; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyVariable; + +@Mixin(ChunkSerializer.class) +public abstract class ChunkSerializerMixin { + + @ModifyVariable( + method = "write", + at = @At(value = "INVOKE", + target = "Lnet/minecraft/nbt/CompoundTag;putInt(Ljava/lang/String;I)V", + ordinal = 0)) + private static CompoundTag addModDataVersions(CompoundTag compound) { + return DataFixesInternals.get().addModDataVersions(compound); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/DataFixTypesMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/DataFixTypesMixin.java new file mode 100644 index 0000000000..2c3d2ab48e --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/DataFixTypesMixin.java @@ -0,0 +1,28 @@ +package com.gregtechceu.gtceu.core.mixins; + +import com.gregtechceu.gtceu.api.datafixer.DataFixesInternals; + +import net.minecraft.util.datafix.DataFixTypes; + +import com.llamalad7.mixinextras.injector.ModifyReturnValue; +import com.mojang.datafixers.DSL; +import com.mojang.serialization.Dynamic; +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; + +@Mixin(DataFixTypes.class) +public class DataFixTypesMixin { + + @Shadow + @Final + private DSL.TypeReference type; + + // ModifyReturnValue to inject our fixes *after* vanilla ones + @ModifyReturnValue(method = "update(Lcom/mojang/datafixers/DataFixer;Lcom/mojang/serialization/Dynamic;II)Lcom/mojang/serialization/Dynamic;", + at = @At(value = "RETURN")) + private Dynamic gtceu$injectDataFixers(Dynamic value) { + return DataFixesInternals.get().updateWithAllFixers(this.type, value); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/HotbarManagerMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/HotbarManagerMixin.java new file mode 100644 index 0000000000..1816463f50 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/HotbarManagerMixin.java @@ -0,0 +1,25 @@ +package com.gregtechceu.gtceu.core.mixins; + +import com.gregtechceu.gtceu.api.datafixer.DataFixesInternals; + +import net.minecraft.client.HotbarManager; +import net.minecraft.nbt.CompoundTag; + +import com.llamalad7.mixinextras.sugar.Local; +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; + +@Mixin(HotbarManager.class) +public abstract class HotbarManagerMixin { + + @Inject( + method = "save", + at = @At(value = "INVOKE", + target = "Lnet/minecraft/nbt/NbtIo;write(Lnet/minecraft/nbt/CompoundTag;Ljava/io/File;)V", + shift = At.Shift.AFTER)) + private void addModDataVersions(CallbackInfo ci, @Local CompoundTag tag) { + DataFixesInternals.get().addModDataVersions(tag); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/IHolderReferenceAccessor.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/IHolderReferenceAccessor.java deleted file mode 100644 index 3a2bc3bdbe..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/core/mixins/IHolderReferenceAccessor.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.gregtechceu.gtceu.core.mixins; - -import net.minecraft.core.Holder; - -import org.spongepowered.asm.mixin.Mixin; - -@Mixin(Holder.Reference.class) -public interface IHolderReferenceAccessor extends Holder { - - // @Invoker - // void invokeBind(ResourceKey key, T value); -} diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/IOWorkerMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/IOWorkerMixin.java new file mode 100644 index 0000000000..7a7e1698c3 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/IOWorkerMixin.java @@ -0,0 +1,26 @@ +package com.gregtechceu.gtceu.core.mixins; + +import com.gregtechceu.gtceu.api.datafixer.DataFixesInternals; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.chunk.storage.IOWorker; + +import org.jetbrains.annotations.Nullable; +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.concurrent.CompletableFuture; + +@Mixin(IOWorker.class) +public class IOWorkerMixin { + + @Inject(method = "store", at = @At("HEAD")) + private void storeDFUVersion(ChunkPos chunkPos, @Nullable CompoundTag chunkData, + CallbackInfoReturnable> cir) { + if (chunkData != null) + DataFixesInternals.get().addModDataVersions(chunkData); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/LevelMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/LevelMixin.java index 2d80a249d7..b7b86fb094 100644 --- a/src/main/java/com/gregtechceu/gtceu/core/mixins/LevelMixin.java +++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/LevelMixin.java @@ -38,7 +38,7 @@ private ChunkAccess getChunkNow(int pX, int pZ) { } @Inject(method = "getBlockEntity", at = @At(value = "HEAD"), cancellable = true) - private void getTileEntity(BlockPos pos, CallbackInfoReturnable cir) { + private void getBlockEntity(BlockPos pos, CallbackInfoReturnable cir) { if (!this.isClientSide && Thread.currentThread() != this.thread && (MultiblockWorldSavedData.isThreadService() || AsyncThreadData.isThreadService()) && isLoaded(pos)) { ChunkAccess chunk = this.getChunkNow(pos.getX() >> 4, pos.getZ() >> 4); diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/ModelManagerMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/ModelManagerMixin.java index af815c8acd..f2318872f9 100644 --- a/src/main/java/com/gregtechceu/gtceu/core/mixins/ModelManagerMixin.java +++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/ModelManagerMixin.java @@ -1,6 +1,8 @@ package com.gregtechceu.gtceu.core.mixins; import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.addon.AddonFinder; +import com.gregtechceu.gtceu.api.addon.IGTAddon; import com.gregtechceu.gtceu.client.renderer.block.MaterialBlockRenderer; import com.gregtechceu.gtceu.client.renderer.block.OreBlockRenderer; import com.gregtechceu.gtceu.client.renderer.block.SurfaceRockRenderer; @@ -40,6 +42,8 @@ public abstract class ModelManagerMixin { ToolItemRenderer.reinitModels(); SurfaceRockRenderer.reinitModels(); GTModels.registerMaterialFluidModels(); + GTModels.registerPipeModels(); + AddonFinder.getAddons().forEach(IGTAddon::loadDynamicResources); GTCEu.LOGGER.info("GregTech Model loading took {}ms", System.currentTimeMillis() - startTime); } } diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/NbtUtilsMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/NbtUtilsMixin.java new file mode 100644 index 0000000000..8cc25712f4 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/NbtUtilsMixin.java @@ -0,0 +1,19 @@ +package com.gregtechceu.gtceu.core.mixins; + +import com.gregtechceu.gtceu.api.datafixer.DataFixesInternals; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtUtils; + +import com.llamalad7.mixinextras.injector.ModifyReturnValue; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(NbtUtils.class) +public class NbtUtilsMixin { + + @ModifyReturnValue(method = "addDataVersion", at = @At("RETURN")) + private static CompoundTag gtceu$addModDataVersion(CompoundTag tag) { + return DataFixesInternals.get().addModDataVersions(tag); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/ServerChunkProviderMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/ServerChunkProviderMixin.java index 45bb8c8542..c540d63914 100644 --- a/src/main/java/com/gregtechceu/gtceu/core/mixins/ServerChunkProviderMixin.java +++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/ServerChunkProviderMixin.java @@ -59,7 +59,7 @@ private void injectClearCache(CallbackInfo ci) { } @Inject(method = "getChunkNow", at = @At(value = "HEAD"), cancellable = true) - private void getTileEntity(int pChunkX, int pChunkZ, CallbackInfoReturnable cir) { + private void getBlockEntity(int pChunkX, int pChunkZ, CallbackInfoReturnable cir) { if (Thread.currentThread() != this.mainThread && (MultiblockWorldSavedData.isThreadService() || AsyncThreadData.isThreadService())) { long i = ChunkPos.asLong(pChunkX, pChunkZ); diff --git a/src/main/java/com/gregtechceu/gtceu/core/mixins/StructureMixin.java b/src/main/java/com/gregtechceu/gtceu/core/mixins/StructureMixin.java new file mode 100644 index 0000000000..cc956bf3ea --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/core/mixins/StructureMixin.java @@ -0,0 +1,22 @@ +package com.gregtechceu.gtceu.core.mixins; + +import com.gregtechceu.gtceu.api.datafixer.DataFixesInternals; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate; + +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; + +@Mixin(StructureTemplate.class) +public abstract class StructureMixin { + + @Inject(method = "save", at = @At("TAIL"), cancellable = true) + private void addModDataVersions(CompoundTag compound, CallbackInfoReturnable cir) { + CompoundTag out = cir.getReturnValue(); + DataFixesInternals.get().addModDataVersions(out); + cir.setReturnValue(out); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/data/lang/IntegrationLang.java b/src/main/java/com/gregtechceu/gtceu/data/lang/IntegrationLang.java index 4752f5be1c..cfeff89a16 100644 --- a/src/main/java/com/gregtechceu/gtceu/data/lang/IntegrationLang.java +++ b/src/main/java/com/gregtechceu/gtceu/data/lang/IntegrationLang.java @@ -16,7 +16,9 @@ public static void init(RegistrateLangProvider provider) { initMinimapLang(provider); } - /** JEI, REI, EMI */ + /** + * JEI, REI, EMI + */ private static void initRecipeViewerLang(RegistrateLangProvider provider) { provider.add("gtceu.jei.multiblock_info", "Multiblock Info"); provider.add("gtceu.jei.ore_processing_diagram", "Ore Processing Diagram"); @@ -44,7 +46,9 @@ private static void initRecipeViewerLang(RegistrateLangProvider provider) { provider.add("gtceu.rei.group.potion_fluids", "Potion Fluids"); } - /** Jade, TheOneProbe, WTHIT */ + /** + * Jade, TheOneProbe, WTHIT + */ private static void initWailaLikeLang(RegistrateLangProvider provider) { provider.add("gtceu.top.working_disabled", "Working Disabled"); provider.add("gtceu.top.energy_consumption", "Using"); @@ -83,6 +87,9 @@ private static void initWailaLikeLang(RegistrateLangProvider provider) { provider.add("gtceu.top.allow_output_input", "Allow Input"); provider.add("gtceu.top.cable_voltage", "Voltage: "); provider.add("gtceu.top.cable_amperage", "Amperage: "); + provider.add("gtceu.top.pipe.fluid_last", "Last Fluid: "); + provider.add("gtceu.top.pipe.item_last", "Last Item: "); + provider.add("gtceu.top.exhaust_vent_direction", "Exhaust Vent: %s"); provider.add("gtceu.top.exhaust_vent_blocked", "Blocked"); provider.add("gtceu.top.machine_mode", "Machine Mode: "); @@ -100,6 +107,11 @@ private static void initWailaLikeLang(RegistrateLangProvider provider) { provider.add("gtceu.top.progress_computation", " / %s CWU"); provider.add("gtceu.top.progress_sec", " / %s s"); provider.add("gtceu.top.progress_tick", " / %s t"); + + provider.add("gregtech.top.pipe.voltage", "Average Voltage / s:"); + provider.add("gregtech.top.pipe.amperage", "Average Amperage / s:"); + provider.add("gregtech.top.pipe.fluid_last", "Last Fluid:"); + provider.add("gregtech.top.pipe.item_last", "Last Item:"); } private static void initMinimapLang(RegistrateLangProvider provider) { diff --git a/src/main/java/com/gregtechceu/gtceu/data/lang/LangHandler.java b/src/main/java/com/gregtechceu/gtceu/data/lang/LangHandler.java index f106ee8137..8ac86aa4af 100644 --- a/src/main/java/com/gregtechceu/gtceu/data/lang/LangHandler.java +++ b/src/main/java/com/gregtechceu/gtceu/data/lang/LangHandler.java @@ -386,12 +386,13 @@ public static void init(RegistrateLangProvider provider) { provider.add("cover.conveyor.mode", "Mode: %s"); provider.add("cover.conveyor.mode.export", "Mode: Export"); provider.add("cover.conveyor.mode.import", "Mode: Import"); - multilineLang(provider, "cover.conveyor.distribution.round_robin_global", - "Distribution Mode: §bRound Robin\n§7Splits items equally across connected inventories"); - multilineLang(provider, "cover.conveyor.distribution.round_robin_prio", - "Distribution Mode: §bRound Robin with Priority\n§7Tries to split items across connected inventories and considers higher priorities first.\n§7Restrictive item pipes lower the priority of a path."); - multilineLang(provider, "cover.conveyor.distribution.insert_first", - "Distribution Mode: §bPriority\n§7Will insert into the first inventory with the highest priority it can find.\n§7Restrictive item pipes lower the priority of a path."); + provider.add("cover.generic.distribution.name", "Distribution Mode"); + multilineLang(provider, "cover.generic.distribution.equalized", + "§bEqual Distribution§r\n§7Fills all destinations by the same amount per operation.\n§cMay be computationally expensive. Use sparingly."); + multilineLang(provider, "cover.generic.distribution.round_robin", + "§bRound Robin§r\n§7Fills destinations in a fixed order, but does not equalize."); + multilineLang(provider, "cover.generic.distribution.flood", + "§bFlood Insert§r\n§7Fills destinations based on their priorities and does not equalize."); multilineLang(provider, "cover.conveyor.blocks_input.enabled", "If enabled, items will not be inserted when cover is set to pull items from the inventory into pipe.\n§aEnabled"); multilineLang(provider, "cover.conveyor.blocks_input.disabled", @@ -501,9 +502,9 @@ public static void init(RegistrateLangProvider provider) { replace(provider, GTMaterials.TungstenSteel.getUnlocalizedName(), "Tungstensteel"); replace(provider, GTMaterials.Iron3Chloride.getUnlocalizedName(), "Iron III Chloride"); replace(provider, GTMaterials.Iron2Chloride.getUnlocalizedName(), "Iron II Chloride"); - replace(provider, GTMaterials.OilHeavy.getUnlocalizedName(), "Heavy Oil"); + replace(provider, GTMaterials.HeavyOil.getUnlocalizedName(), "Heavy Oil"); replace(provider, "block.gtceu.oil_heavy", "Heavy Oil"); - replace(provider, GTMaterials.OilLight.getUnlocalizedName(), "Light Oil"); + replace(provider, GTMaterials.LightOil.getUnlocalizedName(), "Light Oil"); replace(provider, "block.gtceu.oil_light", "Light Oil"); replace(provider, GTMaterials.RawOil.getUnlocalizedName(), "Raw Oil"); replace(provider, "block.gtceu.oil_medium", "Raw Oil"); @@ -789,8 +790,8 @@ public static void init(RegistrateLangProvider provider) { "Caused %s Lag Spike Warnings (anything taking longer than %sms) on the Server."); provider.add("behavior.portable_scanner.debug_machine", "Meta-ID: %s"); provider.add("behavior.portable_scanner.debug_machine_invalid", " invalid!"); - provider.add("behavior.portable_scanner.debug_machine_invalid_null=invalid! MetaTileEntity =", - " null!"); + provider.add("behavior.portable_scanner.debug_machine_invalid_null", + "invalid! MetaTileEntity = null!"); provider.add("behavior.portable_scanner.debug_machine_valid", " valid"); provider.add("behavior.portable_scanner.divider", "========================="); provider.add("behavior.portable_scanner.energy_container_in", "Max IN: %s (%s) EU at %s A"); @@ -798,6 +799,7 @@ public static void init(RegistrateLangProvider provider) { provider.add("behavior.portable_scanner.energy_container_storage", "Energy: %s EU / %s EU"); provider.add("behavior.portable_scanner.eu_per_sec", "Average (last second): %s EU/t"); provider.add("behavior.portable_scanner.amp_per_sec", "Average (last second): %s A"); + provider.add("behavior.portable_scanner.temperature", "Temperature: %sK"); provider.add("behavior.portable_scanner.machine_disabled", "Disabled."); provider.add("behavior.portable_scanner.machine_front_facing", "Front Facing: %s"); provider.add("behavior.portable_scanner.machine_ownership", "§2Machine Owner Type: %s§r"); @@ -1083,14 +1085,17 @@ public static void init(RegistrateLangProvider provider) { provider.add("gtceu.cable.loss_per_block", "Loss/Meter/Ampere: §c%d§7 EU-Volt"); provider.add("gtceu.cable.superconductor", "§d%s Superconductor"); provider.add("gtceu.fluid_pipe.capacity", "§9Capacity: §f%d mB"); - provider.add("gtceu.fluid_pipe.max_temperature", "§cTemperature Limit: §f%d K"); + provider.add("gtceu.fluid_pipe.max_temperature", "§cMax Temperature: §f%s K"); + provider.add("gtceu.fluid_pipe.min_temperature", "§cMin Temperature: §f%s K"); provider.add("gtceu.fluid_pipe.channels", "§eChannels: §f%d"); provider.add("gtceu.fluid_pipe.gas_proof", "§6Can handle Gases"); provider.add("gtceu.fluid_pipe.acid_proof", "§6Can handle Acids"); provider.add("gtceu.fluid_pipe.cryo_proof", "§6Can handle Cryogenics"); provider.add("gtceu.fluid_pipe.plasma_proof", "§6Can handle all Plasmas"); provider.add("gtceu.fluid_pipe.not_gas_proof", "§4Gases may leak!"); - provider.add("gtceu.item_pipe.priority", "§9Priority: §f%d"); + provider.add("gtceu.pipe.priority", "§9Priority: §f%d"); + provider.add("gtceu.pipe.fluid_pipe", "§dFluid Pipe"); + provider.add("gtceu.pipe.item_pipe", "§dItem Pipe"); provider.add("gtceu.duct_pipe.transfer_rate", "§bAir transfer rate: %s"); provider.add("gtceu.multiblock.work_paused", "Work Paused."); provider.add("gtceu.multiblock.running", "Running perfectly."); @@ -1262,6 +1267,7 @@ public static void init(RegistrateLangProvider provider) { provider.add("config.jade.plugin_gtceu.primitive_pump", "[GTCEu] Primitive Pump Info"); provider.add("config.jade.plugin_gtceu.transformer", "[GTCEu] Transformer Info"); provider.add("config.jade.plugin_gtceu.stained_color", "[GTCEu] Stained Block Info"); + provider.add("config.jade.plugin_gtceu.pipe", "[GTCEu] Pipe Info"); provider.add("config.jade.plugin_gtceu.me_pattern_buffer", "[GTCEu] Pattern Buffer Info"); provider.add("config.jade.plugin_gtceu.me_pattern_buffer_proxy", "[GTCEu] Pattern Buffer Proxy Info"); diff --git a/src/main/java/com/gregtechceu/gtceu/data/recipe/GTCraftingComponents.java b/src/main/java/com/gregtechceu/gtceu/data/recipe/GTCraftingComponents.java index fed70a8733..eec55b321b 100644 --- a/src/main/java/com/gregtechceu/gtceu/data/recipe/GTCraftingComponents.java +++ b/src/main/java/com/gregtechceu/gtceu/data/recipe/GTCraftingComponents.java @@ -318,37 +318,37 @@ public static void initializeComponents() { .add(MAX, GTBlocks.MACHINE_CASING_MAX.asStack()); } - PIPE_NORMAL = new CraftingComponent(new UnificationEntry(TagPrefix.pipeNormalFluid, GTMaterials.Bronze)) - .add(ULV, new UnificationEntry(TagPrefix.pipeNormalFluid, GTMaterials.Bronze)) - .add(LV, new UnificationEntry(TagPrefix.pipeNormalFluid, GTMaterials.Bronze)) - .add(MV, new UnificationEntry(TagPrefix.pipeNormalFluid, GTMaterials.Steel)) - .add(HV, new UnificationEntry(TagPrefix.pipeNormalFluid, GTMaterials.StainlessSteel)) - .add(EV, new UnificationEntry(TagPrefix.pipeNormalFluid, GTMaterials.Titanium)) - .add(IV, new UnificationEntry(TagPrefix.pipeNormalFluid, GTMaterials.TungstenSteel)) - .add(LuV, new UnificationEntry(TagPrefix.pipeNormalFluid, GTMaterials.NiobiumTitanium)) - .add(ZPM, new UnificationEntry(TagPrefix.pipeNormalFluid, GTMaterials.Iridium)) - .add(UV, new UnificationEntry(TagPrefix.pipeNormalFluid, GTMaterials.Naquadah)) - .add(UHV, new UnificationEntry(TagPrefix.pipeNormalFluid, GTMaterials.Naquadah)); - - PIPE_LARGE = new CraftingComponent(new UnificationEntry(TagPrefix.pipeLargeFluid, GTMaterials.Bronze)) - .add(ULV, new UnificationEntry(TagPrefix.pipeLargeFluid, GTMaterials.Bronze)) - .add(LV, new UnificationEntry(TagPrefix.pipeLargeFluid, GTMaterials.Bronze)) - .add(MV, new UnificationEntry(TagPrefix.pipeLargeFluid, GTMaterials.Steel)) - .add(HV, new UnificationEntry(TagPrefix.pipeLargeFluid, GTMaterials.StainlessSteel)) - .add(EV, new UnificationEntry(TagPrefix.pipeLargeFluid, GTMaterials.Titanium)) - .add(IV, new UnificationEntry(TagPrefix.pipeLargeFluid, GTMaterials.TungstenSteel)) - .add(LuV, new UnificationEntry(TagPrefix.pipeLargeFluid, GTMaterials.NiobiumTitanium)) - .add(ZPM, new UnificationEntry(TagPrefix.pipeLargeFluid, GTMaterials.Ultimet)) - .add(UV, new UnificationEntry(TagPrefix.pipeLargeFluid, GTMaterials.Naquadah)) - .add(UHV, new UnificationEntry(TagPrefix.pipeLargeFluid, GTMaterials.Neutronium)); - - PIPE_NONUPLE = new CraftingComponent(new UnificationEntry(TagPrefix.pipeNonupleFluid, GTMaterials.Titanium)) - .add(EV, new UnificationEntry(TagPrefix.pipeNonupleFluid, GTMaterials.Titanium)) - .add(IV, new UnificationEntry(TagPrefix.pipeNonupleFluid, GTMaterials.TungstenSteel)) - .add(LuV, new UnificationEntry(TagPrefix.pipeNonupleFluid, GTMaterials.NiobiumTitanium)) - .add(ZPM, new UnificationEntry(TagPrefix.pipeNonupleFluid, GTMaterials.Iridium)) - .add(UV, new UnificationEntry(TagPrefix.pipeNonupleFluid, GTMaterials.Naquadah)) - .add(UHV, new UnificationEntry(TagPrefix.pipeNonupleFluid, GTMaterials.Neutronium)); + PIPE_NORMAL = new CraftingComponent(new UnificationEntry(TagPrefix.pipeNormal, GTMaterials.Bronze)) + .add(ULV, new UnificationEntry(TagPrefix.pipeNormal, GTMaterials.Bronze)) + .add(LV, new UnificationEntry(TagPrefix.pipeNormal, GTMaterials.Bronze)) + .add(MV, new UnificationEntry(TagPrefix.pipeNormal, GTMaterials.Steel)) + .add(HV, new UnificationEntry(TagPrefix.pipeNormal, GTMaterials.StainlessSteel)) + .add(EV, new UnificationEntry(TagPrefix.pipeNormal, GTMaterials.Titanium)) + .add(IV, new UnificationEntry(TagPrefix.pipeNormal, GTMaterials.TungstenSteel)) + .add(LuV, new UnificationEntry(TagPrefix.pipeNormal, GTMaterials.NiobiumTitanium)) + .add(ZPM, new UnificationEntry(TagPrefix.pipeNormal, GTMaterials.Iridium)) + .add(UV, new UnificationEntry(TagPrefix.pipeNormal, GTMaterials.Naquadah)) + .add(UHV, new UnificationEntry(TagPrefix.pipeNormal, GTMaterials.Naquadah)); + + PIPE_LARGE = new CraftingComponent(new UnificationEntry(TagPrefix.pipeLarge, GTMaterials.Bronze)) + .add(ULV, new UnificationEntry(TagPrefix.pipeLarge, GTMaterials.Bronze)) + .add(LV, new UnificationEntry(TagPrefix.pipeLarge, GTMaterials.Bronze)) + .add(MV, new UnificationEntry(TagPrefix.pipeLarge, GTMaterials.Steel)) + .add(HV, new UnificationEntry(TagPrefix.pipeLarge, GTMaterials.StainlessSteel)) + .add(EV, new UnificationEntry(TagPrefix.pipeLarge, GTMaterials.Titanium)) + .add(IV, new UnificationEntry(TagPrefix.pipeLarge, GTMaterials.TungstenSteel)) + .add(LuV, new UnificationEntry(TagPrefix.pipeLarge, GTMaterials.NiobiumTitanium)) + .add(ZPM, new UnificationEntry(TagPrefix.pipeLarge, GTMaterials.Ultimet)) + .add(UV, new UnificationEntry(TagPrefix.pipeLarge, GTMaterials.Naquadah)) + .add(UHV, new UnificationEntry(TagPrefix.pipeLarge, GTMaterials.Neutronium)); + + PIPE_NONUPLE = new CraftingComponent(new UnificationEntry(TagPrefix.pipeNonuple, GTMaterials.Titanium)) + .add(EV, new UnificationEntry(TagPrefix.pipeNonuple, GTMaterials.Titanium)) + .add(IV, new UnificationEntry(TagPrefix.pipeNonuple, GTMaterials.TungstenSteel)) + .add(LuV, new UnificationEntry(TagPrefix.pipeNonuple, GTMaterials.NiobiumTitanium)) + .add(ZPM, new UnificationEntry(TagPrefix.pipeNonuple, GTMaterials.Iridium)) + .add(UV, new UnificationEntry(TagPrefix.pipeNonuple, GTMaterials.Naquadah)) + .add(UHV, new UnificationEntry(TagPrefix.pipeNonuple, GTMaterials.Neutronium)); /* * Glass: Steam-MV @@ -651,13 +651,13 @@ public static void initializeComponents() { .add(ULV, Tags.Items.GLASS) .add(LV, Tags.Items.GLASS) .add(MV, Tags.Items.GLASS) - .add(HV, new UnificationEntry(TagPrefix.pipeNormalFluid, GTMaterials.Polyethylene)) - .add(EV, new UnificationEntry(TagPrefix.pipeLargeFluid, GTMaterials.Polyethylene)) - .add(IV, new UnificationEntry(TagPrefix.pipeHugeFluid, GTMaterials.Polyethylene)) - .add(LuV, new UnificationEntry(TagPrefix.pipeNormalFluid, GTMaterials.Polytetrafluoroethylene)) - .add(ZPM, new UnificationEntry(TagPrefix.pipeLargeFluid, GTMaterials.Polytetrafluoroethylene)) - .add(UV, new UnificationEntry(TagPrefix.pipeHugeFluid, GTMaterials.Polytetrafluoroethylene)) - .add(UHV, new UnificationEntry(TagPrefix.pipeNormalFluid, GTMaterials.Polybenzimidazole)); + .add(HV, new UnificationEntry(TagPrefix.pipeNormal, GTMaterials.Polyethylene)) + .add(EV, new UnificationEntry(TagPrefix.pipeLarge, GTMaterials.Polyethylene)) + .add(IV, new UnificationEntry(TagPrefix.pipeHuge, GTMaterials.Polyethylene)) + .add(LuV, new UnificationEntry(TagPrefix.pipeNormal, GTMaterials.Polytetrafluoroethylene)) + .add(ZPM, new UnificationEntry(TagPrefix.pipeLarge, GTMaterials.Polytetrafluoroethylene)) + .add(UV, new UnificationEntry(TagPrefix.pipeHuge, GTMaterials.Polytetrafluoroethylene)) + .add(UHV, new UnificationEntry(TagPrefix.pipeNormal, GTMaterials.Polybenzimidazole)); POWER_COMPONENT = new CraftingComponent(GTItems.ULTRA_LOW_POWER_INTEGRATED_CIRCUIT.asStack()) .add(MV, GTItems.ULTRA_LOW_POWER_INTEGRATED_CIRCUIT.asStack()) diff --git a/src/main/java/com/gregtechceu/gtceu/data/recipe/builder/GTRecipeBuilder.java b/src/main/java/com/gregtechceu/gtceu/data/recipe/builder/GTRecipeBuilder.java index 526f89ce03..d2613f8e4e 100644 --- a/src/main/java/com/gregtechceu/gtceu/data/recipe/builder/GTRecipeBuilder.java +++ b/src/main/java/com/gregtechceu/gtceu/data/recipe/builder/GTRecipeBuilder.java @@ -292,7 +292,7 @@ public GTRecipeBuilder outputEU(long eu) { return output(EURecipeCapability.CAP, eu); } - public GTRecipeBuilder inputCWU(int cwu) { + public GTRecipeBuilder inputCWU(long cwu) { return input(CWURecipeCapability.CAP, cwu); } @@ -320,7 +320,7 @@ public GTRecipeBuilder totalCWU(int cwu) { return this; } - public GTRecipeBuilder outputCWU(int cwu) { + public GTRecipeBuilder outputCWU(long cwu) { return output(CWURecipeCapability.CAP, cwu); } diff --git a/src/main/java/com/gregtechceu/gtceu/data/recipe/configurable/RecipeAddition.java b/src/main/java/com/gregtechceu/gtceu/data/recipe/configurable/RecipeAddition.java index 552fad8329..987ae8c8b8 100644 --- a/src/main/java/com/gregtechceu/gtceu/data/recipe/configurable/RecipeAddition.java +++ b/src/main/java/com/gregtechceu/gtceu/data/recipe/configurable/RecipeAddition.java @@ -61,7 +61,7 @@ private static void steelSteamMultiblocks(Consumer provider) { GTMachines.STEAM_MACERATOR.right().asStack(), 'C', GTBlocks.CASING_STEEL_SOLID.asStack()); VanillaRecipeHelper.addShapedRecipe(provider, true, "steam_hatch", GTMachines.STEAM_HATCH.asStack(), "BPB", "BTB", "BPB", 'B', new UnificationEntry(TagPrefix.plate, GTMaterials.Steel), 'P', - new UnificationEntry(TagPrefix.pipeNormalFluid, GTMaterials.Steel), 'T', + new UnificationEntry(TagPrefix.pipeNormal, GTMaterials.Steel), 'T', GTMachines.STEEL_DRUM.asStack()); VanillaRecipeHelper.addShapedRecipe(provider, true, "steam_input_bus", GTMachines.STEAM_IMPORT_BUS.asStack(), "C", "H", 'H', GTBlocks.STEEL_HULL.asStack(), 'C', @@ -81,7 +81,7 @@ private static void steelSteamMultiblocks(Consumer provider) { GTMachines.STEAM_MACERATOR.left().asStack(), 'C', GTBlocks.CASING_BRONZE_BRICKS.asStack()); VanillaRecipeHelper.addShapedRecipe(provider, true, "steam_hatch", GTMachines.STEAM_HATCH.asStack(), "BPB", "BTB", "BPB", 'B', new UnificationEntry(TagPrefix.plate, GTMaterials.Bronze), 'P', - new UnificationEntry(TagPrefix.pipeNormalFluid, GTMaterials.Bronze), 'T', + new UnificationEntry(TagPrefix.pipeNormal, GTMaterials.Bronze), 'T', GTMachines.BRONZE_DRUM.asStack()); VanillaRecipeHelper.addShapedRecipe(provider, true, "steam_input_bus", GTMachines.STEAM_IMPORT_BUS.asStack(), "C", "H", 'H', GTBlocks.BRONZE_HULL.asStack(), 'C', diff --git a/src/main/java/com/gregtechceu/gtceu/data/recipe/generated/PartsRecipeHandler.java b/src/main/java/com/gregtechceu/gtceu/data/recipe/generated/PartsRecipeHandler.java index 6594ea680f..0f9683b949 100644 --- a/src/main/java/com/gregtechceu/gtceu/data/recipe/generated/PartsRecipeHandler.java +++ b/src/main/java/com/gregtechceu/gtceu/data/recipe/generated/PartsRecipeHandler.java @@ -9,6 +9,7 @@ import com.gregtechceu.gtceu.common.data.GTMaterials; import com.gregtechceu.gtceu.common.data.GTRecipeCategories; import com.gregtechceu.gtceu.common.item.TurbineRotorBehaviour; +import com.gregtechceu.gtceu.common.pipelike.handlers.properties.MaterialEnergyProperties; import com.gregtechceu.gtceu.config.ConfigHolder; import com.gregtechceu.gtceu.data.recipe.VanillaRecipeHelper; import com.gregtechceu.gtceu.data.recipe.builder.GTRecipeBuilder; @@ -155,7 +156,8 @@ public static void processFineWire(TagPrefix fineWirePrefix, Material material, VanillaRecipeHelper.addShapelessRecipe(provider, String.format("fine_wire_%s", material.getName()), fineWireStack, 'x', new UnificationEntry(foil, material)); - if (material.hasProperty(PropertyKey.WIRE)) { + if (material.hasProperty(PropertyKey.PIPENET_PROPERTIES) && + material.getProperty(PropertyKey.PIPENET_PROPERTIES).hasProperty(MaterialEnergyProperties.KEY)) { WIREMILL_RECIPES.recipeBuilder("mill_" + material.getName() + "_wire_to_fine_wire") .inputItems(wireGtSingle, material) .outputItems(GTUtil.copyAmount(4, fineWireStack)) diff --git a/src/main/java/com/gregtechceu/gtceu/data/recipe/generated/PipeRecipeHandler.java b/src/main/java/com/gregtechceu/gtceu/data/recipe/generated/PipeRecipeHandler.java index d8a943a49a..1c610837a6 100644 --- a/src/main/java/com/gregtechceu/gtceu/data/recipe/generated/PipeRecipeHandler.java +++ b/src/main/java/com/gregtechceu/gtceu/data/recipe/generated/PipeRecipeHandler.java @@ -2,15 +2,13 @@ import com.gregtechceu.gtceu.api.data.chemical.ChemicalHelper; import com.gregtechceu.gtceu.api.data.chemical.material.Material; -import com.gregtechceu.gtceu.api.data.chemical.material.properties.FluidPipeProperties; import com.gregtechceu.gtceu.api.data.chemical.material.properties.IMaterialProperty; -import com.gregtechceu.gtceu.api.data.chemical.material.properties.ItemPipeProperties; import com.gregtechceu.gtceu.api.data.chemical.material.properties.PropertyKey; import com.gregtechceu.gtceu.api.data.chemical.material.stack.UnificationEntry; import com.gregtechceu.gtceu.api.data.tag.TagPrefix; import com.gregtechceu.gtceu.common.data.GTBlocks; import com.gregtechceu.gtceu.common.data.GTItems; -import com.gregtechceu.gtceu.common.pipelike.duct.DuctPipeType; +import com.gregtechceu.gtceu.common.pipelike.block.duct.DuctStructure; import com.gregtechceu.gtceu.data.recipe.VanillaRecipeHelper; import com.gregtechceu.gtceu.utils.FormattingUtil; import com.gregtechceu.gtceu.utils.GTUtil; @@ -29,38 +27,46 @@ public class PipeRecipeHandler { public static void init(Consumer provider) { - pipeTinyFluid.executeHandler(provider, PropertyKey.FLUID_PIPE, PipeRecipeHandler::processPipeTiny); - pipeSmallFluid.executeHandler(provider, PropertyKey.FLUID_PIPE, PipeRecipeHandler::processPipeSmall); - pipeNormalFluid.executeHandler(provider, PropertyKey.FLUID_PIPE, PipeRecipeHandler::processPipeNormal); - pipeLargeFluid.executeHandler(provider, PropertyKey.FLUID_PIPE, PipeRecipeHandler::processPipeLarge); - pipeHugeFluid.executeHandler(provider, PropertyKey.FLUID_PIPE, PipeRecipeHandler::processPipeHuge); + pipeTiny.executeHandler(provider, PropertyKey.PIPENET_PROPERTIES, PipeRecipeHandler::processPipeTiny); + pipeSmall.executeHandler(provider, PropertyKey.PIPENET_PROPERTIES, PipeRecipeHandler::processPipeSmall); + pipeNormal.executeHandler(provider, PropertyKey.PIPENET_PROPERTIES, PipeRecipeHandler::processPipeNormal); + pipeLarge.executeHandler(provider, PropertyKey.PIPENET_PROPERTIES, PipeRecipeHandler::processPipeLarge); + pipeHuge.executeHandler(provider, PropertyKey.PIPENET_PROPERTIES, PipeRecipeHandler::processPipeHuge); - pipeQuadrupleFluid.executeHandler(provider, PropertyKey.FLUID_PIPE, PipeRecipeHandler::processPipeQuadruple); - pipeNonupleFluid.executeHandler(provider, PropertyKey.FLUID_PIPE, PipeRecipeHandler::processPipeNonuple); + pipeQuadruple.executeHandler(provider, PropertyKey.PIPENET_PROPERTIES, PipeRecipeHandler::processPipeQuadruple); + pipeNonuple.executeHandler(provider, PropertyKey.PIPENET_PROPERTIES, PipeRecipeHandler::processPipeNonuple); - pipeSmallItem.executeHandler(provider, PropertyKey.ITEM_PIPE, PipeRecipeHandler::processPipeSmall); - pipeNormalItem.executeHandler(provider, PropertyKey.ITEM_PIPE, PipeRecipeHandler::processPipeNormal); - pipeLargeItem.executeHandler(provider, PropertyKey.ITEM_PIPE, PipeRecipeHandler::processPipeLarge); - pipeHugeItem.executeHandler(provider, PropertyKey.ITEM_PIPE, PipeRecipeHandler::processPipeHuge); + pipeTinyRestrictive.executeHandler(provider, PropertyKey.PIPENET_PROPERTIES, + PipeRecipeHandler::processRestrictivePipe); + pipeSmallRestrictive.executeHandler(provider, PropertyKey.PIPENET_PROPERTIES, + PipeRecipeHandler::processRestrictivePipe); + pipeNormalRestrictive.executeHandler(provider, PropertyKey.PIPENET_PROPERTIES, + PipeRecipeHandler::processRestrictivePipe); + pipeLargeRestrictive.executeHandler(provider, PropertyKey.PIPENET_PROPERTIES, + PipeRecipeHandler::processRestrictivePipe); + pipeHugeRestrictive.executeHandler(provider, PropertyKey.PIPENET_PROPERTIES, + PipeRecipeHandler::processRestrictivePipe); - pipeSmallRestrictive.executeHandler(provider, PropertyKey.ITEM_PIPE, PipeRecipeHandler::processRestrictivePipe); - pipeNormalRestrictive.executeHandler(provider, PropertyKey.ITEM_PIPE, + pipeQuadrupleRestrictive.executeHandler(provider, PropertyKey.PIPENET_PROPERTIES, + PipeRecipeHandler::processRestrictivePipe); + pipeNonupleRestrictive.executeHandler(provider, PropertyKey.PIPENET_PROPERTIES, PipeRecipeHandler::processRestrictivePipe); - pipeLargeRestrictive.executeHandler(provider, PropertyKey.ITEM_PIPE, PipeRecipeHandler::processRestrictivePipe); - pipeHugeRestrictive.executeHandler(provider, PropertyKey.ITEM_PIPE, PipeRecipeHandler::processRestrictivePipe); addDuctRecipes(provider, Steel, 2); addDuctRecipes(provider, StainlessSteel, 4); addDuctRecipes(provider, TungstenSteel, 8); } - private static void processRestrictivePipe(TagPrefix pipePrefix, Material material, ItemPipeProperties property, + private static void processRestrictivePipe(TagPrefix pipePrefix, Material material, IMaterialProperty property, Consumer provider) { TagPrefix unrestrictive; - if (pipePrefix == pipeSmallRestrictive) unrestrictive = pipeSmallItem; - else if (pipePrefix == pipeNormalRestrictive) unrestrictive = pipeNormalItem; - else if (pipePrefix == pipeLargeRestrictive) unrestrictive = pipeLargeItem; - else if (pipePrefix == pipeHugeRestrictive) unrestrictive = pipeHugeItem; + if (pipePrefix == pipeTinyRestrictive) unrestrictive = pipeTiny; + else if (pipePrefix == pipeSmallRestrictive) unrestrictive = pipeSmall; + else if (pipePrefix == pipeNormalRestrictive) unrestrictive = pipeNormal; + else if (pipePrefix == pipeLargeRestrictive) unrestrictive = pipeLarge; + else if (pipePrefix == pipeHugeRestrictive) unrestrictive = pipeHuge; + else if (pipePrefix == pipeQuadrupleRestrictive) unrestrictive = pipeQuadruple; + else if (pipePrefix == pipeNonupleRestrictive) unrestrictive = pipeNonuple; else return; ASSEMBLER_RECIPES.recipeBuilder("assemble_" + material.getName() + "_" + pipePrefix.name) @@ -212,10 +218,10 @@ private static void processPipeHuge(TagPrefix pipePrefix, Material material, IMa } } - private static void processPipeQuadruple(TagPrefix pipePrefix, Material material, FluidPipeProperties property, + private static void processPipeQuadruple(TagPrefix pipePrefix, Material material, IMaterialProperty property, Consumer provider) { if (material.hasProperty(PropertyKey.WOOD)) return; - ItemStack smallPipe = ChemicalHelper.get(pipeSmallFluid, material); + ItemStack smallPipe = ChemicalHelper.get(pipeSmall, material); ItemStack quadPipe = ChemicalHelper.get(pipePrefix, material); VanillaRecipeHelper.addShapedRecipe(provider, String.format("quadruple_%s_pipe", material.getName()), quadPipe, "XX", "XX", @@ -230,10 +236,10 @@ private static void processPipeQuadruple(TagPrefix pipePrefix, Material material .save(provider); } - private static void processPipeNonuple(TagPrefix pipePrefix, Material material, FluidPipeProperties property, + private static void processPipeNonuple(TagPrefix pipePrefix, Material material, IMaterialProperty property, Consumer provider) { if (material.hasProperty(PropertyKey.WOOD)) return; - ItemStack smallPipe = ChemicalHelper.get(pipeSmallFluid, material); + ItemStack smallPipe = ChemicalHelper.get(pipeSmall, material); ItemStack nonuplePipe = ChemicalHelper.get(pipePrefix, material); VanillaRecipeHelper.addShapedRecipe(provider, String.format("nonuple_%s_pipe", material.getName()), nonuplePipe, "XXX", "XXX", "XXX", @@ -250,16 +256,16 @@ private static void processPipeNonuple(TagPrefix pipePrefix, Material material, private static void addDuctRecipes(Consumer provider, Material material, int outputAmount) { VanillaRecipeHelper.addShapedRecipe(provider, "small_duct_%s".formatted(material.getName()), - GTBlocks.DUCT_PIPES[DuctPipeType.SMALL.ordinal()].asStack(outputAmount * 2), "w", "X", "h", + GTBlocks.DUCT_PIPE_BLOCKS.get(DuctStructure.SMALL).asStack(outputAmount * 2), "w", "X", "h", 'X', new UnificationEntry(plate, material)); VanillaRecipeHelper.addShapedRecipe(provider, "medium_duct_%s".formatted(material.getName()), - GTBlocks.DUCT_PIPES[DuctPipeType.NORMAL.ordinal()].asStack(outputAmount), " X ", "wXh", " X ", + GTBlocks.DUCT_PIPE_BLOCKS.get(DuctStructure.NORMAL).asStack(outputAmount), " X ", "wXh", " X ", 'X', new UnificationEntry(plate, material)); VanillaRecipeHelper.addShapedRecipe(provider, "large_duct_%s".formatted(material.getName()), - GTBlocks.DUCT_PIPES[DuctPipeType.LARGE.ordinal()].asStack(outputAmount), "XwX", "X X", "XhX", + GTBlocks.DUCT_PIPE_BLOCKS.get(DuctStructure.LARGE).asStack(outputAmount), "XwX", "X X", "XhX", 'X', new UnificationEntry(plate, material)); VanillaRecipeHelper.addShapedRecipe(provider, "huge_duct_%s".formatted(material.getName()), - GTBlocks.DUCT_PIPES[DuctPipeType.HUGE.ordinal()].asStack(outputAmount), "XwX", "X X", "XhX", + GTBlocks.DUCT_PIPE_BLOCKS.get(DuctStructure.HUGE).asStack(outputAmount), "XwX", "X X", "XhX", 'X', new UnificationEntry(plateDouble, material)); } diff --git a/src/main/java/com/gregtechceu/gtceu/data/recipe/generated/WireCombiningHandler.java b/src/main/java/com/gregtechceu/gtceu/data/recipe/generated/WireCombiningHandler.java index 7e1ae80325..2ef36bcac1 100644 --- a/src/main/java/com/gregtechceu/gtceu/data/recipe/generated/WireCombiningHandler.java +++ b/src/main/java/com/gregtechceu/gtceu/data/recipe/generated/WireCombiningHandler.java @@ -3,11 +3,10 @@ import com.gregtechceu.gtceu.api.GTValues; import com.gregtechceu.gtceu.api.data.chemical.ChemicalHelper; import com.gregtechceu.gtceu.api.data.chemical.material.Material; -import com.gregtechceu.gtceu.api.data.chemical.material.properties.PropertyKey; -import com.gregtechceu.gtceu.api.data.chemical.material.properties.WireProperties; import com.gregtechceu.gtceu.api.data.chemical.material.stack.UnificationEntry; import com.gregtechceu.gtceu.api.data.tag.TagPrefix; import com.gregtechceu.gtceu.common.data.GTMaterials; +import com.gregtechceu.gtceu.common.pipelike.handlers.properties.MaterialEnergyProperties; import com.gregtechceu.gtceu.data.recipe.VanillaRecipeHelper; import net.minecraft.data.recipes.FinishedRecipe; @@ -36,20 +35,24 @@ public class WireCombiningHandler { public static void init(Consumer provider) { // Generate Wire Packer/Unpacker recipes - wireGtSingle.executeHandler(provider, PropertyKey.WIRE, WireCombiningHandler::processWireCompression); + wireGtSingle.executeHandler(provider, + MaterialEnergyProperties.registrationHandler(WireCombiningHandler::processWireCompression)); // Generate manual recipes for combining Wires/Cables for (TagPrefix wirePrefix : WIRE_DOUBLING_ORDER) { - wirePrefix.executeHandler(provider, PropertyKey.WIRE, WireCombiningHandler::generateWireCombiningRecipe); + wirePrefix.executeHandler(provider, + MaterialEnergyProperties.registrationHandler(WireCombiningHandler::generateWireCombiningRecipe)); } // Generate Cable -> Wire recipes in the unpacker for (TagPrefix cablePrefix : cableToWireMap.keySet()) { - cablePrefix.executeHandler(provider, PropertyKey.WIRE, WireCombiningHandler::processCableStripping); + cablePrefix.executeHandler(provider, + MaterialEnergyProperties.registrationHandler(WireCombiningHandler::processCableStripping)); } } - private static void generateWireCombiningRecipe(TagPrefix wirePrefix, Material material, WireProperties property, + private static void generateWireCombiningRecipe(TagPrefix wirePrefix, Material material, + MaterialEnergyProperties property, Consumer provider) { int wireIndex = ArrayUtils.indexOf(WIRE_DOUBLING_ORDER, wirePrefix); @@ -79,7 +82,7 @@ private static void generateWireCombiningRecipe(TagPrefix wirePrefix, Material m } } - private static void processWireCompression(TagPrefix prefix, Material material, WireProperties property, + private static void processWireCompression(TagPrefix prefix, Material material, MaterialEnergyProperties property, Consumer provider) { for (int startTier = 0; startTier < 4; startTier++) { for (int i = 1; i < 5 - startTier; i++) { @@ -100,7 +103,7 @@ private static void processWireCompression(TagPrefix prefix, Material material, } } - private static void processCableStripping(TagPrefix prefix, Material material, WireProperties property, + private static void processCableStripping(TagPrefix prefix, Material material, MaterialEnergyProperties property, Consumer provider) { PACKER_RECIPES.recipeBuilder("strip_" + material.getName() + "_" + prefix.name) .inputItems(prefix, material) diff --git a/src/main/java/com/gregtechceu/gtceu/data/recipe/generated/WireRecipeHandler.java b/src/main/java/com/gregtechceu/gtceu/data/recipe/generated/WireRecipeHandler.java index ba6b4084bb..fbd1c0668f 100644 --- a/src/main/java/com/gregtechceu/gtceu/data/recipe/generated/WireRecipeHandler.java +++ b/src/main/java/com/gregtechceu/gtceu/data/recipe/generated/WireRecipeHandler.java @@ -4,10 +4,10 @@ import com.gregtechceu.gtceu.api.data.chemical.ChemicalHelper; import com.gregtechceu.gtceu.api.data.chemical.material.Material; import com.gregtechceu.gtceu.api.data.chemical.material.properties.PropertyKey; -import com.gregtechceu.gtceu.api.data.chemical.material.properties.WireProperties; import com.gregtechceu.gtceu.api.data.chemical.material.stack.UnificationEntry; import com.gregtechceu.gtceu.api.data.tag.TagPrefix; import com.gregtechceu.gtceu.common.data.GTItems; +import com.gregtechceu.gtceu.common.pipelike.handlers.properties.MaterialEnergyProperties; import com.gregtechceu.gtceu.data.recipe.VanillaRecipeHelper; import com.gregtechceu.gtceu.data.recipe.builder.GTRecipeBuilder; import com.gregtechceu.gtceu.utils.GTUtil; @@ -61,17 +61,23 @@ public static void init(Consumer provider) { // Wiremill: 1x Wire -> Fine // Extruder: Ingot -> 1x Wire // Wire Cutter: Plate -> 1x Wire - wireGtSingle.executeHandler(provider, PropertyKey.WIRE, WireRecipeHandler::processWires); + wireGtSingle.executeHandler(provider, + MaterialEnergyProperties.registrationHandler(WireRecipeHandler::processWires)); // Generate Cable Covering Recipes - wireGtSingle.executeHandler(provider, PropertyKey.WIRE, WireRecipeHandler::generateCableCovering); - wireGtDouble.executeHandler(provider, PropertyKey.WIRE, WireRecipeHandler::generateCableCovering); - wireGtQuadruple.executeHandler(provider, PropertyKey.WIRE, WireRecipeHandler::generateCableCovering); - wireGtOctal.executeHandler(provider, PropertyKey.WIRE, WireRecipeHandler::generateCableCovering); - wireGtHex.executeHandler(provider, PropertyKey.WIRE, WireRecipeHandler::generateCableCovering); + wireGtSingle.executeHandler(provider, + MaterialEnergyProperties.registrationHandler(WireRecipeHandler::generateCableCovering)); + wireGtDouble.executeHandler(provider, + MaterialEnergyProperties.registrationHandler(WireRecipeHandler::generateCableCovering)); + wireGtQuadruple.executeHandler(provider, + MaterialEnergyProperties.registrationHandler(WireRecipeHandler::generateCableCovering)); + wireGtOctal.executeHandler(provider, + MaterialEnergyProperties.registrationHandler(WireRecipeHandler::generateCableCovering)); + wireGtHex.executeHandler(provider, + MaterialEnergyProperties.registrationHandler(WireRecipeHandler::generateCableCovering)); } - public static void processWires(TagPrefix wirePrefix, Material material, WireProperties property, + public static void processWires(TagPrefix wirePrefix, Material material, MaterialEnergyProperties property, Consumer provider) { TagPrefix prefix = material.hasProperty(PropertyKey.INGOT) ? ingot : material.hasProperty(PropertyKey.GEM) ? gem : dust; @@ -120,14 +126,14 @@ public static void processWires(TagPrefix wirePrefix, Material material, WirePro } } - public static void generateCableCovering(TagPrefix wirePrefix, Material material, WireProperties property, + public static void generateCableCovering(TagPrefix wirePrefix, Material material, MaterialEnergyProperties property, Consumer provider) { // Superconductors have no Cables, so exit early if (property.isSuperconductor()) return; int cableAmount = (int) (wirePrefix.getMaterialAmount(material) * 2 / M); TagPrefix cablePrefix = TagPrefix.get("cable" + wirePrefix.name().substring(4)); - int voltageTier = GTUtil.getTierByVoltage(property.getVoltage()); + int voltageTier = GTUtil.getTierByVoltage(property.getVoltageLimit()); int insulationAmount = INSULATION_AMOUNT.get(cablePrefix); // Generate hand-crafting recipes for ULV and LV cables diff --git a/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/AssemblerRecipeLoader.java b/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/AssemblerRecipeLoader.java index 4c65459c18..8e05ab1b30 100644 --- a/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/AssemblerRecipeLoader.java +++ b/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/AssemblerRecipeLoader.java @@ -61,14 +61,14 @@ public static void init(Consumer provider) { // Other ASSEMBLER_RECIPES.recipeBuilder("stable_titanium_casing") .inputItems(rotor, Titanium, 2) - .inputItems(pipeNormalFluid, Titanium, 4) + .inputItems(pipeNormal, Titanium, 4) .inputItems(CASING_TITANIUM_STABLE.asStack()) .outputItems(CASING_ENGINE_INTAKE.asStack(ConfigHolder.INSTANCE.recipes.casingsPerCraft)) .duration(50).EUt(16).save(provider); ASSEMBLER_RECIPES.recipeBuilder("stable_tungstensteel_casing") .inputItems(rotor, TungstenSteel, 2) - .inputItems(pipeNormalFluid, TungstenSteel, 4) + .inputItems(pipeNormal, TungstenSteel, 4) .inputItems(CASING_TUNGSTENSTEEL_ROBUST.asStack()) .outputItems(CASING_EXTREME_ENGINE_INTAKE.asStack(ConfigHolder.INSTANCE.recipes.casingsPerCraft)) .duration(50).EUt(16).save(provider); @@ -127,21 +127,21 @@ public static void init(Consumer provider) { .inputItems(plate, Wood) .circuitMeta(12) .inputFluids(Glue.getFluid(50)) - .outputItems(pipeSmallFluid, Wood) + .outputItems(pipeSmall, Wood) .save(provider); ASSEMBLER_RECIPES.recipeBuilder("normal_wood_pipe").duration(200).EUt(VA[LV]) .inputItems(plate, Wood, 3) .circuitMeta(6) .inputFluids(Glue.getFluid(20)) - .outputItems(pipeNormalFluid, Wood) + .outputItems(pipeNormal, Wood) .save(provider); ASSEMBLER_RECIPES.recipeBuilder("large_wood_pipe").duration(100).EUt(VA[LV]) .inputItems(plate, Wood, 6) .circuitMeta(2) .inputFluids(Glue.getFluid(10)) - .outputItems(pipeLargeFluid, Wood) + .outputItems(pipeLarge, Wood) .save(provider); // Treated Wood Pipes @@ -149,21 +149,21 @@ public static void init(Consumer provider) { .inputItems(plate, TreatedWood) .circuitMeta(12) .inputFluids(Glue.getFluid(50)) - .outputItems(pipeSmallFluid, TreatedWood) + .outputItems(pipeSmall, TreatedWood) .save(provider); ASSEMBLER_RECIPES.recipeBuilder("normal_treated_wood_pipe").duration(200).EUt(VA[LV]) .inputItems(plate, TreatedWood, 3) .circuitMeta(6) .inputFluids(Glue.getFluid(20)) - .outputItems(pipeNormalFluid, TreatedWood) + .outputItems(pipeNormal, TreatedWood) .save(provider); ASSEMBLER_RECIPES.recipeBuilder("large_treated_wood_pipe").duration(100).EUt(VA[LV]) .inputItems(plate, TreatedWood, 6) .circuitMeta(2) .inputFluids(Glue.getFluid(10)) - .outputItems(pipeLargeFluid, TreatedWood) + .outputItems(pipeLarge, TreatedWood) .save(provider); // Voltage Coils diff --git a/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/CircuitRecipes.java b/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/CircuitRecipes.java index afed402d3b..e400c755a3 100644 --- a/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/CircuitRecipes.java +++ b/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/CircuitRecipes.java @@ -1477,7 +1477,7 @@ private static void circuitRecipes(Consumer provider) { CIRCUIT_ASSEMBLER_RECIPES.recipeBuilder("neuro_processor").EUt(80000).duration(600) .inputItems(WETWARE_CIRCUIT_BOARD) .inputItems(STEM_CELLS, 16) - .inputItems(pipeSmallFluid, Polybenzimidazole, 8) + .inputItems(pipeSmall, Polybenzimidazole, 8) .inputItems(plate, Electrum, 8) .inputItems(foil, SiliconeRubber, 16) .inputItems(bolt, HSSE, 8) diff --git a/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/ComponentRecipes.java b/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/ComponentRecipes.java index 20e0117827..1532791994 100644 --- a/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/ComponentRecipes.java +++ b/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/ComponentRecipes.java @@ -228,34 +228,34 @@ public static void init(Consumer provider) { VanillaRecipeHelper.addShapedRecipe(provider, material.equals(Rubber), String.format("electric_pump_lv_%s", name), ELECTRIC_PUMP_LV.asStack(), "SXR", "dPw", "RMC", 'S', new UnificationEntry(screw, Tin), 'X', new UnificationEntry(rotor, Tin), 'P', - new UnificationEntry(pipeNormalFluid, Bronze), 'R', new UnificationEntry(ring, material), 'C', + new UnificationEntry(pipeNormal, Bronze), 'R', new UnificationEntry(ring, material), 'C', new UnificationEntry(cableGtSingle, Tin), 'M', ELECTRIC_MOTOR_LV.asStack()); VanillaRecipeHelper.addShapedRecipe(provider, material.equals(Rubber), String.format("electric_pump_mv_%s", name), ELECTRIC_PUMP_MV.asStack(), "SXR", "dPw", "RMC", 'S', new UnificationEntry(screw, Bronze), 'X', new UnificationEntry(rotor, Bronze), 'P', - new UnificationEntry(pipeNormalFluid, Steel), 'R', new UnificationEntry(ring, material), 'C', + new UnificationEntry(pipeNormal, Steel), 'R', new UnificationEntry(ring, material), 'C', new UnificationEntry(cableGtSingle, Copper), 'M', ELECTRIC_MOTOR_MV.asStack()); VanillaRecipeHelper.addShapedRecipe(provider, material.equals(Rubber), String.format("electric_pump_hv_%s", name), ELECTRIC_PUMP_HV.asStack(), "SXR", "dPw", "RMC", 'S', new UnificationEntry(screw, Steel), 'X', new UnificationEntry(rotor, Steel), 'P', - new UnificationEntry(pipeNormalFluid, StainlessSteel), 'R', new UnificationEntry(ring, material), + new UnificationEntry(pipeNormal, StainlessSteel), 'R', new UnificationEntry(ring, material), 'C', new UnificationEntry(cableGtSingle, Gold), 'M', ELECTRIC_MOTOR_HV.asStack()); VanillaRecipeHelper.addShapedRecipe(provider, material.equals(Rubber), String.format("electric_pump_ev_%s", name), ELECTRIC_PUMP_EV.asStack(), "SXR", "dPw", "RMC", 'S', new UnificationEntry(screw, StainlessSteel), 'X', new UnificationEntry(rotor, StainlessSteel), 'P', - new UnificationEntry(pipeNormalFluid, Titanium), 'R', new UnificationEntry(ring, material), 'C', + new UnificationEntry(pipeNormal, Titanium), 'R', new UnificationEntry(ring, material), 'C', new UnificationEntry(cableGtSingle, Aluminium), 'M', ELECTRIC_MOTOR_EV.asStack()); if (!material.equals(Rubber)) VanillaRecipeHelper.addShapedRecipe(provider, material.equals(SiliconeRubber), String.format("electric_pump_iv_%s", name), ELECTRIC_PUMP_IV.asStack(), "SXR", "dPw", "RMC", 'S', new UnificationEntry(screw, TungstenSteel), 'X', new UnificationEntry(rotor, TungstenSteel), 'P', - new UnificationEntry(pipeNormalFluid, TungstenSteel), 'R', new UnificationEntry(ring, material), + new UnificationEntry(pipeNormal, TungstenSteel), 'R', new UnificationEntry(ring, material), 'C', new UnificationEntry(cableGtSingle, Tungsten), 'M', ELECTRIC_MOTOR_IV.asStack()); ASSEMBLER_RECIPES.recipeBuilder("electric_pump_lv_" + name) .inputItems(cableGtSingle, Tin) - .inputItems(pipeNormalFluid, Bronze) + .inputItems(pipeNormal, Bronze) .inputItems(screw, Tin) .inputItems(rotor, Tin) .inputItems(ring, materialEntry.getValue(), 2) @@ -265,7 +265,7 @@ public static void init(Consumer provider) { ASSEMBLER_RECIPES.recipeBuilder("electric_pump_mv_" + name) .inputItems(cableGtSingle, Copper) - .inputItems(pipeNormalFluid, Steel) + .inputItems(pipeNormal, Steel) .inputItems(screw, Bronze) .inputItems(rotor, Bronze) .inputItems(ring, materialEntry.getValue(), 2) @@ -275,7 +275,7 @@ public static void init(Consumer provider) { ASSEMBLER_RECIPES.recipeBuilder("electric_pump_hv_" + name) .inputItems(cableGtSingle, Gold) - .inputItems(pipeNormalFluid, StainlessSteel) + .inputItems(pipeNormal, StainlessSteel) .inputItems(screw, Steel) .inputItems(rotor, Steel) .inputItems(ring, materialEntry.getValue(), 2) @@ -285,7 +285,7 @@ public static void init(Consumer provider) { ASSEMBLER_RECIPES.recipeBuilder("electric_pump_ev_" + name) .inputItems(cableGtSingle, Aluminium) - .inputItems(pipeNormalFluid, Titanium) + .inputItems(pipeNormal, Titanium) .inputItems(screw, StainlessSteel) .inputItems(rotor, StainlessSteel) .inputItems(ring, materialEntry.getValue(), 2) @@ -296,7 +296,7 @@ public static void init(Consumer provider) { if (!materialEntry.getValue().equals(Rubber)) ASSEMBLER_RECIPES.recipeBuilder("electric_pump_iv_" + name) .inputItems(cableGtSingle, Tungsten) - .inputItems(pipeNormalFluid, TungstenSteel) + .inputItems(pipeNormal, TungstenSteel) .inputItems(screw, TungstenSteel) .inputItems(rotor, TungstenSteel) .inputItems(ring, materialEntry.getValue(), 2) @@ -359,7 +359,7 @@ public static void init(Consumer provider) { ASSEMBLY_LINE_RECIPES.recipeBuilder("electric_pump_luv") .inputItems(ELECTRIC_MOTOR_LuV) - .inputItems(pipeSmallFluid, NiobiumTitanium) + .inputItems(pipeSmall, NiobiumTitanium) .inputItems(plate, HSSS, 2) .inputItems(screw, HSSS, 8) .inputItems(ring, SiliconeRubber, 4) @@ -376,7 +376,7 @@ public static void init(Consumer provider) { ASSEMBLY_LINE_RECIPES.recipeBuilder("electric_pump_zpm") .inputItems(ELECTRIC_MOTOR_ZPM) - .inputItems(pipeNormalFluid, Polybenzimidazole) + .inputItems(pipeNormal, Polybenzimidazole) .inputItems(plate, Osmiridium, 2) .inputItems(screw, Osmiridium, 8) .inputItems(ring, SiliconeRubber, 8) @@ -393,7 +393,7 @@ public static void init(Consumer provider) { ASSEMBLY_LINE_RECIPES.recipeBuilder("electric_pump_uv") .inputItems(ELECTRIC_MOTOR_UV) - .inputItems(pipeLargeFluid, Naquadah) + .inputItems(pipeLarge, Naquadah) .inputItems(plate, Tritanium, 2) .inputItems(screw, Tritanium, 8) .inputItems(ring, SiliconeRubber, 16) @@ -488,12 +488,12 @@ public static void init(Consumer provider) { VanillaRecipeHelper.addShapedRecipe(provider, true, "cover_item_voiding", COVER_ITEM_VOIDING.asStack(), "SDS", "dPw", " E ", 'S', new UnificationEntry(screw, Steel), 'D', COVER_ITEM_DETECTOR.asStack(), 'P', - new UnificationEntry(pipeNormalItem, Brass), 'E', Items.ENDER_PEARL); + new UnificationEntry(pipeNormal, Brass), 'E', Items.ENDER_PEARL); ASSEMBLER_RECIPES.recipeBuilder("cover_item_voiding") .inputItems(screw, Steel, 2) .inputItems(COVER_ITEM_DETECTOR) - .inputItems(pipeNormalItem, Brass) + .inputItems(pipeNormal, Brass) .inputItems(Items.ENDER_PEARL) .outputItems(COVER_ITEM_VOIDING) .duration(100).EUt(VA[LV]).save(provider); @@ -506,12 +506,12 @@ public static void init(Consumer provider) { VanillaRecipeHelper.addShapedRecipe(provider, true, "cover_fluid_voiding", COVER_FLUID_VOIDING.asStack(), "SDS", "dPw", " E ", 'S', new UnificationEntry(screw, Steel), 'D', COVER_FLUID_DETECTOR.asStack(), 'P', - new UnificationEntry(pipeNormalFluid, Bronze), 'E', Items.ENDER_PEARL); + new UnificationEntry(pipeNormal, Bronze), 'E', Items.ENDER_PEARL); ASSEMBLER_RECIPES.recipeBuilder("cover_fluid_voiding") .inputItems(screw, Steel, 2) .inputItems(COVER_FLUID_DETECTOR) - .inputItems(pipeNormalFluid, Bronze) + .inputItems(pipeNormal, Bronze) .inputItems(Items.ENDER_PEARL) .outputItems(COVER_FLUID_VOIDING) .duration(100).EUt(VA[LV]).save(provider); diff --git a/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/ComputerRecipes.java b/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/ComputerRecipes.java index 347d0806aa..9ee4347ea5 100644 --- a/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/ComputerRecipes.java +++ b/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/ComputerRecipes.java @@ -76,7 +76,7 @@ public static void init(Consumer provider) { .inputItems(frameGt, StainlessSteel) .inputItems(ELECTRIC_MOTOR_IV, 2) .inputItems(rotor, StainlessSteel, 2) - .inputItems(pipeTinyFluid, StainlessSteel, 16) + .inputItems(pipeTiny, StainlessSteel, 16) .inputItems(plate, Copper, 16) .inputItems(wireGtSingle, SamariumIronArsenicOxide) .outputItems(COMPUTER_HEAT_VENT, @@ -88,7 +88,7 @@ public static void init(Consumer provider) { .inputItems(foil, Silver, 8) .inputFluids(Polytetrafluoroethylene.getFluid(L)) .cleanroom(CleanroomType.CLEANROOM) - .outputItems(OPTICAL_PIPES[0]) + .outputItems(OPTICAL_PIPE) .duration(100).EUt(VA[IV]).save(provider); ASSEMBLY_LINE_RECIPES.recipeBuilder("data_bank") @@ -97,7 +97,7 @@ public static void init(Consumer provider) { .inputItems(TOOL_DATA_ORB) .inputItems(wireFine, Cobalt, 64) .inputItems(wireFine, Copper, 64) - .inputItems(OPTICAL_PIPES[0].asStack(4)) + .inputItems(OPTICAL_PIPE.asStack(4)) .inputItems(wireGtDouble, IndiumTinBariumTitaniumCuprate, 16) .inputFluids(SolderingAlloy.getFluid(L * 2)) .inputFluids(Lubricant.getFluid(500)) @@ -116,7 +116,7 @@ public static void init(Consumer provider) { .inputItems(ELECTRIC_MOTOR_ZPM, 2) .inputItems(wireGtDouble, UraniumRhodiumDinaquadide, 32) .inputItems(foil, Trinium, 32) - .inputItems(OPTICAL_PIPES[0].asStack(16)) + .inputItems(OPTICAL_PIPE.asStack(16)) .inputFluids(SolderingAlloy.getFluid(L * 8)) .inputFluids(VanadiumGallium.getFluid(L * 8)) .outputItems(RESEARCH_STATION) @@ -133,7 +133,7 @@ public static void init(Consumer provider) { .inputItems(ROBOT_ARM_ZPM, 2) .inputItems(ELECTRIC_MOTOR_ZPM, 2) .inputItems(wireGtDouble, UraniumRhodiumDinaquadide, 16) - .inputItems(OPTICAL_PIPES[0].asStack(2)) + .inputItems(OPTICAL_PIPE.asStack(2)) .inputFluids(SolderingAlloy.getFluid(L * 4)) .inputFluids(Polybenzimidazole.getFluid(L * 2)) .outputItems(OBJECT_HOLDER) @@ -151,12 +151,12 @@ public static void init(Consumer provider) { .inputItems(wireGtDouble, EnrichedNaquadahTriniumEuropiumDuranide, 32) .inputItems(foil, Tritanium, 64) .inputItems(foil, Tritanium, 64) - .inputItems(OPTICAL_PIPES[0].asStack(8)) + .inputItems(OPTICAL_PIPE.asStack(8)) .inputFluids(SolderingAlloy.getFluid(L * 4)) .inputFluids(Polybenzimidazole.getFluid(L * 4)) .outputItems(NETWORK_SWITCH) .stationResearch(b -> b - .researchStack(new ItemStack(OPTICAL_PIPES[0])) + .researchStack(new ItemStack(OPTICAL_PIPE)) .CWUt(32) .EUt(VA[ZPM])) .duration(1200).EUt(100000).save(provider); @@ -168,7 +168,7 @@ public static void init(Consumer provider) { .inputItems(TOOL_DATA_ORB) .inputItems(COVER_SCREEN) .inputItems(wireGtDouble, UraniumRhodiumDinaquadide, 64) - .inputItems(OPTICAL_PIPES[0].asStack(16)) + .inputItems(OPTICAL_PIPE.asStack(16)) .inputFluids(SolderingAlloy.getFluid(L * 8)) .inputFluids(VanadiumGallium.getFluid(L * 8)) .inputFluids(PCBCoolant.getFluid(4000)) @@ -200,7 +200,7 @@ public static void init(Consumer provider) { ASSEMBLER_RECIPES.recipeBuilder("hpca_active_cooler_component") .inputItems(ADVANCED_COMPUTER_CASING.asStack()) .inputItems(plate, Aluminium, 16) - .inputItems(pipeTinyFluid, StainlessSteel, 16) + .inputItems(pipeTiny, StainlessSteel, 16) .inputItems(screw, StainlessSteel, 8) .outputItems(HPCA_ACTIVE_COOLER_COMPONENT) .inputFluids(PCBCoolant.getFluid(1000)) @@ -211,7 +211,7 @@ public static void init(Consumer provider) { .inputItems(ADVANCED_COMPUTER_CASING.asStack()) .inputItems(CustomTags.UV_CIRCUITS) .inputItems(EMITTER_ZPM) - .inputItems(OPTICAL_PIPES[0].asStack(2)) + .inputItems(OPTICAL_PIPE.asStack(2)) .outputItems(HPCA_BRIDGE_COMPONENT) .inputFluids(PCBCoolant.getFluid(1000)) .cleanroom(CleanroomType.CLEANROOM) @@ -240,7 +240,7 @@ public static void init(Consumer provider) { .inputItems(ITEM_IMPORT_BUS[LuV]) .inputItems(CustomTags.LuV_CIRCUITS) .inputItems(SENSOR_IV) - .inputItems(OPTICAL_PIPES[0].asStack(2)) + .inputItems(OPTICAL_PIPE.asStack(2)) .inputFluids(Polybenzimidazole.getFluid(L * 2)) .outputItems(DATA_HATCH_RECEIVER) .cleanroom(CleanroomType.CLEANROOM) @@ -251,7 +251,7 @@ public static void init(Consumer provider) { .inputItems(ITEM_EXPORT_BUS[LuV]) .inputItems(CustomTags.LuV_CIRCUITS) .inputItems(EMITTER_IV) - .inputItems(OPTICAL_PIPES[0].asStack(2)) + .inputItems(OPTICAL_PIPE.asStack(2)) .inputFluids(Polybenzimidazole.getFluid(L * 2)) .outputItems(DATA_HATCH_TRANSMITTER) .cleanroom(CleanroomType.CLEANROOM) @@ -288,8 +288,31 @@ public static void init(Consumer provider) { .inputItems(CASING_LAMINATED_GLASS.asStack(1)) .inputItems(foil, Osmiridium, 2) .inputFluids(Polytetrafluoroethylene.getFluid(L)) - .outputItems(LASER_PIPES[0]) + .outputItems(LASER_PIPE) .cleanroom(CleanroomType.CLEANROOM) .duration(100).EUt(VA[IV]).save(provider); + + FORMING_PRESS_RECIPES.recipeBuilder("laser_reflector") + .inputItems(plate, BorosilicateGlass) + .inputItems(foil, Titanium, 12) + .inputItems(foil, Silicon, 12) + .inputItems(foil, Tantalum, 12) + .inputItems(foil, Zinc, 12) + .inputItems(foil, Beryllium, 12) + .outputItems(LASER_REFLECTOR) + .cleanroom(CleanroomType.CLEANROOM) + .duration(200).EUt(VA[LuV]).save(provider); + + ASSEMBLY_LINE_RECIPES.recipeBuilder("laser_reflector_pipe") + .inputItems(frameGt, Trinium) + .inputItems(LASER_PIPE, 2) + .inputItems(LASER_REFLECTOR) + .inputItems(lens, NetherStar, 2) + .inputItems(foil, Osmiridium, 20) + .inputFluids(Polybenzimidazole.getFluid(L)) + .outputItems(LASER_REFLECTOR_PIPE) + .scannerResearch(b -> b.researchStack(LASER_PIPE.asStack()) + .EUt(VA[IV])) + .duration(200).EUt(VA[LuV]).save(provider); } } diff --git a/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/CraftingRecipeLoader.java b/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/CraftingRecipeLoader.java index 72fdb6ba40..4c1cec3db7 100644 --- a/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/CraftingRecipeLoader.java +++ b/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/CraftingRecipeLoader.java @@ -29,20 +29,20 @@ public static void init(Consumer provider) { // todo facades // registerFacadeRecipe(provider, Iron, 4); - VanillaRecipeHelper.addShapedRecipe(provider, "small_wooden_pipe", ChemicalHelper.get(pipeSmallFluid, Wood), + VanillaRecipeHelper.addShapedRecipe(provider, "small_wooden_pipe", ChemicalHelper.get(pipeSmall, Wood), "sWr", 'W', ItemTags.PLANKS); - VanillaRecipeHelper.addShapedRecipe(provider, "normal_wooden_pipe", ChemicalHelper.get(pipeNormalFluid, Wood), + VanillaRecipeHelper.addShapedRecipe(provider, "normal_wooden_pipe", ChemicalHelper.get(pipeNormal, Wood), "WWW", "s r", 'W', ItemTags.PLANKS); - VanillaRecipeHelper.addShapedRecipe(provider, "large_wooden_pipe", ChemicalHelper.get(pipeLargeFluid, Wood), + VanillaRecipeHelper.addShapedRecipe(provider, "large_wooden_pipe", ChemicalHelper.get(pipeLarge, Wood), "WWW", "s r", "WWW", 'W', ItemTags.PLANKS); VanillaRecipeHelper.addShapedRecipe(provider, "small_treated_wooden_pipe", - ChemicalHelper.get(pipeSmallFluid, TreatedWood), "sWr", 'W', GTBlocks.TREATED_WOOD_PLANK.asStack()); + ChemicalHelper.get(pipeSmall, TreatedWood), "sWr", 'W', GTBlocks.TREATED_WOOD_PLANK.asStack()); VanillaRecipeHelper.addShapedRecipe(provider, "normal_treated_wooden_pipe", - ChemicalHelper.get(pipeNormalFluid, TreatedWood), "WWW", "s r", 'W', + ChemicalHelper.get(pipeNormal, TreatedWood), "WWW", "s r", 'W', GTBlocks.TREATED_WOOD_PLANK.asStack()); VanillaRecipeHelper.addShapedRecipe(provider, "large_treated_wooden_pipe", - ChemicalHelper.get(pipeLargeFluid, TreatedWood), "WWW", "s r", "WWW", 'W', + ChemicalHelper.get(pipeLarge, TreatedWood), "WWW", "s r", "WWW", 'W', GTBlocks.TREATED_WOOD_PLANK.asStack()); VanillaRecipeHelper.addShapelessRecipe(provider, "programmed_circuit", PROGRAMMED_CIRCUIT.asStack(), @@ -154,7 +154,7 @@ public static void init(Consumer provider) { new UnificationEntry(rotor, Steel)); VanillaRecipeHelper.addShapedRecipe(provider, true, "filter_casing_sterile", GTBlocks.FILTER_CASING_STERILE.asStack(), "BEB", "ISI", "MFR", 'B', - new UnificationEntry(pipeLargeFluid, Polybenzimidazole), 'E', EMITTER_ZPM.asStack(), 'I', + new UnificationEntry(pipeLarge, Polybenzimidazole), 'E', EMITTER_ZPM.asStack(), 'I', ITEM_FILTER.asStack(), 'S', BLACKLIGHT.asStack(), 'M', ELECTRIC_MOTOR_ZPM.asStack(), 'F', new UnificationEntry(frameGt, Tritanium), 'R', new UnificationEntry(rotor, NaquadahAlloy)); @@ -288,7 +288,7 @@ public static void init(Consumer provider) { VanillaRecipeHelper.addShapedRecipe(provider, "fluid_jetpack", GTItems.LIQUID_FUEL_JETPACK.asStack(), "xCw", "SUS", "RIR", 'C', CustomTags.LV_CIRCUITS, 'S', GTItems.FLUID_CELL_LARGE_STEEL.asStack(), 'U', GTItems.ELECTRIC_PUMP_LV.asStack(), 'R', new UnificationEntry(rotor, Lead), 'I', - new UnificationEntry(pipeSmallFluid, Potin)); + new UnificationEntry(pipeSmall, Potin)); VanillaRecipeHelper.addShapedRecipe(provider, "electric_jetpack", GTItems.ELECTRIC_JETPACK.asStack(), "xCd", "TBT", "I I", 'C', CustomTags.MV_CIRCUITS, 'T', GTItems.POWER_THRUSTER.asStack(), 'B', GTItems.BATTERY_MV_LITHIUM.asStack(), 'I', new UnificationEntry(wireGtDouble, AnnealedCopper)); diff --git a/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/FuelRecipes.java b/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/FuelRecipes.java index 5f957bc720..74ecccac92 100644 --- a/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/FuelRecipes.java +++ b/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/FuelRecipes.java @@ -71,7 +71,7 @@ public static void init(Consumer provider) { .save(provider); LARGE_BOILER_RECIPES.recipeBuilder("oil_heavy") - .inputFluids(OilHeavy.getFluid(32)) + .inputFluids(HeavyOil.getFluid(32)) .duration(10) .save(provider); @@ -170,7 +170,7 @@ public static void init(Consumer provider) { .save(provider); COMBUSTION_GENERATOR_FUELS.recipeBuilder("light_oil") - .inputFluids(OilLight.getFluid(32)) + .inputFluids(LightOil.getFluid(32)) .duration(5) .EUt(-V[LV]) .save(provider); diff --git a/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/GCYMRecipes.java b/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/GCYMRecipes.java index 3e945fb0fd..d41453e91f 100644 --- a/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/GCYMRecipes.java +++ b/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/GCYMRecipes.java @@ -27,9 +27,9 @@ import static com.gregtechceu.gtceu.common.data.GTItems.*; import static com.gregtechceu.gtceu.common.data.GTMachines.*; import static com.gregtechceu.gtceu.common.data.GTMaterials.*; -import static com.gregtechceu.gtceu.common.data.GTRecipeTypes.ASSEMBLER_RECIPES; -import static com.gregtechceu.gtceu.common.data.GTRecipeTypes.MIXER_RECIPES; +import static com.gregtechceu.gtceu.common.data.GTRecipeTypes.*; import static com.gregtechceu.gtceu.common.data.machines.GCYMMachines.*; +import static com.gregtechceu.gtceu.common.data.machines.GTMultiMachines.*; import static com.gregtechceu.gtceu.data.recipe.CustomTags.*; public class GCYMRecipes { @@ -80,11 +80,11 @@ private static void registerMultiblockControllerRecipes(Consumer 'C', CustomTags.EV_CIRCUITS, 'P', new UnificationEntry(plate, HSLASteel), 'R', ROBOT_ARM_HV.asStack(), 'K', CONVEYOR_MODULE_HV.asStack(), 'X', PACKER[HV].asStack()); VanillaRecipeHelper.addShapedRecipe(provider, true, "large_mixer", LARGE_MIXER.asStack(), "FCF", "RXR", "MKM", - 'C', CustomTags.IV_CIRCUITS, 'F', ChemicalHelper.get(pipeNormalFluid, Polybenzimidazole), 'R', + 'C', CustomTags.IV_CIRCUITS, 'F', ChemicalHelper.get(pipeNormal, Polybenzimidazole), 'R', ChemicalHelper.get(rotor, Osmiridium), 'M', ELECTRIC_MOTOR_IV.asStack(), 'X', MIXER[IV].asStack(), 'K', new UnificationEntry(TagPrefix.cableGtSingle, Platinum)); VanillaRecipeHelper.addShapedRecipe(provider, true, "large_centrifuge", LARGE_CENTRIFUGE.asStack(), "SFS", - "CXC", "MKM", 'C', CustomTags.IV_CIRCUITS, 'F', ChemicalHelper.get(pipeHugeFluid, StainlessSteel), 'S', + "CXC", "MKM", 'C', CustomTags.IV_CIRCUITS, 'F', ChemicalHelper.get(pipeHuge, StainlessSteel), 'S', ChemicalHelper.get(spring, MolybdenumDisilicide), 'M', ELECTRIC_MOTOR_IV.asStack(), 'X', CENTRIFUGE[IV].asStack(), 'K', new UnificationEntry(TagPrefix.cableGtSingle, Platinum)); VanillaRecipeHelper.addShapedRecipe(provider, true, "large_assembler", LARGE_ASSEMBLER.asStack(), "RKR", "CXC", @@ -110,12 +110,14 @@ private static void registerMultiblockControllerRecipes(Consumer FIELD_GENERATOR_ZPM.asStack(), 'P', new UnificationEntry(spring, Naquadah), 'D', new UnificationEntry(plateDense, NaquadahAlloy), 'W', new UnificationEntry(wireGtQuadruple, RutheniumTriniumAmericiumNeutronate)); - VanillaRecipeHelper.addShapedRecipe(provider, true, "mega_vacuum_freezer", MEGA_VACUUM_FREEZER.asStack(), "PCP", - "FSF", "DWD", 'C', ZPM_CIRCUITS, 'S', GTMultiMachines.VACUUM_FREEZER.asStack(), 'F', - FIELD_GENERATOR_ZPM.asStack(), 'P', - new UnificationEntry(pipeNormalFluid, NiobiumTitanium), 'D', - new UnificationEntry(plateDense, RhodiumPlatedPalladium), 'W', - new UnificationEntry(wireGtQuadruple, RutheniumTriniumAmericiumNeutronate)); + VanillaRecipeHelper.addShapedRecipe(provider, true, "mega_vacuum_freezer", MEGA_VACUUM_FREEZER.asStack(), + "PCP", "FSF", "DWD", + 'C', ZPM_CIRCUITS, + 'S', VACUUM_FREEZER.asStack(), + 'F', FIELD_GENERATOR_ZPM.asStack(), + 'P', new UnificationEntry(pipeNormal, NiobiumTitanium), + 'D', new UnificationEntry(plateDense, RhodiumPlatedPalladium), + 'W', new UnificationEntry(wireGtQuadruple, RutheniumTriniumAmericiumNeutronate)); VanillaRecipeHelper.addShapedRecipe(provider, true, "large_autoclave", LARGE_AUTOCLAVE.asStack(), "PCP", "PAP", "BKB", 'C', CustomTags.IV_CIRCUITS, 'A', AUTOCLAVE[IV].asStack(), 'P', new UnificationEntry(plateDouble, HSLASteel), 'B', ELECTRIC_PUMP_IV.asStack(), 'K', @@ -133,9 +135,12 @@ private static void registerMultiblockControllerRecipes(Consumer CUTTER[IV].asStack(), 'M', CONVEYOR_MODULE_IV.asStack(), 'S', new UnificationEntry(toolHeadBuzzSaw, TungstenCarbide), 'K', new UnificationEntry(cableGtSingle, Platinum)); - VanillaRecipeHelper.addShapedRecipe(provider, true, "large_distillery", LARGE_DISTILLERY.asStack(), "PZP", - "EDE", "PZP", 'Z', CustomTags.IV_CIRCUITS, 'D', GTMultiMachines.DISTILLATION_TOWER.asStack(), 'E', - ELECTRIC_PUMP_IV.asStack(), 'P', ChemicalHelper.get(pipeLargeFluid, Iridium)); + VanillaRecipeHelper.addShapedRecipe(provider, true, "large_distillery", LARGE_DISTILLERY.asStack(), + "PZP", "EDE", "PZP", + 'Z', CustomTags.IV_CIRCUITS, + 'D', DISTILLATION_TOWER.asStack(), + 'E', ELECTRIC_PUMP_IV.asStack(), + 'P', ChemicalHelper.get(pipeLarge, Iridium)); VanillaRecipeHelper.addShapedRecipe(provider, true, "large_extractor", LARGE_EXTRACTOR.asStack(), "PTP", "EZC", "BKB", 'Z', CustomTags.IV_CIRCUITS, 'B', ELECTRIC_PISTON_IV.asStack(), 'P', ELECTRIC_PUMP_IV.asStack(), 'E', EXTRACTOR[IV].asStack(), 'C', CANNER[IV].asStack(), 'T', CASING_TEMPERED_GLASS.asStack(), 'K', @@ -145,7 +150,7 @@ private static void registerMultiblockControllerRecipes(Consumer new UnificationEntry(spring, MolybdenumDisilicide), 'K', new UnificationEntry(cableGtSingle, Platinum)); VanillaRecipeHelper.addShapedRecipe(provider, true, "large_solidifier", LARGE_SOLIDIFIER.asStack(), "PZP", "ESE", "PKP", 'Z', CustomTags.IV_CIRCUITS, 'S', FLUID_SOLIDIFIER[IV].asStack(), 'E', - ELECTRIC_PUMP_IV.asStack(), 'P', ChemicalHelper.get(pipeNormalFluid, Polyethylene), 'K', + ELECTRIC_PUMP_IV.asStack(), 'P', ChemicalHelper.get(pipeNormal, Polyethylene), 'K', new UnificationEntry(cableGtSingle, Platinum)); VanillaRecipeHelper.addShapedRecipe(provider, true, "large_wiremill", LARGE_WIREMILL.asStack(), "PZP", "SWS", "MKM", 'Z', CustomTags.IV_CIRCUITS, 'W', WIREMILL[IV].asStack(), 'P', diff --git a/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/MachineRecipeLoader.java b/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/MachineRecipeLoader.java index 4f3f3cbaa0..b598000583 100644 --- a/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/MachineRecipeLoader.java +++ b/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/MachineRecipeLoader.java @@ -741,7 +741,7 @@ private static void registerAssemblerRecipes(Consumer provider) ASSEMBLER_RECIPES.recipeBuilder("fusion_coil").EUt(VA[ZPM]).inputItems(GTBlocks.SUPERCONDUCTING_COIL.asStack()) .inputItems(FIELD_GENERATOR_IV.asStack(2)).inputItems(ELECTRIC_PUMP_IV) .inputItems(NEUTRON_REFLECTOR.asStack(2)).inputItems(CustomTags.LuV_CIRCUITS, 4) - .inputItems(pipeSmallFluid, Naquadah, 4).inputItems(plate, Europium, 4) + .inputItems(pipeSmall, Naquadah, 4).inputItems(plate, Europium, 4) .inputFluids(VanadiumGallium.getFluid(GTValues.L * 4)).outputItems(GTBlocks.FUSION_COIL.asStack()) .duration(100).cleanroom(CleanroomType.CLEANROOM).save(provider); ASSEMBLER_RECIPES.recipeBuilder("fusion_glass").EUt(VA[LuV]) @@ -1155,8 +1155,8 @@ private static void registerRecyclingRecipes(Consumer provider) .save(provider); MACERATOR_RECIPES.recipeBuilder("macerate_red_granite") - .inputItems(rock, GraniteRed) - .outputItems(dust, GraniteRed) + .inputItems(rock, RedGranite) + .outputItems(dust, RedGranite) .chancedOutput(dust, Uranium238, 10, 5) .duration(150).EUt(2) .save(provider); diff --git a/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/MetaTileEntityLoader.java b/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/MetaTileEntityLoader.java index 3b7c36de02..0beba18211 100644 --- a/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/MetaTileEntityLoader.java +++ b/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/MetaTileEntityLoader.java @@ -118,27 +118,27 @@ public static void init(Consumer provider) { GTBlocks.CASING_BRONZE_PIPE.asStack(ConfigHolder.INSTANCE.recipes.casingsPerCraft), "PIP", "IFI", "PIP", 'P', new UnificationEntry(TagPrefix.plate, GTMaterials.Bronze), 'F', new UnificationEntry(TagPrefix.frameGt, GTMaterials.Bronze), 'I', - new UnificationEntry(TagPrefix.pipeNormalFluid, GTMaterials.Bronze)); + new UnificationEntry(TagPrefix.pipeNormal, GTMaterials.Bronze)); VanillaRecipeHelper.addShapedRecipe(provider, true, "casing_steel_pipe", GTBlocks.CASING_STEEL_PIPE.asStack(ConfigHolder.INSTANCE.recipes.casingsPerCraft), "PIP", "IFI", "PIP", 'P', new UnificationEntry(TagPrefix.plate, GTMaterials.Steel), 'F', new UnificationEntry(TagPrefix.frameGt, GTMaterials.Steel), 'I', - new UnificationEntry(TagPrefix.pipeNormalFluid, GTMaterials.Steel)); + new UnificationEntry(TagPrefix.pipeNormal, GTMaterials.Steel)); VanillaRecipeHelper.addShapedRecipe(provider, true, "casing_titanium_pipe", GTBlocks.CASING_TITANIUM_PIPE.asStack(ConfigHolder.INSTANCE.recipes.casingsPerCraft), "PIP", "IFI", "PIP", 'P', new UnificationEntry(TagPrefix.plate, GTMaterials.Titanium), 'F', new UnificationEntry(TagPrefix.frameGt, GTMaterials.Titanium), 'I', - new UnificationEntry(TagPrefix.pipeNormalFluid, GTMaterials.Titanium)); + new UnificationEntry(TagPrefix.pipeNormal, GTMaterials.Titanium)); VanillaRecipeHelper.addShapedRecipe(provider, true, "casing_tungstensteel_pipe", GTBlocks.CASING_TUNGSTENSTEEL_PIPE.asStack(ConfigHolder.INSTANCE.recipes.casingsPerCraft), "PIP", "IFI", "PIP", 'P', new UnificationEntry(TagPrefix.plate, GTMaterials.TungstenSteel), 'F', new UnificationEntry(TagPrefix.frameGt, GTMaterials.TungstenSteel), 'I', - new UnificationEntry(TagPrefix.pipeNormalFluid, GTMaterials.TungstenSteel)); + new UnificationEntry(TagPrefix.pipeNormal, GTMaterials.TungstenSteel)); VanillaRecipeHelper.addShapedRecipe(provider, true, "casing_ptfe_pipe", GTBlocks.CASING_POLYTETRAFLUOROETHYLENE_PIPE.asStack(ConfigHolder.INSTANCE.recipes.casingsPerCraft), "PIP", "IFI", "PIP", 'P', new UnificationEntry(TagPrefix.plate, GTMaterials.Polytetrafluoroethylene), 'F', new UnificationEntry(TagPrefix.frameGt, GTMaterials.Polytetrafluoroethylene), 'I', - new UnificationEntry(TagPrefix.pipeNormalFluid, GTMaterials.Polytetrafluoroethylene)); + new UnificationEntry(TagPrefix.pipeNormal, GTMaterials.Polytetrafluoroethylene)); VanillaRecipeHelper.addShapedRecipe(provider, true, "casing_bronze_firebox", GTBlocks.FIREBOX_BRONZE.asStack(2), "PSP", "SFS", "PSP", 'P', new UnificationEntry(TagPrefix.plate, GTMaterials.Bronze), 'F', new UnificationEntry(TagPrefix.frameGt, GTMaterials.Bronze), 'S', @@ -346,22 +346,22 @@ public static void init(Consumer provider) { provider, true, "fluid_import_hatch_4x_" + tierName, importHatch4x.asStack(), "P", "M", 'M', importHatch.asStack(), - 'P', new UnificationEntry(TagPrefix.pipeQuadrupleFluid, material)); + 'P', new UnificationEntry(TagPrefix.pipeQuadruple, material)); VanillaRecipeHelper.addShapedRecipe( provider, true, "fluid_export_hatch_4x_" + tierName, exportHatch4x.asStack(), "M", "P", 'M', exportHatch.asStack(), - 'P', new UnificationEntry(TagPrefix.pipeQuadrupleFluid, material)); + 'P', new UnificationEntry(TagPrefix.pipeQuadruple, material)); VanillaRecipeHelper.addShapedRecipe( provider, true, "fluid_import_hatch_9x_" + tierName, importHatch9x.asStack(), "P", "M", 'M', importHatch.asStack(), - 'P', new UnificationEntry(TagPrefix.pipeNonupleFluid, material)); + 'P', new UnificationEntry(TagPrefix.pipeNonuple, material)); VanillaRecipeHelper.addShapedRecipe( provider, true, "fluid_export_hatch_9x_" + tierName, exportHatch9x.asStack(), "M", "P", 'M', exportHatch.asStack(), - 'P', new UnificationEntry(TagPrefix.pipeNonupleFluid, material)); + 'P', new UnificationEntry(TagPrefix.pipeNonuple, material)); } VanillaRecipeHelper.addShapedRecipe(provider, true, "rotor_holder_hv", GTMachines.ROTOR_HOLDER[HV].asStack(), @@ -417,7 +417,7 @@ public static void init(Consumer provider) { VanillaRecipeHelper.addShapedRecipe(provider, true, "passthrough_hatch_fluid", GTMachines.FLUID_PASSTHROUGH_HATCH[HV].asStack(), " C ", "GHG", " S ", 'C', GTItems.ELECTRIC_PUMP_HV.asStack(), 'G', - new UnificationEntry(TagPrefix.pipeSmallFluid, GTMaterials.Steel), 'H', GTMachines.HULL[HV].asStack(), + new UnificationEntry(TagPrefix.pipeSmall, GTMaterials.Steel), 'H', GTMachines.HULL[HV].asStack(), 'S', GTBlocks.CASING_TEMPERED_GLASS); // STEAM MACHINES @@ -449,93 +449,93 @@ public static void init(Consumer provider) { VanillaRecipeHelper.addShapedRecipe(provider, true, "steam_boiler_solar_bronze", GTMachines.STEAM_SOLAR_BOILER.left().asStack(), "GGG", "SSS", "PMP", 'M', GTBlocks.BRONZE_BRICKS_HULL.asStack(), 'P', - new UnificationEntry(TagPrefix.pipeSmallFluid, GTMaterials.Bronze), 'S', + new UnificationEntry(TagPrefix.pipeSmall, GTMaterials.Bronze), 'S', new UnificationEntry(TagPrefix.plate, GTMaterials.Silver), 'G', new ItemStack(Blocks.GLASS)); VanillaRecipeHelper.addShapedRecipe(provider, true, "steam_boiler_solar_steel", GTMachines.STEAM_SOLAR_BOILER.right().asStack(), "GGG", "SSS", "PMP", 'M', GTBlocks.STEEL_BRICKS_HULL.asStack(), 'P', - new UnificationEntry(TagPrefix.pipeSmallFluid, GTMaterials.Steel), 'S', + new UnificationEntry(TagPrefix.pipeSmall, GTMaterials.Steel), 'S', new UnificationEntry(TagPrefix.plateDouble, GTMaterials.Silver), 'G', new ItemStack(Blocks.GLASS)); VanillaRecipeHelper.addShapedRecipe(provider, true, "steam_furnace_bronze", GTMachines.STEAM_FURNACE.left().asStack(), "XXX", "XMX", "XFX", 'M', GTBlocks.BRONZE_BRICKS_HULL.asStack(), 'X', - new UnificationEntry(TagPrefix.pipeSmallFluid, GTMaterials.Bronze), 'F', Blocks.FURNACE); + new UnificationEntry(TagPrefix.pipeSmall, GTMaterials.Bronze), 'F', Blocks.FURNACE); VanillaRecipeHelper.addShapedRecipe(provider, true, "steam_furnace_steel", GTMachines.STEAM_FURNACE.right().asStack(), "XSX", "PMP", "XXX", 'M', GTMachines.STEAM_FURNACE.left().asStack(), 'X', - new UnificationEntry(TagPrefix.pipeSmallFluid, GTMaterials.TinAlloy), 'S', + new UnificationEntry(TagPrefix.pipeSmall, GTMaterials.TinAlloy), 'S', new UnificationEntry(TagPrefix.plate, GTMaterials.Steel), 'P', new UnificationEntry(TagPrefix.plate, GTMaterials.WroughtIron)); VanillaRecipeHelper.addShapedRecipe(provider, true, "steam_macerator_bronze", GTMachines.STEAM_MACERATOR.left().asStack(), "DXD", "XMX", "PXP", 'M', GTBlocks.BRONZE_HULL.asStack(), - 'X', new UnificationEntry(TagPrefix.pipeSmallFluid, GTMaterials.Bronze), 'P', CustomTags.PISTONS, + 'X', new UnificationEntry(TagPrefix.pipeSmall, GTMaterials.Bronze), 'P', CustomTags.PISTONS, 'D', new UnificationEntry(TagPrefix.gem, GTMaterials.Diamond)); VanillaRecipeHelper.addShapedRecipe(provider, true, "steam_macerator_steel", GTMachines.STEAM_MACERATOR.right().asStack(), "WSW", "PMP", "WWW", 'M', GTMachines.STEAM_MACERATOR.left().asStack(), 'W', new UnificationEntry(TagPrefix.plate, GTMaterials.WroughtIron), 'S', new UnificationEntry(TagPrefix.plate, GTMaterials.Steel), 'P', - new UnificationEntry(TagPrefix.pipeSmallFluid, GTMaterials.TinAlloy)); + new UnificationEntry(TagPrefix.pipeSmall, GTMaterials.TinAlloy)); VanillaRecipeHelper.addShapedRecipe(provider, true, "steam_extractor_bronze", GTMachines.STEAM_EXTRACTOR.left().asStack(), "XXX", "PMG", "XXX", 'M', GTBlocks.BRONZE_HULL.asStack(), - 'X', new UnificationEntry(TagPrefix.pipeSmallFluid, GTMaterials.Bronze), 'P', CustomTags.PISTONS, + 'X', new UnificationEntry(TagPrefix.pipeSmall, GTMaterials.Bronze), 'P', CustomTags.PISTONS, 'G', new ItemStack(Blocks.GLASS)); VanillaRecipeHelper.addShapedRecipe(provider, true, "steam_extractor_steel", GTMachines.STEAM_EXTRACTOR.right().asStack(), "PSP", "WMW", "PPP", 'M', GTMachines.STEAM_EXTRACTOR.left().asStack(), 'P', - new UnificationEntry(TagPrefix.pipeSmallFluid, GTMaterials.TinAlloy), 'S', + new UnificationEntry(TagPrefix.pipeSmall, GTMaterials.TinAlloy), 'S', new UnificationEntry(TagPrefix.plate, GTMaterials.Steel), 'W', new UnificationEntry(TagPrefix.plate, GTMaterials.WroughtIron)); VanillaRecipeHelper.addShapedRecipe(provider, true, "steam_hammer_bronze", GTMachines.STEAM_HAMMER.left().asStack(), "XPX", "XMX", "XAX", 'M', GTBlocks.BRONZE_HULL.asStack(), 'X', - new UnificationEntry(TagPrefix.pipeSmallFluid, GTMaterials.Bronze), 'P', CustomTags.PISTONS, 'A', + new UnificationEntry(TagPrefix.pipeSmall, GTMaterials.Bronze), 'P', CustomTags.PISTONS, 'A', Blocks.ANVIL); VanillaRecipeHelper.addShapedRecipe(provider, true, "steam_hammer_steel", GTMachines.STEAM_HAMMER.right().asStack(), "WSW", "PMP", "WWW", 'M', GTMachines.STEAM_HAMMER.left().asStack(), 'S', new UnificationEntry(TagPrefix.plate, GTMaterials.Steel), 'W', new UnificationEntry(TagPrefix.plate, GTMaterials.WroughtIron), 'P', - new UnificationEntry(TagPrefix.pipeSmallFluid, GTMaterials.TinAlloy)); + new UnificationEntry(TagPrefix.pipeSmall, GTMaterials.TinAlloy)); VanillaRecipeHelper.addShapedRecipe(provider, true, "steam_compressor_bronze", GTMachines.STEAM_COMPRESSOR.left().asStack(), "XXX", "PMP", "XXX", 'M', GTBlocks.BRONZE_HULL.asStack(), - 'X', new UnificationEntry(TagPrefix.pipeSmallFluid, GTMaterials.Bronze), 'P', CustomTags.PISTONS); + 'X', new UnificationEntry(TagPrefix.pipeSmall, GTMaterials.Bronze), 'P', CustomTags.PISTONS); VanillaRecipeHelper.addShapedRecipe(provider, true, "steam_compressor_steel", GTMachines.STEAM_COMPRESSOR.right().asStack(), "PSP", "WMW", "PPP", 'M', GTMachines.STEAM_COMPRESSOR.left().asStack(), 'S', new UnificationEntry(TagPrefix.plate, GTMaterials.Steel), 'W', new UnificationEntry(TagPrefix.plate, GTMaterials.WroughtIron), 'P', - new UnificationEntry(TagPrefix.pipeSmallFluid, GTMaterials.TinAlloy)); + new UnificationEntry(TagPrefix.pipeSmall, GTMaterials.TinAlloy)); VanillaRecipeHelper.addShapedRecipe(provider, true, "steam_alloy_smelter_bronze", GTMachines.STEAM_ALLOY_SMELTER.left().asStack(), "XXX", "FMF", "XXX", 'M', GTBlocks.BRONZE_BRICKS_HULL.asStack(), 'X', - new UnificationEntry(TagPrefix.pipeSmallFluid, GTMaterials.Bronze), 'F', Blocks.FURNACE); + new UnificationEntry(TagPrefix.pipeSmall, GTMaterials.Bronze), 'F', Blocks.FURNACE); VanillaRecipeHelper.addShapedRecipe(provider, true, "steam_alloy_smelter_steel", GTMachines.STEAM_ALLOY_SMELTER.right().asStack(), "WSW", "WMW", "WPW", 'M', GTMachines.STEAM_ALLOY_SMELTER.left().asStack(), 'S', new UnificationEntry(TagPrefix.plate, GTMaterials.Steel), 'W', new UnificationEntry(TagPrefix.plate, GTMaterials.WroughtIron), 'P', - new UnificationEntry(TagPrefix.pipeSmallFluid, GTMaterials.TinAlloy)); + new UnificationEntry(TagPrefix.pipeSmall, GTMaterials.TinAlloy)); VanillaRecipeHelper.addShapedRecipe(provider, true, "steam_rock_breaker_bronze", GTMachines.STEAM_ROCK_CRUSHER.left().asStack(), "PXP", "XMX", "DXD", 'M', - GTBlocks.BRONZE_HULL.asStack(), 'X', new UnificationEntry(TagPrefix.pipeSmallFluid, GTMaterials.Bronze), + GTBlocks.BRONZE_HULL.asStack(), 'X', new UnificationEntry(TagPrefix.pipeSmall, GTMaterials.Bronze), 'P', CustomTags.PISTONS, 'D', new UnificationEntry(TagPrefix.gem, GTMaterials.Diamond)); VanillaRecipeHelper.addShapedRecipe(provider, true, "steam_rock_breaker_steel", GTMachines.STEAM_ROCK_CRUSHER.right().asStack(), "WSW", "PMP", "WWW", 'M', GTMachines.STEAM_ROCK_CRUSHER.left().asStack(), 'W', new UnificationEntry(TagPrefix.plate, GTMaterials.WroughtIron), 'S', new UnificationEntry(TagPrefix.plate, GTMaterials.Steel), 'P', - new UnificationEntry(TagPrefix.pipeSmallFluid, GTMaterials.TinAlloy)); + new UnificationEntry(TagPrefix.pipeSmall, GTMaterials.TinAlloy)); VanillaRecipeHelper.addShapedRecipe(provider, true, "steam_miner_bronze", GTMachines.STEAM_MINER.first().asStack(), "DSD", "SMS", "GSG", 'M', GTBlocks.BRONZE_BRICKS_HULL.asStack(), - 'S', new UnificationEntry(TagPrefix.pipeNormalFluid, GTMaterials.Bronze), + 'S', new UnificationEntry(TagPrefix.pipeNormal, GTMaterials.Bronze), 'D', new UnificationEntry(TagPrefix.gem, GTMaterials.Diamond), 'G', new UnificationEntry(TagPrefix.gearSmall, GTMaterials.Bronze)); VanillaRecipeHelper.addShapedRecipe(provider, true, "steam_miner_steel", GTMachines.STEAM_MINER.second().asStack(), "DSD", "SMS", "GSG", 'M', GTMachines.STEAM_MINER.first().asStack(), - 'S', new UnificationEntry(TagPrefix.pipeNormalFluid, GTMaterials.TinAlloy), + 'S', new UnificationEntry(TagPrefix.pipeNormal, GTMaterials.TinAlloy), 'D', new UnificationEntry(TagPrefix.gem, GTMaterials.Diamond), 'G', new UnificationEntry(TagPrefix.gearSmall, GTMaterials.Steel)); // MULTI BLOCK CONTROLLERS @@ -581,8 +581,7 @@ public static void init(Consumer provider) { 'C', CustomTags.HV_CIRCUITS, 'W', new UnificationEntry(TagPrefix.cableGtSingle, GTMaterials.Gold)); VanillaRecipeHelper.addShapedRecipe(provider, true, "distillation_tower", GTMultiMachines.DISTILLATION_TOWER.asStack(), "CBC", "FMF", "CBC", 'M', GTMachines.HULL[HV].asStack(), - 'B', - new UnificationEntry(TagPrefix.pipeLargeFluid, GTMaterials.StainlessSteel), 'C', CustomTags.EV_CIRCUITS, + 'B', new UnificationEntry(TagPrefix.pipeLarge, GTMaterials.StainlessSteel), 'C', CustomTags.EV_CIRCUITS, 'F', GTItems.ELECTRIC_PUMP_HV); VanillaRecipeHelper.addShapedRecipe(provider, true, "evaporation_plant", GTMultiMachines.EVAPORATION_PLANT.asStack(), "CBC", "FMF", "CBC", 'M', GTMachines.HULL[HV].asStack(), @@ -614,32 +613,33 @@ public static void init(Consumer provider) { GTBlocks.CASING_ENGINE_INTAKE.asStack(ConfigHolder.INSTANCE.recipes.casingsPerCraft), "PhP", "RFR", "PwP", 'R', new UnificationEntry(TagPrefix.rotor, GTMaterials.Titanium), 'F', GTBlocks.CASING_TITANIUM_STABLE.asStack(), 'P', - new UnificationEntry(TagPrefix.pipeNormalFluid, GTMaterials.Titanium)); + new UnificationEntry(TagPrefix.pipeNormal, GTMaterials.Titanium)); VanillaRecipeHelper.addShapedRecipe(provider, true, "extreme_engine_intake_casing", GTBlocks.CASING_EXTREME_ENGINE_INTAKE.asStack(ConfigHolder.INSTANCE.recipes.casingsPerCraft), "PhP", "RFR", "PwP", 'R', new UnificationEntry(TagPrefix.rotor, GTMaterials.TungstenSteel), 'F', GTBlocks.CASING_TUNGSTENSTEEL_ROBUST.asStack(), 'P', - new UnificationEntry(TagPrefix.pipeNormalFluid, GTMaterials.TungstenSteel)); + new UnificationEntry(TagPrefix.pipeNormal, GTMaterials.TungstenSteel)); VanillaRecipeHelper.addShapedRecipe(provider, true, "multi_furnace", GTMultiMachines.MULTI_SMELTER.asStack(), - "PPP", - "ASA", "CAC", 'P', Blocks.FURNACE, 'A', CustomTags.HV_CIRCUITS, 'S', - GTBlocks.CASING_INVAR_HEATPROOF.asStack(), 'C', - new UnificationEntry(TagPrefix.cableGtSingle, GTMaterials.Copper)); + "PPP", "ASA", "CAC", + 'P', Blocks.FURNACE, + 'A', CustomTags.HV_CIRCUITS, + 'S', GTBlocks.CASING_INVAR_HEATPROOF.asStack(), + 'C', new UnificationEntry(TagPrefix.cableGtSingle, GTMaterials.Copper)); VanillaRecipeHelper.addShapedRecipe(provider, true, "large_steam_turbine", GTMultiMachines.LARGE_STEAM_TURBINE.asStack(), "PSP", "SAS", "CSC", 'S', new UnificationEntry(TagPrefix.gear, GTMaterials.Steel), 'P', CustomTags.HV_CIRCUITS, 'A', - GTMachines.HULL[HV].asStack(), 'C', new UnificationEntry(TagPrefix.pipeLargeFluid, GTMaterials.Steel)); + GTMachines.HULL[HV].asStack(), 'C', new UnificationEntry(TagPrefix.pipeLarge, GTMaterials.Steel)); VanillaRecipeHelper.addShapedRecipe(provider, true, "large_gas_turbine", - GTMultiMachines.LARGE_GAS_TURBINE.asStack(), - "PSP", "SAS", "CSC", 'S', new UnificationEntry(TagPrefix.gear, GTMaterials.StainlessSteel), 'P', + GTMultiMachines.LARGE_GAS_TURBINE.asStack(), "PSP", "SAS", "CSC", + 'S', new UnificationEntry(TagPrefix.gear, GTMaterials.StainlessSteel), 'P', CustomTags.EV_CIRCUITS, 'A', GTMachines.HULL[GTValues.EV].asStack(), 'C', - new UnificationEntry(TagPrefix.pipeLargeFluid, GTMaterials.StainlessSteel)); + new UnificationEntry(TagPrefix.pipeLarge, GTMaterials.StainlessSteel)); VanillaRecipeHelper.addShapedRecipe(provider, true, "large_plasma_turbine", GTMultiMachines.LARGE_PLASMA_TURBINE.asStack(), "PSP", "SAS", "CSC", 'S', new UnificationEntry(TagPrefix.gear, GTMaterials.TungstenSteel), 'P', CustomTags.LuV_CIRCUITS, 'A', GTMachines.HULL[GTValues.LuV].asStack(), 'C', - new UnificationEntry(TagPrefix.pipeLargeFluid, GTMaterials.TungstenSteel)); + new UnificationEntry(TagPrefix.pipeLarge, GTMaterials.TungstenSteel)); VanillaRecipeHelper.addShapedRecipe(provider, true, "large_bronze_boiler", GTMultiMachines.LARGE_BOILER_BRONZE.asStack(), "PSP", "SAS", "PSP", 'P', @@ -666,7 +666,7 @@ public static void init(Consumer provider) { VanillaRecipeHelper.addShapedRecipe(provider, true, "large_chemical_reactor", GTMultiMachines.LARGE_CHEMICAL_REACTOR.asStack(), "CRC", "PMP", "CHC", 'C', CustomTags.HV_CIRCUITS, 'R', ChemicalHelper.get(TagPrefix.rotor, GTMaterials.StainlessSteel), 'P', - ChemicalHelper.get(TagPrefix.pipeLargeFluid, GTMaterials.Polytetrafluoroethylene), 'M', + ChemicalHelper.get(TagPrefix.pipeLarge, GTMaterials.Polytetrafluoroethylene), 'M', GTItems.ELECTRIC_MOTOR_HV.asStack(), 'H', GTMachines.HULL[HV].asStack()); VanillaRecipeHelper.addShapedRecipe(provider, true, "power_substation", @@ -708,17 +708,17 @@ public static void init(Consumer provider) { "PCP", "RMR", "EWE", 'M', GTMachines.HULL[GTValues.LV].asStack(), 'E', GTItems.ELECTRIC_MOTOR_LV, 'R', new UnificationEntry(TagPrefix.rotor, GTMaterials.Tin), 'C', CustomTags.LV_CIRCUITS, 'W', new UnificationEntry(TagPrefix.cableGtSingle, GTMaterials.Tin), 'P', - new UnificationEntry(TagPrefix.pipeNormalFluid, GTMaterials.Bronze)); + new UnificationEntry(TagPrefix.pipeNormal, GTMaterials.Bronze)); VanillaRecipeHelper.addShapedRecipe(provider, true, "steam_turbine_mv", GTMachines.STEAM_TURBINE[MV].asStack(), "PCP", "RMR", "EWE", 'M', GTMachines.HULL[GTValues.MV].asStack(), 'E', GTItems.ELECTRIC_MOTOR_MV, 'R', new UnificationEntry(TagPrefix.rotor, GTMaterials.Bronze), 'C', CustomTags.MV_CIRCUITS, 'W', new UnificationEntry(TagPrefix.cableGtSingle, GTMaterials.Copper), 'P', - new UnificationEntry(TagPrefix.pipeNormalFluid, GTMaterials.Steel)); + new UnificationEntry(TagPrefix.pipeNormal, GTMaterials.Steel)); VanillaRecipeHelper.addShapedRecipe(provider, true, "steam_turbine_hv", GTMachines.STEAM_TURBINE[HV].asStack(), "PCP", "RMR", "EWE", 'M', GTMachines.HULL[HV].asStack(), 'E', GTItems.ELECTRIC_MOTOR_HV, 'R', new UnificationEntry(TagPrefix.rotor, GTMaterials.Steel), 'C', CustomTags.HV_CIRCUITS, 'W', new UnificationEntry(TagPrefix.cableGtSingle, GTMaterials.Gold), 'P', - new UnificationEntry(TagPrefix.pipeNormalFluid, GTMaterials.StainlessSteel)); + new UnificationEntry(TagPrefix.pipeNormal, GTMaterials.StainlessSteel)); // TODO Crafting station // VanillaRecipeHelper.addShapedRecipe(provider, true, "workbench_bronze", GTMachines.WORKBENCH.getStackForm(), @@ -727,11 +727,11 @@ public static void init(Consumer provider) { VanillaRecipeHelper.addShapedRecipe(provider, true, "primitive_pump", GTMultiMachines.PRIMITIVE_PUMP.asStack(), "RGS", "OWd", "CLC", 'R', new UnificationEntry(TagPrefix.ring, GTMaterials.Iron), 'G', - new UnificationEntry(TagPrefix.pipeNormalFluid, GTMaterials.Wood), 'S', + new UnificationEntry(TagPrefix.pipeNormal, GTMaterials.Wood), 'S', new UnificationEntry(TagPrefix.screw, GTMaterials.Iron), 'O', new UnificationEntry(TagPrefix.rotor, GTMaterials.Iron), 'W', GTBlocks.TREATED_WOOD_PLANK.asStack(), 'C', new ItemStack(Items.COBBLESTONE_SLAB), 'L', - new UnificationEntry(TagPrefix.pipeLargeFluid, GTMaterials.Wood)); + new UnificationEntry(TagPrefix.pipeLarge, GTMaterials.Wood)); VanillaRecipeHelper.addShapedRecipe(provider, true, "pump_deck", GTBlocks.CASING_PUMP_DECK.asStack(ConfigHolder.INSTANCE.recipes.casingsPerCraft), "SWS", "dCh", 'S', new UnificationEntry(TagPrefix.screw, GTMaterials.Iron), 'W', GTBlocks.TREATED_WOOD_PLANK.asStack(), @@ -739,7 +739,7 @@ public static void init(Consumer provider) { VanillaRecipeHelper.addShapedRecipe(provider, true, "pump_hatch", GTMachines.PUMP_HATCH.asStack(), "SRd", "PLP", "CRC", 'S', new UnificationEntry(TagPrefix.screw, GTMaterials.Iron), 'R', new UnificationEntry(TagPrefix.ring, GTMaterials.Iron), 'P', GTBlocks.TREATED_WOOD_PLANK.asStack(), 'L', - new UnificationEntry(TagPrefix.pipeLargeFluid, GTMaterials.Wood), 'C', + new UnificationEntry(TagPrefix.pipeLarge, GTMaterials.Wood), 'C', new ItemStack(Items.COBBLESTONE_SLAB)); VanillaRecipeHelper.addShapedRecipe(provider, true, "wood_multiblock_tank", @@ -1066,34 +1066,34 @@ public static void init(Consumer provider) { // Hermetic Casings VanillaRecipeHelper.addShapedRecipe(provider, true, "hermetic_casing_lv", GTBlocks.HERMETIC_CASING_LV.asStack(), "PPP", "PFP", "PPP", 'P', new UnificationEntry(TagPrefix.plate, GTMaterials.Steel), 'F', - new UnificationEntry(TagPrefix.pipeLargeFluid, GTMaterials.Polyethylene)); + new UnificationEntry(TagPrefix.pipeLarge, GTMaterials.Polyethylene)); VanillaRecipeHelper.addShapedRecipe(provider, true, "hermetic_casing_mv", GTBlocks.HERMETIC_CASING_MV.asStack(), "PPP", "PFP", "PPP", 'P', new UnificationEntry(TagPrefix.plate, GTMaterials.Aluminium), 'F', - new UnificationEntry(TagPrefix.pipeLargeItem, GTMaterials.PolyvinylChloride)); + new UnificationEntry(TagPrefix.pipeLarge, GTMaterials.PolyvinylChloride)); VanillaRecipeHelper.addShapedRecipe(provider, true, "hermetic_casing_hv", GTBlocks.HERMETIC_CASING_HV.asStack(), "PPP", "PFP", "PPP", 'P', new UnificationEntry(TagPrefix.plate, GTMaterials.StainlessSteel), 'F', - new UnificationEntry(TagPrefix.pipeLargeFluid, GTMaterials.Polytetrafluoroethylene)); + new UnificationEntry(TagPrefix.pipeLarge, GTMaterials.Polytetrafluoroethylene)); VanillaRecipeHelper.addShapedRecipe(provider, true, "hermetic_casing_ev", GTBlocks.HERMETIC_CASING_EV.asStack(), "PPP", "PFP", "PPP", 'P', new UnificationEntry(TagPrefix.plate, GTMaterials.Titanium), 'F', - new UnificationEntry(TagPrefix.pipeLargeFluid, GTMaterials.StainlessSteel)); + new UnificationEntry(TagPrefix.pipeLarge, GTMaterials.StainlessSteel)); VanillaRecipeHelper.addShapedRecipe(provider, true, "hermetic_casing_iv", GTBlocks.HERMETIC_CASING_IV.asStack(), "PPP", "PFP", "PPP", 'P', new UnificationEntry(TagPrefix.plate, GTMaterials.TungstenSteel), 'F', - new UnificationEntry(TagPrefix.pipeLargeFluid, GTMaterials.Titanium)); + new UnificationEntry(TagPrefix.pipeLarge, GTMaterials.Titanium)); VanillaRecipeHelper.addShapedRecipe(provider, true, "hermetic_casing_luv", GTBlocks.HERMETIC_CASING_LuV.asStack(), "PPP", "PFP", "PPP", 'P', new UnificationEntry(TagPrefix.plate, GTMaterials.RhodiumPlatedPalladium), 'F', - new UnificationEntry(TagPrefix.pipeLargeFluid, GTMaterials.TungstenSteel)); + new UnificationEntry(TagPrefix.pipeLarge, GTMaterials.TungstenSteel)); VanillaRecipeHelper.addShapedRecipe(provider, true, "hermetic_casing_zpm", GTBlocks.HERMETIC_CASING_ZPM.asStack(), "PPP", "PFP", "PPP", 'P', new UnificationEntry(TagPrefix.plate, GTMaterials.NaquadahAlloy), 'F', - new UnificationEntry(TagPrefix.pipeLargeFluid, GTMaterials.NiobiumTitanium)); + new UnificationEntry(TagPrefix.pipeLarge, GTMaterials.NiobiumTitanium)); VanillaRecipeHelper.addShapedRecipe(provider, true, "hermetic_casing_uv", GTBlocks.HERMETIC_CASING_UV.asStack(), "PPP", "PFP", "PPP", 'P', new UnificationEntry(TagPrefix.plate, GTMaterials.Darmstadtium), 'F', - new UnificationEntry(TagPrefix.pipeLargeFluid, GTMaterials.Naquadah)); + new UnificationEntry(TagPrefix.pipeLarge, GTMaterials.Naquadah)); VanillaRecipeHelper.addShapedRecipe(provider, true, "hermetic_casing_max", GTBlocks.HERMETIC_CASING_UHV.asStack(), "PPP", "PFP", "PPP", 'P', new UnificationEntry(TagPrefix.plate, GTMaterials.Neutronium), 'F', - new UnificationEntry(TagPrefix.pipeLargeFluid, GTMaterials.Duranium)); + new UnificationEntry(TagPrefix.pipeLarge, GTMaterials.Duranium)); // Super / Quantum Chests VanillaRecipeHelper.addShapedRecipe(provider, true, "super_chest_lv", GTMachines.SUPER_CHEST[LV].asStack(), diff --git a/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/MetaTileEntityMachineRecipeLoader.java b/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/MetaTileEntityMachineRecipeLoader.java index 6fd47bc3e4..86e92682c4 100644 --- a/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/MetaTileEntityMachineRecipeLoader.java +++ b/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/MetaTileEntityMachineRecipeLoader.java @@ -510,7 +510,7 @@ public static void init(Consumer provider) { // Long Distance Pipes ASSEMBLER_RECIPES.recipeBuilder("long_distance_item_endpoint") - .inputItems(pipeLargeItem, Tin, 2) + .inputItems(pipeLarge, Tin, 2) .inputItems(plate, Steel, 8) .inputItems(gear, Steel, 2) .circuitMeta(1) @@ -520,7 +520,7 @@ public static void init(Consumer provider) { .save(provider); ASSEMBLER_RECIPES.recipeBuilder("long_distance_fluid_endpoint") - .inputItems(pipeLargeFluid, Bronze, 2) + .inputItems(pipeLarge, Bronze, 2) .inputItems(plate, Steel, 8) .inputItems(gear, Steel, 2) .circuitMeta(1) @@ -530,7 +530,7 @@ public static void init(Consumer provider) { .save(provider); ASSEMBLER_RECIPES.recipeBuilder("long_distance_item_pipe") - .inputItems(pipeLargeItem, Tin, 2) + .inputItems(pipeLarge, Tin, 2) .inputItems(plate, Steel, 8) .circuitMeta(2) .inputFluids(SolderingAlloy.getFluid(L / 2)) @@ -539,7 +539,7 @@ public static void init(Consumer provider) { .save(provider); ASSEMBLER_RECIPES.recipeBuilder("long_distance_fluid_pipe") - .inputItems(pipeLargeFluid, Bronze, 2) + .inputItems(pipeLarge, Bronze, 2) .inputItems(plate, Steel, 8) .circuitMeta(2) .inputFluids(SolderingAlloy.getFluid(L / 2)) diff --git a/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/MiscRecipeLoader.java b/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/MiscRecipeLoader.java index b21dc2d9b0..dd3ce57db4 100644 --- a/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/MiscRecipeLoader.java +++ b/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/MiscRecipeLoader.java @@ -184,8 +184,8 @@ public static void init(Consumer provider) { .save(provider); ROCK_BREAKER_RECIPES.recipeBuilder("red_granite") - .notConsumable(rock, GraniteRed) - .outputItems(rock, GraniteRed) + .notConsumable(rock, RedGranite) + .outputItems(rock, RedGranite) .duration(16) .EUt(VHA[EV]) .addData("fluidA", "minecraft:lava") diff --git a/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/WoodMachineRecipes.java b/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/WoodMachineRecipes.java index 31bb3e2dab..cf9f007478 100644 --- a/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/WoodMachineRecipes.java +++ b/src/main/java/com/gregtechceu/gtceu/data/recipe/misc/WoodMachineRecipes.java @@ -1069,7 +1069,7 @@ private static void registerPyrolyseOvenRecipes(Consumer provide PYROLYSE_RECIPES.recipeBuilder("log_to_heavy_oil").circuitMeta(3) .inputItems(ItemTags.LOGS_THAT_BURN, 16) .outputItems(dust, Ash, 4) - .outputFluids(OilHeavy.getFluid(200)) + .outputFluids(HeavyOil.getFluid(200)) .duration(320).EUt(192) .save(provider); diff --git a/src/main/java/com/gregtechceu/gtceu/data/recipe/serialized/chemistry/PetrochemRecipes.java b/src/main/java/com/gregtechceu/gtceu/data/recipe/serialized/chemistry/PetrochemRecipes.java index 6999945921..2c2fa0853d 100644 --- a/src/main/java/com/gregtechceu/gtceu/data/recipe/serialized/chemistry/PetrochemRecipes.java +++ b/src/main/java/com/gregtechceu/gtceu/data/recipe/serialized/chemistry/PetrochemRecipes.java @@ -40,7 +40,7 @@ public static void init(Consumer provider) { .duration(20).EUt(96).save(provider); DISTILLATION_RECIPES.recipeBuilder("distill_light_oil") - .inputFluids(OilLight.getFluid(150)) + .inputFluids(LightOil.getFluid(150)) .outputFluids(SulfuricHeavyFuel.getFluid(10)) .outputFluids(SulfuricLightFuel.getFluid(20)) .outputFluids(SulfuricNaphtha.getFluid(30)) @@ -48,7 +48,7 @@ public static void init(Consumer provider) { .duration(20).EUt(96).save(provider); DISTILLATION_RECIPES.recipeBuilder("distill_heavy_oil") - .inputFluids(OilHeavy.getFluid(100)) + .inputFluids(HeavyOil.getFluid(100)) .outputFluids(SulfuricHeavyFuel.getFluid(250)) .outputFluids(SulfuricLightFuel.getFluid(45)) .outputFluids(SulfuricNaphtha.getFluid(15)) diff --git a/src/main/java/com/gregtechceu/gtceu/data/recipe/serialized/chemistry/SeparationRecipes.java b/src/main/java/com/gregtechceu/gtceu/data/recipe/serialized/chemistry/SeparationRecipes.java index d9a22a273d..4ae00ecf1c 100644 --- a/src/main/java/com/gregtechceu/gtceu/data/recipe/serialized/chemistry/SeparationRecipes.java +++ b/src/main/java/com/gregtechceu/gtceu/data/recipe/serialized/chemistry/SeparationRecipes.java @@ -56,7 +56,7 @@ public static void init(Consumer provider) { CENTRIFUGE_RECIPES.recipeBuilder("oilsands_dust_separation") .inputItems(dust, Oilsands) .chancedOutput(new ItemStack(Blocks.SAND), 5000, 5000) - .outputFluids(OilHeavy.getFluid(2000)) + .outputFluids(HeavyOil.getFluid(2000)) .duration(200).EUt(30).save(provider); CENTRIFUGE_RECIPES.recipeBuilder("nether_wart_separation").duration(144).EUt(5) diff --git a/src/main/java/com/gregtechceu/gtceu/data/tags/BlockTagLoader.java b/src/main/java/com/gregtechceu/gtceu/data/tags/BlockTagLoader.java index 489d6267ba..a2fd89614e 100644 --- a/src/main/java/com/gregtechceu/gtceu/data/tags/BlockTagLoader.java +++ b/src/main/java/com/gregtechceu/gtceu/data/tags/BlockTagLoader.java @@ -35,8 +35,8 @@ public static void init(RegistrateTagsProvider provider) { create(provider, BlockTags.REPLACEABLE, GTMaterials.Oil.getFluid().defaultFluidState().createLegacyBlock().getBlock(), - GTMaterials.OilLight.getFluid().defaultFluidState().createLegacyBlock().getBlock(), - GTMaterials.OilHeavy.getFluid().defaultFluidState().createLegacyBlock().getBlock(), + GTMaterials.LightOil.getFluid().defaultFluidState().createLegacyBlock().getBlock(), + GTMaterials.HeavyOil.getFluid().defaultFluidState().createLegacyBlock().getBlock(), GTMaterials.RawOil.getFluid().defaultFluidState().createLegacyBlock().getBlock(), GTMaterials.NaturalGas.getFluid().defaultFluidState().createLegacyBlock().getBlock()); diff --git a/src/main/java/com/gregtechceu/gtceu/forge/ForgeCommonEventListener.java b/src/main/java/com/gregtechceu/gtceu/forge/ForgeCommonEventListener.java index 5dfc4b7289..a5a87dd39c 100644 --- a/src/main/java/com/gregtechceu/gtceu/forge/ForgeCommonEventListener.java +++ b/src/main/java/com/gregtechceu/gtceu/forge/ForgeCommonEventListener.java @@ -12,6 +12,7 @@ import com.gregtechceu.gtceu.api.data.chemical.material.Material; import com.gregtechceu.gtceu.api.data.chemical.material.properties.HazardProperty; import com.gregtechceu.gtceu.api.data.chemical.material.properties.PropertyKey; +import com.gregtechceu.gtceu.api.data.chemical.material.registry.MaterialRegistry; import com.gregtechceu.gtceu.api.data.medicalcondition.MedicalCondition; import com.gregtechceu.gtceu.api.data.tag.TagPrefix; import com.gregtechceu.gtceu.api.item.DrumMachineItem; @@ -32,6 +33,7 @@ import com.gregtechceu.gtceu.common.commands.MedicalConditionCommands; import com.gregtechceu.gtceu.common.data.*; import com.gregtechceu.gtceu.common.data.machines.GTAEMachines; +import com.gregtechceu.gtceu.common.datafixer.fixes.OilVariantsRenameFix; import com.gregtechceu.gtceu.common.fluid.potion.PotionFluidHelper; import com.gregtechceu.gtceu.common.item.ToggleEnergyConsumerBehavior; import com.gregtechceu.gtceu.common.item.armor.IJetpack; @@ -49,6 +51,7 @@ import com.gregtechceu.gtceu.integration.map.ClientCacheManager; import com.gregtechceu.gtceu.integration.map.WaypointManager; import com.gregtechceu.gtceu.integration.map.cache.server.ServerCache; +import com.gregtechceu.gtceu.utils.FormattingUtil; import com.gregtechceu.gtceu.utils.TaskHandler; import net.minecraft.core.Direction; @@ -96,18 +99,19 @@ import net.minecraftforge.fml.LogicalSide; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.items.IItemHandler; +import net.minecraftforge.registries.IForgeRegistry; import net.minecraftforge.registries.MissingMappingsEvent; +import net.minecraftforge.registries.RegistryManager; import com.tterrag.registrate.util.entry.BlockEntry; import com.tterrag.registrate.util.entry.ItemEntry; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; -import static com.gregtechceu.gtceu.utils.FormattingUtil.toLowerCaseUnder; - /** * @author KilaBash * @date 2022/8/27 @@ -574,9 +578,61 @@ public static void remapIds(MissingMappingsEvent event) { } }); + // remap pipe blocks (uses the datafixer in 1.21) + for (MaterialRegistry registry : GTCEuAPI.materialManager.getRegistries()) { + String namespace = registry.getModid(); + event.getMappings(Registries.BLOCK, namespace).forEach(mapping -> { + remapPipe(mapping); + }); + event.getMappings(Registries.ITEM, namespace).forEach(mapping -> { + remapPipe(mapping); + }); + } + event.getMappings(Registries.BLOCK, GTCEu.MOD_ID).forEach(mapping -> { + switch (mapping.getKey().getPath()) { + case "normal_laser_pipe" -> mapping.remap(GTBlocks.LASER_PIPE.get()); + case "normal_optical_pipe" -> mapping.remap(GTBlocks.OPTICAL_PIPE.get()); + default -> { + remapMaterial(Pattern.compile("uranium"), "uranium_238", mapping); + remapMaterial(Pattern.compile("plutonium"), "plutonium_239", mapping); + remapMaterial(Pattern.compile("granite_red"), "red_granite", mapping); + remapMaterial(OilVariantsRenameFix.RENAMED_BLOCK_IDS, mapping); + } + } + }); + event.getMappings(Registries.ITEM, GTCEu.MOD_ID).forEach(mapping -> { + switch (mapping.getKey().getPath()) { + case "normal_laser_pipe" -> mapping.remap(GTBlocks.LASER_PIPE.asItem()); + case "normal_optical_pipe" -> mapping.remap(GTBlocks.OPTICAL_PIPE.asItem()); + case "avanced_nanomuscle_chestplate" -> mapping.remap(GTItems.NANO_CHESTPLATE_ADVANCED.get()); + default -> { + remapMaterial(Pattern.compile("uranium"), "uranium_238", mapping); + remapMaterial(Pattern.compile("plutonium"), "plutonium_239", mapping); + remapMaterial(Pattern.compile("granite_red"), "red_granite", mapping); + remapMaterial(OilVariantsRenameFix.RENAMED_ITEM_IDS, mapping); + } + } + }); + event.getMappings(Registries.FLUID, GTCEu.MOD_ID).forEach(mapping -> { + remapMaterial(OilVariantsRenameFix.RENAMED_FLUID_IDS, mapping); + remapMaterial(Map.of( + "gtceu:uranium", "gtceu:uranium_238", + "gtceu:flowing_uranium", "gtceu:flowing_uranium_238", + "gtceu:plutonium", "gtceu:plutonium_238", + "gtceu:flowing_plutonium", "gtceu:flowing_plutonium_238"), mapping); + }); + event.getMappings(Registries.BLOCK_ENTITY_TYPE, GTCEu.MOD_ID).forEach(mapping -> { + switch (mapping.getKey().getPath()) { + case "cable", "fluid_pipe", "item_pipe" -> mapping.remap(GTBlockEntities.MATERIAL_PIPE.get()); + case "laser_pipe", "optical_pipe" -> mapping.remap(GTBlockEntities.ACTIVATABLE_PIPE.get()); + case "duct_pipe" -> mapping.remap(GTBlockEntities.PIPE.get()); + } + }); + + // remap old material items for (TagPrefix prefix : TagPrefix.values()) { - String first = prefix.invertedName ? toLowerCaseUnder(prefix.name) : "(.+?)"; - String last = prefix.invertedName ? "(.+?)" : toLowerCaseUnder(prefix.name); + String first = prefix.invertedName ? FormattingUtil.toLowerCaseUnder(prefix.name) : "(.+?)"; + String last = prefix.invertedName ? "(.+?)" : FormattingUtil.toLowerCaseUnder(prefix.name); Pattern idPattern = Pattern.compile(first + "_" + last); event.getMappings(Registries.BLOCK, GTCEu.MOD_ID).forEach(mapping -> { Matcher matcher = idPattern.matcher(mapping.getKey().getPath()); @@ -606,4 +662,53 @@ public static void remapIds(MissingMappingsEvent event) { }); } } + + private static void remapPipe(MissingMappingsEvent.Mapping mapping) { + String namespace = mapping.getKey().getNamespace(); + String path = mapping.getKey().getPath(); + if (path.contains("_pipe")) { + StringBuilder result; + boolean restrictive = path.contains("restrictive"); + boolean fluid = path.contains("fluid"); + String[] split = path.substring(0, path.length() - (fluid ? 11 : 10)).split("_"); + if (restrictive) { + result = new StringBuilder(split[split.length - 2] + "_" + split[split.length - 1]); + for (int i = 0; i < split.length - 2; ++i) { + result.append("_").append(split[i]); + } + } else { + result = new StringBuilder(split[split.length - 1]); + for (int i = 0; i < split.length - 1; ++i) { + result.append("_").append(split[i]); + } + } + result.append("_pipe"); + + ResourceLocation newBlock = new ResourceLocation(namespace, result.toString()); + IForgeRegistry registry = RegistryManager.ACTIVE.getRegistry(mapping.getRegistry().getRegistryKey()); + mapping.remap(registry.getValue(newBlock)); + } + } + + private static void remapMaterial(Pattern pattern, String replacement, + MissingMappingsEvent.Mapping mapping) { + String namespace = mapping.getKey().getNamespace(); + String path = mapping.getKey().getPath(); + Matcher match = pattern.matcher(path); + String replaced = match.replaceAll(replacement); + if (!path.equals(replaced)) { + ResourceLocation newBlock = new ResourceLocation(namespace, replaced); + IForgeRegistry registry = RegistryManager.ACTIVE.getRegistry(mapping.getRegistry().getRegistryKey()); + mapping.remap(registry.getValue(newBlock)); + } + } + + private static void remapMaterial(Map renameMap, MissingMappingsEvent.Mapping mapping) { + String newName = renameMap.get(mapping.getKey().toString()); + if (newName != null) { + ResourceLocation newBlock = new ResourceLocation(newName); + IForgeRegistry registry = RegistryManager.ACTIVE.getRegistry(mapping.getRegistry().getRegistryKey()); + mapping.remap(registry.getValue(newBlock)); + } + } } diff --git a/src/main/java/com/gregtechceu/gtceu/integration/jade/GTJadePlugin.java b/src/main/java/com/gregtechceu/gtceu/integration/jade/GTJadePlugin.java index 269313b31d..945e313584 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/jade/GTJadePlugin.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/jade/GTJadePlugin.java @@ -1,8 +1,6 @@ package com.gregtechceu.gtceu.integration.jade; import com.gregtechceu.gtceu.GTCEu; -import com.gregtechceu.gtceu.api.blockentity.MetaMachineBlockEntity; -import com.gregtechceu.gtceu.common.blockentity.FluidPipeBlockEntity; import com.gregtechceu.gtceu.common.data.GTMaterialItems; import com.gregtechceu.gtceu.integration.jade.provider.*; @@ -42,14 +40,11 @@ public void register(IWailaCommonRegistration registration) { registration.registerBlockDataProvider(new HazardCleanerBlockProvider(), BlockEntity.class); registration.registerBlockDataProvider(new TransformerBlockProvider(), BlockEntity.class); registration.registerBlockDataProvider(new PrimitivePumpBlockProvider(), BlockEntity.class); + registration.registerBlockDataProvider(new PipeProvider(), BlockEntity.class); if (GTCEu.Mods.isAE2Loaded()) { registration.registerBlockDataProvider(new MEPatternBufferProxyProvider(), BlockEntity.class); registration.registerBlockDataProvider(new MEPatternBufferProvider(), BlockEntity.class); } - - registration.registerItemStorage(GTItemStorageProvider.INSTANCE, MetaMachineBlockEntity.class); - registration.registerFluidStorage(GTFluidStorageProvider.INSTANCE, MetaMachineBlockEntity.class); - registration.registerFluidStorage(FluidPipeStorageProvider.INSTANCE, FluidPipeBlockEntity.class); } @Override @@ -71,14 +66,11 @@ public void registerClient(IWailaClientRegistration registration) { registration.registerBlockComponent(new HazardCleanerBlockProvider(), Block.class); registration.registerBlockComponent(new TransformerBlockProvider(), Block.class); registration.registerBlockComponent(new PrimitivePumpBlockProvider(), Block.class); + registration.registerBlockComponent(new PipeProvider(), Block.class); if (GTCEu.Mods.isAE2Loaded()) { registration.registerBlockComponent(new MEPatternBufferProxyProvider(), Block.class); registration.registerBlockComponent(new MEPatternBufferProvider(), Block.class); } - - registration.registerItemStorageClient(GTItemStorageProvider.INSTANCE); - registration.registerFluidStorageClient(GTFluidStorageProvider.INSTANCE); - registration.registerFluidStorageClient(FluidPipeStorageProvider.INSTANCE); } static { diff --git a/src/main/java/com/gregtechceu/gtceu/integration/jade/element/FluidStackElement.java b/src/main/java/com/gregtechceu/gtceu/integration/jade/element/FluidStackElement.java new file mode 100644 index 0000000000..99260d0be2 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/integration/jade/element/FluidStackElement.java @@ -0,0 +1,51 @@ +package com.gregtechceu.gtceu.integration.jade.element; + +import com.lowdragmc.lowdraglib.gui.util.DrawerHelper; +import com.lowdragmc.lowdraglib.gui.util.TextFormattingUtil; +import com.lowdragmc.lowdraglib.side.fluid.FluidStack; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.world.phys.Vec2; + +import com.mojang.blaze3d.systems.RenderSystem; +import snownee.jade.api.ui.Element; + +public class FluidStackElement extends Element { + + private final FluidStack fluidStack; + private float width, height; + + public FluidStackElement(FluidStack fluidStack, float width, float height) { + this.fluidStack = fluidStack; + this.width = width; + this.height = height; + } + + @Override + public Vec2 getSize() { + return new Vec2(width, height); + } + + @Override + public void render(GuiGraphics guiGraphics, float x, float y, float width, float height) { + RenderSystem.disableBlend(); + if (!fluidStack.isEmpty()) { + x += 2; + y += 2; + DrawerHelper.drawFluidForGui(guiGraphics, fluidStack, fluidStack.getAmount(), + (int) x, (int) y, (int) width, (int) height); + + guiGraphics.pose().pushPose(); + guiGraphics.pose().scale(0.5F, 0.5F, 1); + String s = TextFormattingUtil.formatLongToCompactStringBuckets(fluidStack.getAmount(), 3) + "B"; + Font fontRenderer = Minecraft.getInstance().font; + guiGraphics.drawString(fontRenderer, s, (x + (width / 3f)) * 2 - fontRenderer.width(s) + 21, + (y + (height / 3f) + 6) * 2, 0xFFFFFF, true); + guiGraphics.pose().popPose(); + } + RenderSystem.enableBlend(); + RenderSystem.setShaderColor(1, 1, 1, 1); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/integration/jade/provider/CableBlockProvider.java b/src/main/java/com/gregtechceu/gtceu/integration/jade/provider/CableBlockProvider.java index b91c909ee9..017e818ada 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/jade/provider/CableBlockProvider.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/jade/provider/CableBlockProvider.java @@ -2,14 +2,19 @@ import com.gregtechceu.gtceu.GTCEu; import com.gregtechceu.gtceu.api.GTValues; -import com.gregtechceu.gtceu.common.block.CableBlock; -import com.gregtechceu.gtceu.common.blockentity.CableBlockEntity; +import com.gregtechceu.gtceu.api.data.chemical.material.properties.PropertyKey; +import com.gregtechceu.gtceu.api.graphnet.pipenet.WorldPipeNode; +import com.gregtechceu.gtceu.common.pipelike.block.cable.CableBlock; +import com.gregtechceu.gtceu.common.pipelike.handlers.properties.MaterialEnergyProperties; +import com.gregtechceu.gtceu.common.pipelike.net.energy.EnergyFlowLogic; +import com.gregtechceu.gtceu.common.pipelike.net.energy.WorldEnergyNet; import com.gregtechceu.gtceu.utils.GTUtil; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.Tag; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.block.entity.BlockEntity; import snownee.jade.api.BlockAccessor; @@ -51,16 +56,29 @@ public void appendTooltip(ITooltip iTooltip, BlockAccessor blockAccessor, IPlugi public void appendServerData(CompoundTag compoundTag, BlockAccessor blockAccessor) { CompoundTag data = compoundTag.getCompound(getUid().toString()); if (blockAccessor.getBlock() instanceof CableBlock cableBlock) { - CableBlockEntity cable = (CableBlockEntity) cableBlock.getPipeTile(blockAccessor.getLevel(), - blockAccessor.getPosition()); - if (cable != null) { - var cableData = new CompoundTag(); - cableData.putLong("maxVoltage", cable.getMaxVoltage()); - cableData.putLong("currentVoltage", cable.getCurrentMaxVoltage()); - cableData.putDouble("maxAmperage", cable.getMaxAmperage()); - cableData.putDouble("currentAmperage", cable.getAverageAmperage()); - data.put("cableData", cableData); + if (!(blockAccessor.getLevel() instanceof ServerLevel serverLevel)) { + compoundTag.put(getUid().toString(), data); + return; } + WorldPipeNode node = WorldEnergyNet.getWorldNet(serverLevel).getNode(blockAccessor.getPosition()); + EnergyFlowLogic logic = node.getData().getLogicEntryDefaultable(EnergyFlowLogic.TYPE); + + long currentTick = serverLevel.getServer().getTickCount(); + long totalVoltage = 0L; + double averageAmperage = logic.getAverageAmperage(currentTick); + for (var flow : logic.getFlow(currentTick)) { + totalVoltage += flow.voltage(); + } + + var cableData = new CompoundTag(); + cableData.putLong("maxVoltage", cableBlock.material.getProperty(PropertyKey.PIPENET_PROPERTIES) + .getProperty(MaterialEnergyProperties.KEY).getVoltageLimit()); + cableData.putLong("currentVoltage", totalVoltage); + cableData.putDouble("maxAmperage", cableBlock.material.getProperty(PropertyKey.PIPENET_PROPERTIES) + .getProperty(MaterialEnergyProperties.KEY).getAmperage(cableBlock.getStructure())); + cableData.putDouble("currentAmperage", averageAmperage); + + data.put("cableData", cableData); } compoundTag.put(getUid().toString(), data); } diff --git a/src/main/java/com/gregtechceu/gtceu/integration/jade/provider/FluidPipeStorageProvider.java b/src/main/java/com/gregtechceu/gtceu/integration/jade/provider/FluidPipeStorageProvider.java deleted file mode 100644 index 3c9f4a6ad1..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/integration/jade/provider/FluidPipeStorageProvider.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.gregtechceu.gtceu.integration.jade.provider; - -import com.gregtechceu.gtceu.GTCEu; -import com.gregtechceu.gtceu.common.blockentity.FluidPipeBlockEntity; - -import net.minecraft.nbt.CompoundTag; -import net.minecraft.network.chat.Component; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; - -import org.jetbrains.annotations.Nullable; -import snownee.jade.api.Accessor; -import snownee.jade.api.fluid.JadeFluidObject; -import snownee.jade.api.view.*; - -import java.util.ArrayList; -import java.util.List; - -public enum FluidPipeStorageProvider implements IServerExtensionProvider, - IClientExtensionProvider { - - INSTANCE; - - @Override - public List> getClientGroups(Accessor accessor, List> groups) { - return ClientViewGroup.map(groups, FluidView::readDefault, (group, clientGroup) -> { - if (group.id != null) { - clientGroup.title = Component.literal(group.id); - } - clientGroup.bgColor = 0x55666666; - }); - } - - @Override - public @Nullable List> getGroups(ServerPlayer serverPlayer, ServerLevel serverLevel, - FluidPipeBlockEntity pipe, boolean showDetails) { - List> tanks = new ArrayList<>(); - for (var tank : pipe.getFluidTanks()) { - if (tank.getFluidAmount() > 0) { - tanks.add(new ViewGroup<>(List.of(FluidView.writeDefault( - JadeFluidObject.of(tank.getFluid().getFluid(), tank.getFluidAmount()), tank.getCapacity())))); - } - } - return tanks; - } - - @Override - public ResourceLocation getUid() { - return GTCEu.id("fluid_storage"); - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/integration/jade/provider/PipeProvider.java b/src/main/java/com/gregtechceu/gtceu/integration/jade/provider/PipeProvider.java new file mode 100644 index 0000000000..6cd21281d8 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/integration/jade/provider/PipeProvider.java @@ -0,0 +1,214 @@ +package com.gregtechceu.gtceu.integration.jade.provider; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.graphnet.logic.NetLogicData; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.block.PipeBlock; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; +import com.gregtechceu.gtceu.api.graphnet.predicate.test.FluidTestObject; +import com.gregtechceu.gtceu.api.graphnet.predicate.test.ItemTestObject; +import com.gregtechceu.gtceu.common.pipelike.net.energy.EnergyFlowData; +import com.gregtechceu.gtceu.common.pipelike.net.energy.EnergyFlowLogic; +import com.gregtechceu.gtceu.common.pipelike.net.fluid.FluidFlowLogic; +import com.gregtechceu.gtceu.common.pipelike.net.item.ItemFlowLogic; +import com.gregtechceu.gtceu.integration.jade.element.FluidStackElement; + +import com.lowdragmc.lowdraglib.side.fluid.FluidStack; + +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; + +import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; +import snownee.jade.api.*; +import snownee.jade.api.config.IPluginConfig; +import snownee.jade.api.ui.IElement; +import snownee.jade.api.ui.IElementHelper; + +public class PipeProvider extends BlockInfoProvider { + + public PipeProvider() { + super(GTCEu.id("pipe")); + } + + @Override + protected PipeBlockEntity getCapability(Level level, BlockPos pos) { + if (level.getBlockEntity(pos) instanceof PipeBlockEntity pipeBlockEntity) { + return pipeBlockEntity; + } + return null; + } + + @Override + protected void write(CompoundTag compoundTag, PipeBlockEntity capability, BlockAccessor block) { + if (capability != null) { + for (NetLogicData data : capability.getNetLogicDatas().values()) { + EnergyFlowLogic energy = data.getLogicEntryNullable(EnergyFlowLogic.TYPE); + if (energy != null) { + addEnergyFlowInformation(compoundTag, energy); + } + FluidFlowLogic fluid = data.getLogicEntryNullable(FluidFlowLogic.TYPE); + if (fluid != null) { + addFluidFlowInformation(compoundTag, fluid); + } + ItemFlowLogic item = data.getLogicEntryNullable(ItemFlowLogic.TYPE); + if (item != null) { + addItemFlowInformation(compoundTag, item); + } + } + } + } + + @Override + protected void addTooltip(CompoundTag capData, ITooltip tooltip, Player player, BlockAccessor block, + BlockEntity blockEntity, IPluginConfig config) { + CompoundTag serverData = block.getServerData(); + if (serverData.contains("Energy")) { + CompoundTag energy = serverData.getCompound("Energy"); + tooltip.add(Component.translatable("gtceu.top.cable_voltage") + .append(Component.literal(String.valueOf(energy.getLong("voltage"))))); + tooltip.add(Component.translatable("gtceu.top.cable_amperage") + .append(Component.literal(String.valueOf(energy.getLong("amperage"))))); + } + if (serverData.contains("Fluid")) { + CompoundTag fluid = serverData.getCompound("Fluid"); + + FluidStack stack = FluidStack.loadFromTag(fluid.getCompound("LastFluid")); + tooltip.add(IElementHelper.get().text(Component.translatable("gtceu.top.pipe.fluid_last")) + .align(IElement.Align.LEFT)); + tooltip.append(new FluidStackElement(stack, 14, 14)); + tooltip.append(stack.getDisplayName()); + + ListTag list = fluid.getList("ExtraFluids", Tag.TAG_COMPOUND); + for (int i = 0; i < list.size(); i++) { + CompoundTag tag = list.getCompound(i); + stack = FluidStack.loadFromTag(tag.getCompound("Fluid")); + tooltip.add(new FluidStackElement(stack, 14, 14)); + tooltip.append(Component.literal(String.valueOf(tag.getLong("Amount"))) + .append(Component.literal(" mB/s ")) + .append(stack.getDisplayName())); + } + } + if (serverData.contains("Item")) { + CompoundTag item = serverData.getCompound("Item"); + + ItemStack stack = ItemStack.of(item.getCompound("LastItem")); + tooltip.add(IElementHelper.get().text(Component.translatable("gtceu.top.pipe.item_last")) + .align(IElement.Align.LEFT)); + tooltip.append(IElementHelper.get().item(stack)); + tooltip.append(stack.getDisplayName()); + + ListTag list = item.getList("ExtraItems", Tag.TAG_COMPOUND); + for (int i = 0; i < list.size(); i++) { + CompoundTag tag = list.getCompound(i); + stack = ItemStack.of(tag.getCompound("Item")); + tooltip.add(IElementHelper.get().item(stack)); + tooltip.append(Component.literal(String.valueOf(tag.getLong("Amount"))) + .append(Component.literal(" /s ")) + .append(stack.getDisplayName())); + } + } + } + + @Override + public void appendServerData(CompoundTag compoundTag, BlockAccessor blockAccessor) { + if (blockAccessor.getBlock() instanceof PipeBlock pipe) { + PipeBlockEntity tile = pipe.getBlockEntity(blockAccessor.getLevel(), blockAccessor.getPosition()); + if (tile != null) { + for (NetLogicData data : tile.getNetLogicDatas().values()) { + EnergyFlowLogic energy = data.getLogicEntryNullable(EnergyFlowLogic.TYPE); + if (energy != null) { + addEnergyFlowInformation(compoundTag, energy); + } + FluidFlowLogic fluid = data.getLogicEntryNullable(FluidFlowLogic.TYPE); + if (fluid != null) { + addFluidFlowInformation(compoundTag, fluid); + } + ItemFlowLogic item = data.getLogicEntryNullable(ItemFlowLogic.TYPE); + if (item != null) { + addItemFlowInformation(compoundTag, item); + } + } + } + } + } + + private void addEnergyFlowInformation(CompoundTag tag, EnergyFlowLogic logic) { + long cumulativeVoltage = 0; + long cumulativeAmperage = 0; + for (var memory : logic.getMemory().values()) { + int count = 0; + double voltage = 0; + long amperage = 0; + for (EnergyFlowData flow : memory) { + count++; + long prev = amperage; + amperage += flow.amperage(); + // weighted average + voltage = voltage * prev / amperage + (double) (flow.voltage() * flow.amperage()) / amperage; + } + if (count != 0) { + cumulativeVoltage += voltage / count; + cumulativeAmperage += amperage / count; + } + } + CompoundTag energy = new CompoundTag(); + energy.putLong("voltage", cumulativeVoltage / EnergyFlowLogic.MEMORY_TICKS); + energy.putLong("amperage", cumulativeAmperage / EnergyFlowLogic.MEMORY_TICKS); + tag.put("Energy", energy); + } + + private void addFluidFlowInformation(CompoundTag tag, FluidFlowLogic logic) { + CompoundTag fluid = new CompoundTag(); + + fluid.put("LastFluid", logic.getLast().writeToNBT(new CompoundTag())); + + Object2LongOpenHashMap counts = new Object2LongOpenHashMap<>(); + for (var memory : logic.getMemory().values()) { + for (var entry : memory.object2LongEntrySet()) { + counts.merge(entry.getKey(), entry.getLongValue(), Long::sum); + } + } + + ListTag extraFluids = new ListTag(); + for (var entry : counts.object2LongEntrySet()) { + CompoundTag inner = new CompoundTag(); + net.minecraftforge.fluids.FluidStack stack = entry.getKey().recombine(); + + inner.put("Fluid", stack.writeToNBT(new CompoundTag())); + inner.putLong("Amount", entry.getLongValue() * 20 / FluidFlowLogic.MEMORY_TICKS); + extraFluids.add(inner); + } + fluid.put("ExtraFluids", extraFluids); + tag.put("Fluid", fluid); + } + + private void addItemFlowInformation(CompoundTag tag, ItemFlowLogic logic) { + CompoundTag item = new CompoundTag(); + item.put("LastItem", logic.getLast().save(new CompoundTag())); + + Object2LongOpenHashMap counts = new Object2LongOpenHashMap<>(); + for (var memory : logic.getMemory().values()) { + for (var entry : memory.object2LongEntrySet()) { + counts.merge(entry.getKey(), entry.getLongValue(), Long::sum); + } + } + + ListTag extraItems = new ListTag(); + for (var entry : counts.object2LongEntrySet()) { + CompoundTag inner = new CompoundTag(); + ItemStack stack = entry.getKey().recombine(); + + inner.put("Item", stack.save(new CompoundTag())); + inner.putLong("Amount", entry.getLongValue() * 20 / FluidFlowLogic.MEMORY_TICKS); + extraItems.add(inner); + } + item.put("ExtraItems", extraItems); + tag.put("Item", item); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/integration/jade/provider/StainedColorProvider.java b/src/main/java/com/gregtechceu/gtceu/integration/jade/provider/StainedColorProvider.java index f1954da828..d002b9af1b 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/jade/provider/StainedColorProvider.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/jade/provider/StainedColorProvider.java @@ -1,9 +1,7 @@ package com.gregtechceu.gtceu.integration.jade.provider; import com.gregtechceu.gtceu.GTCEu; -import com.gregtechceu.gtceu.api.blockentity.PipeBlockEntity; -import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; -import com.gregtechceu.gtceu.api.machine.MetaMachine; +import com.gregtechceu.gtceu.api.blockentity.IPaintable; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; @@ -24,7 +22,8 @@ public class StainedColorProvider implements IBlockComponentProvider, IServerDat public void appendTooltip(ITooltip iTooltip, BlockAccessor blockAccessor, IPluginConfig iPluginConfig) { if (blockAccessor.getServerData().contains("StainedColor")) { int paintingColor = blockAccessor.getServerData().getInt("StainedColor"); - if (paintingColor != -1) { + int defaultColor = blockAccessor.getServerData().getInt("DefaultColor"); + if (paintingColor != defaultColor) { iTooltip.add(Component .translatable("gtceu.top.stained", "#" + Integer.toHexString(paintingColor).toUpperCase(Locale.ROOT)) @@ -35,15 +34,9 @@ public void appendTooltip(ITooltip iTooltip, BlockAccessor blockAccessor, IPlugi @Override public void appendServerData(CompoundTag compoundTag, BlockAccessor blockAccessor) { - if (blockAccessor.getBlockEntity() instanceof IMachineBlockEntity blockEntity) { - MetaMachine metaMachine = blockEntity.getMetaMachine(); - if (metaMachine != null) { - int paintingColor = metaMachine.getPaintingColor(); - compoundTag.putInt("StainedColor", paintingColor); - } - } else if (blockAccessor.getBlockEntity() instanceof PipeBlockEntity pipe) { - int paintingColor = pipe.getPaintingColor(); - compoundTag.putInt("StainedColor", paintingColor); + if (blockAccessor.getBlockEntity() instanceof IPaintable paintable) { + compoundTag.putInt("StainedColor", paintable.getRealColor()); + compoundTag.putInt("DefaultColor", paintable.getDefaultPaintingColor()); } } diff --git a/src/main/java/com/gregtechceu/gtceu/integration/map/ClientCacheManager.java b/src/main/java/com/gregtechceu/gtceu/integration/map/ClientCacheManager.java index d6f8dbd352..33cd710c79 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/map/ClientCacheManager.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/map/ClientCacheManager.java @@ -106,8 +106,8 @@ public static void saveCaches() { CompoundTag data = cache.saveDimFile(dimFilePrefix, dim); if (data == null) continue; File dimFile = new File(cacheInfo.cacheFolder, - dimFilePrefix + filePrefix + dim.location().getNamespace() + "=" + - dim.location().getPath() + fileEnding); + dimFilePrefix + filePrefix + dim.location().getNamespace() + + resourceLocationSeparator + dim.location().getPath() + fileEnding); try { NbtIo.writeCompressed(data, new FileOutputStream(dimFile)); } catch (IOException e) { diff --git a/src/main/java/com/gregtechceu/gtceu/integration/top/TheOneProbePlugin.java b/src/main/java/com/gregtechceu/gtceu/integration/top/TheOneProbePlugin.java index ddfe0a5a77..93812167f0 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/top/TheOneProbePlugin.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/top/TheOneProbePlugin.java @@ -49,12 +49,12 @@ public ResourceLocation getId() { oneProbe.registerProvider(new ExhaustVentInfoProvider()); oneProbe.registerProvider(new SteamBoilerInfoProvider()); oneProbe.registerProvider(new AutoOutputInfoProvider()); - oneProbe.registerProvider(new CableInfoProvider()); oneProbe.registerProvider(new MachineModeProvider()); oneProbe.registerProvider(new StainedColorProvider()); oneProbe.registerProvider(new PrimitivePumpProvider()); oneProbe.registerProvider(new CoverProvider()); oneProbe.registerProvider(new HazardCleanerInfoProvider()); oneProbe.registerProvider(new TransformerInfoProvider()); + oneProbe.registerProvider(new PipeInfoProvider()); } } diff --git a/src/main/java/com/gregtechceu/gtceu/integration/top/provider/CableInfoProvider.java b/src/main/java/com/gregtechceu/gtceu/integration/top/provider/CableInfoProvider.java deleted file mode 100644 index f819034570..0000000000 --- a/src/main/java/com/gregtechceu/gtceu/integration/top/provider/CableInfoProvider.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.gregtechceu.gtceu.integration.top.provider; - -import com.gregtechceu.gtceu.GTCEu; -import com.gregtechceu.gtceu.api.GTValues; -import com.gregtechceu.gtceu.common.block.CableBlock; -import com.gregtechceu.gtceu.common.blockentity.CableBlockEntity; -import com.gregtechceu.gtceu.utils.GTUtil; - -import net.minecraft.network.chat.Component; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.state.BlockState; - -import mcjty.theoneprobe.api.*; - -import static com.gregtechceu.gtceu.utils.FormattingUtil.DECIMAL_FORMAT_1F; - -public class CableInfoProvider implements IProbeInfoProvider { - - @Override - public ResourceLocation getID() { - return GTCEu.id("cable_info"); - } - - @Override - public void addProbeInfo(ProbeMode probeMode, IProbeInfo iProbeInfo, Player player, Level level, - BlockState blockState, IProbeHitData iProbeHitData) { - if (blockState.getBlock() instanceof CableBlock cableBlock) { - CableBlockEntity cable = (CableBlockEntity) cableBlock.getPipeTile(level, iProbeHitData.getPos()); - if (cable != null) { - long voltage = cable.getCurrentMaxVoltage(); - double amperage = cable.getAverageAmperage(); - IProbeInfo horizontalPane = iProbeInfo - .horizontal(iProbeInfo.defaultLayoutStyle().alignment(ElementAlignment.ALIGN_CENTER)); - horizontalPane.text(Component.translatable("gtceu.top.cable_voltage")); - if (voltage != 0) { - horizontalPane.text(GTValues.VNF[GTUtil.getTierByVoltage(voltage)]).text(" / "); - } - horizontalPane.text(GTValues.VNF[GTUtil.getTierByVoltage(cable.getMaxVoltage())]); - - horizontalPane = iProbeInfo - .horizontal(iProbeInfo.defaultLayoutStyle().alignment(ElementAlignment.ALIGN_CENTER)); - horizontalPane.text(Component.translatable("gtceu.top.cable_amperage")); - if (amperage != 0) { - horizontalPane.text(DECIMAL_FORMAT_1F.format(cable.getAverageAmperage()) + "A / "); - } - horizontalPane.text(DECIMAL_FORMAT_1F.format(cable.getMaxAmperage()) + "A"); - } - } - } -} diff --git a/src/main/java/com/gregtechceu/gtceu/integration/top/provider/PipeInfoProvider.java b/src/main/java/com/gregtechceu/gtceu/integration/top/provider/PipeInfoProvider.java new file mode 100644 index 0000000000..480c1adcc3 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/integration/top/provider/PipeInfoProvider.java @@ -0,0 +1,151 @@ +package com.gregtechceu.gtceu.integration.top.provider; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.api.GTValues; +import com.gregtechceu.gtceu.api.data.chemical.material.properties.PropertyKey; +import com.gregtechceu.gtceu.api.graphnet.logic.NetLogicData; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.block.PipeBlock; +import com.gregtechceu.gtceu.api.graphnet.pipenet.physical.blockentity.PipeBlockEntity; +import com.gregtechceu.gtceu.api.graphnet.predicate.test.FluidTestObject; +import com.gregtechceu.gtceu.api.graphnet.predicate.test.ItemTestObject; +import com.gregtechceu.gtceu.common.pipelike.block.cable.CableBlock; +import com.gregtechceu.gtceu.common.pipelike.handlers.properties.MaterialEnergyProperties; +import com.gregtechceu.gtceu.common.pipelike.net.energy.EnergyFlowData; +import com.gregtechceu.gtceu.common.pipelike.net.energy.EnergyFlowLogic; +import com.gregtechceu.gtceu.common.pipelike.net.fluid.FluidFlowLogic; +import com.gregtechceu.gtceu.common.pipelike.net.item.ItemFlowLogic; +import com.gregtechceu.gtceu.integration.top.element.FluidStackElement; +import com.gregtechceu.gtceu.integration.top.element.FluidStyle; +import com.gregtechceu.gtceu.utils.FormattingUtil; +import com.gregtechceu.gtceu.utils.GTUtil; + +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; + +import it.unimi.dsi.fastutil.objects.Object2LongMap; +import mcjty.theoneprobe.api.*; + +import static com.gregtechceu.gtceu.utils.FormattingUtil.DECIMAL_FORMAT_1F; + +public class PipeInfoProvider implements IProbeInfoProvider { + + @Override + public ResourceLocation getID() { + return GTCEu.id("pipe"); + } + + @Override + public void addProbeInfo(ProbeMode mode, IProbeInfo info, Player player, Level level, + BlockState state, IProbeHitData hitData) { + if (state.getBlock() instanceof PipeBlock pipe) { + PipeBlockEntity tile = pipe.getBlockEntity(level, hitData.getPos()); + if (tile != null) { + for (NetLogicData data : tile.getNetLogicDatas().values()) { + EnergyFlowLogic energy = data.getLogicEntryNullable(EnergyFlowLogic.TYPE); + if (energy != null) { + addEnergyFlowInformation(mode, info, player, state, hitData, energy); + } + FluidFlowLogic fluid = data.getLogicEntryNullable(FluidFlowLogic.TYPE); + if (fluid != null) { + addFluidFlowInformation(mode, info, player, hitData, fluid); + } + ItemFlowLogic item = data.getLogicEntryNullable(ItemFlowLogic.TYPE); + if (item != null) { + addItemFlowInformation(mode, info, player, hitData, item); + } + } + } + } + } + + private void addEnergyFlowInformation(ProbeMode probeMode, IProbeInfo iProbeInfo, + Player entityPlayer, BlockState state, + IProbeHitData iProbeHitData, EnergyFlowLogic logic) { + long cumulativeVoltage = 0; + long cumulativeAmperage = 0; + for (var memory : logic.getMemory().values()) { + int count = 0; + double voltage = 0; + long amperage = 0; + for (EnergyFlowData flow : memory) { + count++; + long prev = amperage; + amperage += flow.amperage(); + // weighted average + voltage = voltage * prev / amperage + (double) (flow.voltage() * flow.amperage()) / amperage; + } + if (count != 0) { + cumulativeVoltage += voltage / count; + cumulativeAmperage += amperage / count; + } + } + + if (state.getBlock() instanceof CableBlock cableBlock) { + + iProbeInfo.text(CompoundText.create().info(Component.translatable("gtceu.top.cable_voltage")) + .important(Component.literal(String.valueOf(cumulativeVoltage / EnergyFlowLogic.MEMORY_TICKS))) + .text(" / ") + .important(Component.literal(GTValues.VNF[GTUtil.getTierByVoltage( + cableBlock.material.getProperty(PropertyKey.PIPENET_PROPERTIES) + .getProperty(MaterialEnergyProperties.KEY).getVoltageLimit())]))); + iProbeInfo.text(CompoundText.create().info(Component.translatable("gtceu.top.cable_amperage")) + .important(Component.literal(String.valueOf(cumulativeAmperage / EnergyFlowLogic.MEMORY_TICKS))) + .text(" / ") + .important(Component.literal(DECIMAL_FORMAT_1F.format( + cableBlock.material.getProperty(PropertyKey.PIPENET_PROPERTIES) + .getProperty(MaterialEnergyProperties.KEY).getAmperage(cableBlock.getStructure())) + + "A"))); + } else { + iProbeInfo.text(CompoundText.create().info(Component.translatable("gtceu.top.cable_voltage")) + .important(Component.literal(String.valueOf(cumulativeVoltage / EnergyFlowLogic.MEMORY_TICKS)))); + iProbeInfo.text(CompoundText.create().info(Component.translatable("gtceu.top.cable_amperage")) + .important(Component.literal(String.valueOf(cumulativeAmperage / EnergyFlowLogic.MEMORY_TICKS)))); + } + } + + private void addFluidFlowInformation(ProbeMode probeMode, IProbeInfo iProbeInfo, Player entityPlayer, + IProbeHitData iProbeHitData, FluidFlowLogic logic) { + if (logic.getMemory().isEmpty()) { + iProbeInfo.horizontal(iProbeInfo.defaultLayoutStyle().alignment(ElementAlignment.ALIGN_CENTER)) + .text(CompoundText.create().info(Component.translatable("gtceu.top.pipe.fluid_last"))) + .element(new FluidStackElement(logic.getLast(), new FluidStyle())) + .text(logic.getLast().getDisplayName()); + } + + Object2LongMap counts = logic.getSum(); + + for (var entry : counts.object2LongEntrySet()) { + net.minecraftforge.fluids.FluidStack stack = entry.getKey().recombine(); + String value = FormattingUtil.formatNumbers(20 * entry.getLongValue() / FluidFlowLogic.MEMORY_TICKS); + iProbeInfo.horizontal(iProbeInfo.defaultLayoutStyle().alignment(ElementAlignment.ALIGN_CENTER)) + .element(new FluidStackElement(stack, new FluidStyle())) + .text(" §b" + value + " L/s §f" + stack.getDisplayName()); + } + } + + private void addItemFlowInformation(ProbeMode probeMode, IProbeInfo iProbeInfo, Player entityPlayer, + IProbeHitData iProbeHitData, ItemFlowLogic logic) { + if (logic.getMemory().isEmpty()) { + ItemStack countlessLast = logic.getLast().copy(); + countlessLast.setCount(1); + iProbeInfo.horizontal(iProbeInfo.defaultLayoutStyle().alignment(ElementAlignment.ALIGN_CENTER)) + .text(CompoundText.create().info(Component.translatable("gtceu.top.pipe.item_last"))) + .item(countlessLast) + .text(logic.getLast().getDisplayName()); + } + + Object2LongMap counts = logic.getSum(); + + for (var entry : counts.object2LongEntrySet()) { + ItemStack stack = entry.getKey().recombine(); + String value = FormattingUtil.formatNumbers(20 * entry.getLongValue() / ItemFlowLogic.MEMORY_TICKS); + iProbeInfo.horizontal(iProbeInfo.defaultLayoutStyle().alignment(ElementAlignment.ALIGN_CENTER)) + .item(stack) + .text(" §b" + value + " /s §f" + stack.getDisplayName()); + } + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/integration/top/provider/RecipeLogicInfoProvider.java b/src/main/java/com/gregtechceu/gtceu/integration/top/provider/RecipeLogicInfoProvider.java index e8b46ba7cb..0d6bb01b0d 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/top/provider/RecipeLogicInfoProvider.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/top/provider/RecipeLogicInfoProvider.java @@ -69,7 +69,7 @@ protected void addProbeInfo(RecipeLogic capability, IProbeInfo probeInfo, Player if (text == null) { // Default behavior, if this TE is not a steam machine (or somehow not instanceof - // IGregTechTileEntity...) + // IGregTechBlockEntity...) text = ChatFormatting.RED.toString() + absEUt + TextStyleClass.INFO + " EU/t" + ChatFormatting.GREEN + " (" + GTValues.VNF[GTUtil.getTierByVoltage(absEUt)] + ChatFormatting.GREEN + ")"; diff --git a/src/main/java/com/gregtechceu/gtceu/integration/top/provider/StainedColorProvider.java b/src/main/java/com/gregtechceu/gtceu/integration/top/provider/StainedColorProvider.java index 0ed59e7c60..5bc094ff26 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/top/provider/StainedColorProvider.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/top/provider/StainedColorProvider.java @@ -1,8 +1,7 @@ package com.gregtechceu.gtceu.integration.top.provider; import com.gregtechceu.gtceu.GTCEu; -import com.gregtechceu.gtceu.api.blockentity.PipeBlockEntity; -import com.gregtechceu.gtceu.api.machine.IMachineBlockEntity; +import com.gregtechceu.gtceu.api.blockentity.IPaintable; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; @@ -30,17 +29,17 @@ public ResourceLocation getID() { public void addProbeInfo(ProbeMode probeMode, IProbeInfo iProbeInfo, Player player, Level level, BlockState blockState, IProbeHitData iProbeHitData) { BlockEntity blockEntity = level.getBlockEntity(iProbeHitData.getPos()); - int paintingColor = -1; - if (blockEntity instanceof IMachineBlockEntity machineBlockEntity) { - paintingColor = machineBlockEntity.getMetaMachine().getPaintingColor(); - } else if (blockEntity instanceof PipeBlockEntity pipe) { - paintingColor = pipe.getPaintingColor(); - } - if (paintingColor != -1) { - IProbeInfo horizontal = iProbeInfo - .horizontal(iProbeInfo.defaultLayoutStyle().alignment(ElementAlignment.ALIGN_CENTER)); - horizontal.mcText(Component.translatable("gtceu.top.stained", - "#" + Integer.toHexString(paintingColor).toUpperCase(Locale.ROOT))); + + if (blockEntity instanceof IPaintable paintable) { + int paintingColor = paintable.getRealColor(); + int defaultColor = paintable.getDefaultPaintingColor(); + + if (paintingColor != defaultColor) { + IProbeInfo horizontal = iProbeInfo + .horizontal(iProbeInfo.defaultLayoutStyle().alignment(ElementAlignment.ALIGN_CENTER)); + horizontal.mcText(Component.translatable("gtceu.top.stained", + "#" + Integer.toHexString(paintingColor).toUpperCase(Locale.ROOT))); + } } } } diff --git a/src/main/java/com/gregtechceu/gtceu/integration/xei/widgets/GTRecipeWidget.java b/src/main/java/com/gregtechceu/gtceu/integration/xei/widgets/GTRecipeWidget.java index 10ca5b5c48..3c0d684bdd 100644 --- a/src/main/java/com/gregtechceu/gtceu/integration/xei/widgets/GTRecipeWidget.java +++ b/src/main/java/com/gregtechceu/gtceu/integration/xei/widgets/GTRecipeWidget.java @@ -200,8 +200,8 @@ private static List getRecipeParaText(GTRecipe recipe, int duration, // sadly we still need a custom override here, since computation uses duration and EU/t very differently if (recipe.data.getBoolean("duration_is_total_cwu") && recipe.tickInputs.containsKey(CWURecipeCapability.CAP)) { - int minimumCWUt = Math.max(recipe.tickInputs.get(CWURecipeCapability.CAP).stream() - .map(Content::getContent).mapToInt(CWURecipeCapability.CAP::of).sum(), 1); + long minimumCWUt = Math.max(recipe.tickInputs.get(CWURecipeCapability.CAP).stream() + .map(Content::getContent).mapToLong(CWURecipeCapability.CAP::of).sum(), 1); texts.add(Component.translatable("gtceu.recipe.max_eu", FormattingUtil.formatNumbers(euTotal / minimumCWUt))); } else { diff --git a/src/main/java/com/gregtechceu/gtceu/utils/DimensionFacingPos.java b/src/main/java/com/gregtechceu/gtceu/utils/DimensionFacingPos.java new file mode 100644 index 0000000000..118f82d0d3 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/utils/DimensionFacingPos.java @@ -0,0 +1,42 @@ +package com.gregtechceu.gtceu.utils; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.level.Level; + +import lombok.Getter; + +import java.util.Objects; + +public class DimensionFacingPos { + + @Getter + private final BlockPos pos; + @Getter + private final Direction facing; + @Getter + private final ResourceKey dimension; + private final int hashCode; + + public DimensionFacingPos(BlockPos pos, Direction facing, ResourceKey dimension) { + this.pos = pos; + this.facing = facing; + this.dimension = dimension; + this.hashCode = Objects.hash(pos, facing, dimension); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DimensionFacingPos that = (DimensionFacingPos) o; + return dimension == that.dimension && Objects.equals(pos, that.pos) && + facing == that.facing; + } + + @Override + public int hashCode() { + return hashCode; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/utils/DummyMachineBlockEntity.java b/src/main/java/com/gregtechceu/gtceu/utils/DummyMachineBlockEntity.java index 6f5d7f28e9..1927dc1dff 100644 --- a/src/main/java/com/gregtechceu/gtceu/utils/DummyMachineBlockEntity.java +++ b/src/main/java/com/gregtechceu/gtceu/utils/DummyMachineBlockEntity.java @@ -10,6 +10,10 @@ import com.lowdragmc.lowdraglib.syncdata.managed.MultiManagedStorage; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; + import com.google.common.collect.Table; import it.unimi.dsi.fastutil.ints.Int2IntFunction; import lombok.Getter; @@ -45,4 +49,20 @@ public long getOffset() { public MultiManagedStorage getRootStorage() { return null; } + + @Override + public void onNeighborChanged(Block fromBlock, BlockPos fromPos, boolean isMoving) {} + + @Override + public Level getLevel() { + return null; + } + + @Override + public BlockPos getBlockPos() { + return null; + } + + @Override + public void markAsDirty() {} } diff --git a/src/main/java/com/gregtechceu/gtceu/utils/EntityDamageUtil.java b/src/main/java/com/gregtechceu/gtceu/utils/EntityDamageUtil.java index ee25165122..433c88f628 100644 --- a/src/main/java/com/gregtechceu/gtceu/utils/EntityDamageUtil.java +++ b/src/main/java/com/gregtechceu/gtceu/utils/EntityDamageUtil.java @@ -25,13 +25,13 @@ public class EntityDamageUtil { public static void applyTemperatureDamage(@NotNull LivingEntity entity, int temperature, float multiplier, int maximum) { if (temperature > 320) { - int damage = (int) ((multiplier * (temperature - 300)) / 50.0F); + float damage = multiplier * (temperature - 300) / 50.0F; if (maximum > 0) { damage = Math.min(maximum, damage); } applyHeatDamage(entity, damage); } else if (temperature < 260) { - int damage = (int) ((multiplier * (273 - temperature)) / 25.0F); + float damage = multiplier * (273 - temperature) / 25.0F; if (maximum > 0) { damage = Math.min(maximum, damage); } @@ -43,7 +43,7 @@ public static void applyTemperatureDamage(@NotNull LivingEntity entity, int temp * @param entity the entity to damage * @param damage the damage to apply */ - public static void applyHeatDamage(@NotNull LivingEntity entity, int damage) { + public static void applyHeatDamage(@NotNull LivingEntity entity, float damage) { // do not attempt to damage by 0 if (damage <= 0) return; if (!entity.isAlive()) return; @@ -63,7 +63,7 @@ public static void applyHeatDamage(@NotNull LivingEntity entity, int damage) { * @param entity the entity to damage * @param damage the damage to apply */ - public static void applyFrostDamage(@NotNull LivingEntity entity, int damage) { + public static void applyFrostDamage(@NotNull LivingEntity entity, float damage) { // do not attempt to damage by 0 if (damage <= 0) return; if (!entity.isAlive()) return; @@ -91,7 +91,7 @@ public static void applyFrostDamage(@NotNull LivingEntity entity, int damage) { * @param entity the entity to damage * @param damage the damage to apply */ - public static void applyChemicalDamage(@NotNull LivingEntity entity, int damage) { + public static void applyChemicalDamage(@NotNull LivingEntity entity, float damage) { // do not attempt to damage by 0 if (damage <= 0) return; if (!entity.isAlive()) return; @@ -100,7 +100,7 @@ public static void applyChemicalDamage(@NotNull LivingEntity entity, int damage) return; entity.hurt(GTDamageTypes.CHEMICAL.source(entity.level()), damage); - entity.addEffect(new MobEffectInstance(MobEffects.POISON, damage * 100, 1)); + entity.addEffect(new MobEffectInstance(MobEffects.POISON, (int) (damage * 100), 1)); // TODO advancements // if (entity instanceof ServerPlayer) AdvancementTriggers.CHEMICAL_DEATH.trigger((ServerPlayer) entity); } diff --git a/src/main/java/com/gregtechceu/gtceu/utils/FacingPos.java b/src/main/java/com/gregtechceu/gtceu/utils/FacingPos.java index cc7f5d984c..30037085d6 100644 --- a/src/main/java/com/gregtechceu/gtceu/utils/FacingPos.java +++ b/src/main/java/com/gregtechceu/gtceu/utils/FacingPos.java @@ -9,16 +9,18 @@ public class FacingPos { + public static final FacingPos ZERO = new FacingPos(BlockPos.ZERO, null); + @Getter private final BlockPos pos; @Getter - private final Direction facing; + private final Direction direction; private final int hashCode; - public FacingPos(BlockPos pos, Direction facing) { + public FacingPos(BlockPos pos, Direction direction) { this.pos = pos; - this.facing = facing; - this.hashCode = Objects.hash(pos, facing); + this.direction = direction; + this.hashCode = Objects.hash(pos, direction); } @Override @@ -26,7 +28,7 @@ public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; FacingPos facingPos = (FacingPos) o; - return pos.equals(facingPos.pos) && facing == facingPos.getFacing(); + return pos.equals(facingPos.pos) && direction == facingPos.getDirection(); } @Override diff --git a/src/main/java/com/gregtechceu/gtceu/utils/GTUtil.java b/src/main/java/com/gregtechceu/gtceu/utils/GTUtil.java index 09fa0f4c18..26de39ac1f 100644 --- a/src/main/java/com/gregtechceu/gtceu/utils/GTUtil.java +++ b/src/main/java/com/gregtechceu/gtceu/utils/GTUtil.java @@ -7,6 +7,7 @@ import com.gregtechceu.gtceu.api.data.tag.TagPrefix; import com.gregtechceu.gtceu.api.fluids.store.FluidStorageKeys; import com.gregtechceu.gtceu.api.item.tool.GTToolType; +import com.gregtechceu.gtceu.client.ClientProxy; import com.gregtechceu.gtceu.config.ConfigHolder; import com.gregtechceu.gtceu.data.recipe.CustomTags; @@ -15,6 +16,7 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Holder; +import net.minecraft.core.particles.ParticleOptions; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; import net.minecraft.nbt.CompoundTag; @@ -23,17 +25,22 @@ import net.minecraft.network.chat.MutableComponent; import net.minecraft.network.chat.Style; import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; import net.minecraft.tags.BiomeTags; import net.minecraft.util.RandomSource; import net.minecraft.util.Tuple; import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.entity.Entity; import net.minecraft.world.item.DyeColor; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.RecipeType; import net.minecraft.world.level.Level; import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.block.RedStoneWireBlock; +import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.phys.Vec3; import net.minecraftforge.client.extensions.common.IClientFluidTypeExtensions; import net.minecraftforge.common.ForgeHooks; import net.minecraftforge.common.Tags; @@ -42,17 +49,17 @@ import com.mojang.blaze3d.platform.InputConstants; import com.mojang.datafixers.util.Pair; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.lwjgl.glfw.GLFW; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; +import java.util.function.IntPredicate; +import java.util.function.LongPredicate; import static com.gregtechceu.gtceu.api.data.chemical.material.properties.PropertyKey.HAZARD; @@ -149,6 +156,19 @@ public static float getExplosionPower(long voltage) { return getTierByVoltage(voltage) + 1; } + public static int getRedstonePower(Level world, BlockPos blockPos, Direction side) { + BlockPos offsetPos = blockPos.relative(side); + int worldPower = world.getDirectSignal(offsetPos, side); + if (worldPower < 15) { + BlockState offsetState = world.getBlockState(offsetPos); + if (offsetState.getBlock() instanceof RedStoneWireBlock) { + int wirePower = offsetState.getValue(RedStoneWireBlock.POWER); + return Math.max(worldPower, wirePower); + } + } + return worldPower; + } + /** * @param array Array sorted with natural order * @param value Value to search for @@ -385,6 +405,53 @@ public static DyeColor determineDyeColor(int rgbColor) { return distances.get(min); } + public static double geometricMean(double first, double... numbers) { + for (double number : numbers) { + first *= number; + } + return Math.pow(first, 1D / (1 + numbers.length)); + } + + /** + * @param minValue the minimum possible succeeding value + * @param maxValue the maximum possible succeeding value + * @param test the predicate to query for success + * @param ascending determines the direction of search + * @return the smallest succeeding value if ascending, or the largest succeeding value if descending. + */ + public static long binarySearchLong(long minValue, long maxValue, LongPredicate test, boolean ascending) { + while (maxValue - minValue > 1) { + long middle = (minValue + maxValue) / 2; + // XOR + if (test.test(middle) ^ !ascending) { + maxValue = middle; + } else { + minValue = middle; + } + } + return test.test(ascending ? minValue : maxValue) ^ ascending ? maxValue : minValue; + } + + /** + * @param minValue the minimum possible succeeding value + * @param maxValue the maximum possible succeeding value + * @param test the predicate to query for success + * @param ascending determines the direction of search + * @return the smallest succeeding value if ascending, or the largest succeeding value if descending. + */ + public static int binarySearchInt(int minValue, int maxValue, IntPredicate test, boolean ascending) { + while (maxValue - minValue > 1) { + int middle = (minValue + maxValue) / 2; + // XOR + if (test.test(middle) ^ !ascending) { + maxValue = middle; + } else { + minValue = middle; + } + } + return test.test(ascending ? minValue : maxValue) ^ ascending ? maxValue : minValue; + } + public static int convertRGBtoARGB(int colorValue) { return convertRGBtoARGB(colorValue, 0xFF); } @@ -395,6 +462,60 @@ public static int convertRGBtoARGB(int colorValue, int opacity) { return opacity << 24 | colorValue; } + public static int[] convertARGBtoArray(int argb) { + int a = argb >> 24 & 255; + int r = argb >> 16 & 255; + int g = argb >> 8 & 255; + int b = argb & 255; + return new int[] { a, r, g, b }; + } + + @Contract(pure = true) + public static boolean evalMask(@NotNull Enum anEnum, byte mask) { + return (mask & (1 << anEnum.ordinal())) > 0; + } + + @Contract(pure = true) + public static boolean evalMask(@NotNull Enum anEnum, @NotNull BitSet mask) { + return mask.get(anEnum.ordinal()); + } + + @Contract(pure = true) + @NotNull + public static > EnumSet maskToSet(@NotNull Class enumClass, byte mask) { + EnumSet set = EnumSet.noneOf(enumClass); + for (T anEnum : enumClass.getEnumConstants()) { + if (evalMask(anEnum, mask)) set.add(anEnum); + } + return set; + } + + @Contract(pure = true) + @NotNull + public static > EnumSet maskToSet(@NotNull Class enumClass, @NotNull BitSet mask) { + EnumSet set = EnumSet.noneOf(enumClass); + for (T anEnum : enumClass.getEnumConstants()) { + if (evalMask(anEnum, mask)) set.add(anEnum); + } + return set; + } + + @Contract(pure = true) + @NotNull + public static BitSet setToMask(@NotNull EnumSet enumSet) { + BitSet mask = new BitSet(); + for (Enum anEnum : enumSet) { + mask.set(anEnum.ordinal()); + } + return mask; + } + + @Contract(pure = true, value = "-> new") + @NotNull + public static Set createWeakHashSet() { + return Collections.newSetFromMap(new WeakHashMap<>()); + } + /** * @param material the material to use * @return the correct "molten" fluid for a material @@ -417,7 +538,8 @@ public static boolean canSeeSunClearly(Level world, BlockPos blockPos) { return false; } - Biome biome = world.getBiome(blockPos.above()).value(); + Holder biomeHolder = world.getBiome(blockPos.above()); + Biome biome = biomeHolder.value(); if (world.isRaining()) { if (biome.warmEnoughToRain(blockPos.above()) || biome.coldEnoughToSnow(blockPos.above())) { return false; @@ -428,7 +550,7 @@ public static boolean canSeeSunClearly(Level world, BlockPos blockPos) { return false; } - ResourceLocation javdVoidBiome = new ResourceLocation("javd", "void"); + ResourceLocation javdVoidBiome = new ResourceLocation(GTValues.MODID_JAVD, "void"); if (GTCEu.Mods.isJAVDLoaded() && world.registryAccess().registryOrThrow(Registries.BIOME).getKey(biome).equals(javdVoidBiome)) { return !world.isDay(); @@ -518,4 +640,54 @@ public static void addPotionTooltip(List> effects .setStyle(Style.EMPTY.withColor(ChatFormatting.GREEN)))); }); } + + /** + * Forces the initialization of a class; this includes things like loading its static fields. + * This can be useful because a statement like {@code AClass.class} does not initialize a class. + *
+ *
+ * Does nothing if the class is already initialized. + * + * @param clazz the class object to initialize. + */ + public static void forceInitialization(Class clazz) { + try { + Class.forName(clazz.getName(), true, clazz.getClassLoader()); + } catch (ClassNotFoundException e) { + throw new AssertionError(e); // Can't happen + } + } + + public static void spawnParticles(Level world, Direction direction, ParticleOptions particleType, BlockPos pos, + int particleCount) { + spawnParticles(world, direction, particleType, pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, + particleCount); + } + + public static void spawnParticles(Level world, Direction direction, ParticleOptions particleType, Entity entity, + int particleCount) { + Vec3 vec = entity.getEyePosition(1.0f); + spawnParticles(world, direction, particleType, vec.x, vec.y, vec.y, particleCount); + } + + public static void spawnParticles(Level world, Direction direction, ParticleOptions particleType, double x, + double y, double z, + int particleCount) { + if (world instanceof ServerLevel server) { + server.sendParticles(particleType, + x, y, z, particleCount, + direction.getStepX() * 0.2 + GTValues.RNG.nextDouble() * 0.1, + direction.getStepY() * 0.2 + GTValues.RNG.nextDouble() * 0.1, + direction.getStepZ() * 0.2 + GTValues.RNG.nextDouble() * 0.1, + 0.1); + } + } + + public static long getCurrentServerTick() { + if (GTCEu.isClientThread()) { + return ClientProxy.getServerTickCount(); + } else { + return GTCEu.getMinecraftServer().getTickCount(); + } + } } diff --git a/src/main/java/com/gregtechceu/gtceu/utils/MapUtil.java b/src/main/java/com/gregtechceu/gtceu/utils/MapUtil.java new file mode 100644 index 0000000000..9326273eff --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/utils/MapUtil.java @@ -0,0 +1,51 @@ +package com.gregtechceu.gtceu.utils; + +import com.gregtechceu.gtceu.api.graphnet.net.NetNode; +import com.gregtechceu.gtceu.utils.function.ToFloatFunction; + +import it.unimi.dsi.fastutil.objects.Object2BooleanMap; +import it.unimi.dsi.fastutil.objects.Object2FloatMap; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Predicate; +import java.util.function.ToIntFunction; + +public class MapUtil { + + public static int computeIfAbsent(@NotNull Object2IntMap map, @NotNull NetNode key, + @NotNull ToIntFunction compute) { + int val; + if (map.containsKey(key)) { + val = map.getInt(key); + } else { + val = compute.applyAsInt(key); + map.put(key, val); + } + return val; + } + + public static boolean computeIfAbsent(@NotNull Object2BooleanMap map, @NotNull NetNode key, + @NotNull Predicate compute) { + boolean val; + if (map.containsKey(key)) { + val = map.getBoolean(key); + } else { + val = compute.test(key); + map.put(key, val); + } + return val; + } + + public static float computeIfAbsent(@NotNull Object2FloatMap map, @NotNull NetNode key, + @NotNull ToFloatFunction compute) { + float val; + if (map.containsKey(key)) { + val = map.getFloat(key); + } else { + val = compute.applyAsFloat(key); + map.put(key, val); + } + return val; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/utils/ReflectionUtils.java b/src/main/java/com/gregtechceu/gtceu/utils/ReflectionUtils.java new file mode 100644 index 0000000000..d7cab02206 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/utils/ReflectionUtils.java @@ -0,0 +1,18 @@ +package com.gregtechceu.gtceu.utils; + +import java.lang.reflect.Field; + +public class ReflectionUtils { + + public static O getOuterClassObject(I innerClass, Class outerClassType) { + try { + for (Field field : innerClass.getClass().getDeclaredFields()) { + if (field.getType().isAssignableFrom(outerClassType)) { + field.setAccessible(true); + return (O) field.get(innerClass); + } + } + } catch (IllegalAccessException ignored) {} + return null; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/utils/SupplierMemoizer.java b/src/main/java/com/gregtechceu/gtceu/utils/SupplierMemoizer.java index ef6b2b8a2c..cf27a7eae3 100644 --- a/src/main/java/com/gregtechceu/gtceu/utils/SupplierMemoizer.java +++ b/src/main/java/com/gregtechceu/gtceu/utils/SupplierMemoizer.java @@ -10,10 +10,16 @@ public class SupplierMemoizer { public static MemoizedSupplier memoize(Supplier delegate) { + if (delegate instanceof MemoizedSupplier supplier) { + return supplier; + } return new MemoizedSupplier<>(delegate); } public static MemoizedBlockSupplier memoizeBlockSupplier(Supplier delegate) { + if (delegate instanceof MemoizedBlockSupplier supplier) { + return supplier; + } return new MemoizedBlockSupplier<>(delegate); } diff --git a/src/main/java/com/gregtechceu/gtceu/utils/TaskScheduler.java b/src/main/java/com/gregtechceu/gtceu/utils/TaskScheduler.java new file mode 100644 index 0000000000..ee32a53310 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/utils/TaskScheduler.java @@ -0,0 +1,90 @@ +package com.gregtechceu.gtceu.utils; + +import com.gregtechceu.gtceu.GTCEu; +import com.gregtechceu.gtceu.utils.function.Task; + +import net.minecraft.world.level.Level; +import net.minecraftforge.event.TickEvent; +import net.minecraftforge.event.level.LevelEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; + +import org.jetbrains.annotations.Nullable; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Mod.EventBusSubscriber(modid = GTCEu.MOD_ID) +public class TaskScheduler { + + @Nullable + public static TaskScheduler get(Level world) { + return tasksPerWorld.get(world); + } + + private static final Map tasksPerWorld = new HashMap<>(); + + private final List tasks = new ArrayList<>(); + private final List scheduledTasks = new ArrayList<>(); + private boolean running = false; + + public static void scheduleTask(Level world, Task task) { + if (world.isClientSide) { + throw new IllegalArgumentException("Attempt to schedule task on client world!"); + } + tasksPerWorld.computeIfAbsent(world, k -> new TaskScheduler()).scheduleTask(task); + } + + public void scheduleTask(Task task) { + if (running) { + scheduledTasks.add(task); + } else { + tasks.add(task); + } + } + + public void unload() { + tasks.clear(); + scheduledTasks.clear(); + } + + @SubscribeEvent + public static void onWorldUnload(LevelEvent.Unload event) { + if (!event.getLevel().isClientSide()) { + tasksPerWorld.remove(event.getLevel()); + } + } + + @SubscribeEvent + public static void onWorldTick(TickEvent.LevelTickEvent event) { + if (!event.level.isClientSide && event.phase == TickEvent.Phase.START) { + TaskScheduler scheduler = get(event.level); + if (scheduler != null) { + if (!scheduler.scheduledTasks.isEmpty()) { + scheduler.tasks.addAll(scheduler.scheduledTasks); + scheduler.scheduledTasks.clear(); + } + scheduler.running = true; + scheduler.tasks.removeIf(task -> !task.run()); + scheduler.running = false; + } + } + } + + public static Task weakTask(Task task) { + return new Task() { + + private final WeakReference ref = new WeakReference<>(task); + + @Override + public boolean run() { + Task task = this.ref.get(); + if (task == null) return false; + else return task.run(); + } + }; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/utils/TriConsumer.java b/src/main/java/com/gregtechceu/gtceu/utils/TriConsumer.java new file mode 100644 index 0000000000..f61650ded4 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/utils/TriConsumer.java @@ -0,0 +1,7 @@ +package com.gregtechceu.gtceu.utils; + +@FunctionalInterface +public interface TriConsumer { + + void accept(T t, U u, S s); +} diff --git a/src/main/java/com/gregtechceu/gtceu/utils/function/BiIntConsumer.java b/src/main/java/com/gregtechceu/gtceu/utils/function/BiIntConsumer.java new file mode 100644 index 0000000000..0e394fb8b8 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/utils/function/BiIntConsumer.java @@ -0,0 +1,6 @@ +package com.gregtechceu.gtceu.utils.function; + +public interface BiIntConsumer { + + void accept(int a, int b); +} diff --git a/src/main/java/com/gregtechceu/gtceu/utils/function/Task.java b/src/main/java/com/gregtechceu/gtceu/utils/function/Task.java new file mode 100644 index 0000000000..723fd57497 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/utils/function/Task.java @@ -0,0 +1,12 @@ +package com.gregtechceu.gtceu.utils.function; + +@FunctionalInterface +public interface Task { + + /** + * Run the actions of this Task. Will be infinitely run each world tick until false is returned. + * + * @return {@code true} if the task should be run again, otherwise {@code false} + */ + boolean run(); +} diff --git a/src/main/java/com/gregtechceu/gtceu/utils/function/ToFloatFunction.java b/src/main/java/com/gregtechceu/gtceu/utils/function/ToFloatFunction.java new file mode 100644 index 0000000000..507e170b46 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/utils/function/ToFloatFunction.java @@ -0,0 +1,28 @@ +package com.gregtechceu.gtceu.utils.function; + +import java.util.function.Function; + +/** + * Represents a function that produces a float-valued result. This is the + * {@code float}-producing primitive specialization for {@link Function}. + * + *

+ * This is a functional interface + * whose functional method is {@link #applyAsFloat(Object)}. + * + * @param the type of the input to the function + * + * @see Function + * @since 1.7.0 + */ +@FunctionalInterface +public interface ToFloatFunction { + + /** + * Applies this function to the given argument. + * + * @param value the function argument + * @return the function result + */ + float applyAsFloat(T value); +} diff --git a/src/main/java/com/gregtechceu/gtceu/utils/input/KeyBind.java b/src/main/java/com/gregtechceu/gtceu/utils/input/KeyBind.java index 82e6a6b61d..af62cf3d84 100644 --- a/src/main/java/com/gregtechceu/gtceu/utils/input/KeyBind.java +++ b/src/main/java/com/gregtechceu/gtceu/utils/input/KeyBind.java @@ -66,7 +66,7 @@ public static void onInputEvent(InputEvent.Key event) { updating.add(keybind); } } - if (!updating.isEmpty()) { + if (!updating.isEmpty() && Minecraft.getInstance().getConnection() != null) { GTNetwork.NETWORK.sendToServer(new CPacketKeysPressed(updating)); } } diff --git a/src/main/java/com/gregtechceu/gtceu/utils/reference/RegeneratingSoftReference.java b/src/main/java/com/gregtechceu/gtceu/utils/reference/RegeneratingSoftReference.java new file mode 100644 index 0000000000..d23ab8b333 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/utils/reference/RegeneratingSoftReference.java @@ -0,0 +1,44 @@ +package com.gregtechceu.gtceu.utils.reference; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; +import java.util.function.Supplier; + +public class RegeneratingSoftReference implements Supplier { + + private final @Nullable ReferenceQueue q; + private final @NotNull Supplier regenerator; + private @NotNull SoftReference reference; + + public RegeneratingSoftReference(@NotNull T initialReference, @NotNull Supplier regenerator, + @Nullable ReferenceQueue q) { + this.q = q; + this.reference = new SoftReference<>(initialReference, q); + this.regenerator = regenerator; + } + + public RegeneratingSoftReference(@NotNull Supplier regenerator, @Nullable ReferenceQueue q) { + this(regenerator.get(), regenerator, q); + } + + public RegeneratingSoftReference(@NotNull T initialReference, @NotNull Supplier regenerator) { + this(initialReference, regenerator, null); + } + + public RegeneratingSoftReference(@NotNull Supplier regenerator) { + this(regenerator.get(), regenerator); + } + + @Override + public T get() { + T fetch = reference.get(); + if (fetch == null) { + fetch = regenerator.get(); + reference = new SoftReference<>(fetch, q); + } + return fetch; + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/utils/reference/WeakHashSet.java b/src/main/java/com/gregtechceu/gtceu/utils/reference/WeakHashSet.java new file mode 100644 index 0000000000..b6c458e837 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/utils/reference/WeakHashSet.java @@ -0,0 +1,135 @@ +package com.gregtechceu.gtceu.utils.reference; + +import org.jetbrains.annotations.NotNull; + +import java.util.AbstractSet; +import java.util.Collection; +import java.util.Iterator; +import java.util.Objects; +import java.util.Set; +import java.util.Spliterator; +import java.util.WeakHashMap; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.stream.Stream; + +/** + * Replica of {@link java.util.Collections.SetFromMap} for {@link WeakHashMap} to allow for greater type specificity + * than the {@link java.util.Set} interface. + */ +public class WeakHashSet extends AbstractSet { + + private final WeakHashMap m = new WeakHashMap<>(); + + private final transient Set s = m.keySet(); + + @Override + public void clear() { + m.clear(); + } + + @Override + public int size() { + return m.size(); + } + + @Override + public boolean isEmpty() { + return m.isEmpty(); + } + + // TODO access WeakHashMap#getEntry somehow + // public E get(Object o) { + // } + + @SuppressWarnings("SuspiciousMethodCalls") + @Override + public boolean contains(Object o) { + return m.containsKey(o); + } + + @Override + public boolean remove(Object o) { + return m.remove(o) != null; + } + + @Override + public boolean add(E e) { + return m.put(e, Boolean.TRUE) == null; + } + + @Override + public Iterator iterator() { + return s.iterator(); + } + + @Override + public Object[] toArray() { + return s.toArray(); + } + + @Override + public T[] toArray(T @NotNull [] a) { + return s.toArray(a); + } + + @Override + public String toString() { + return s.toString(); + } + + @Override + public int hashCode() { + return s.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + WeakHashSet that = (WeakHashSet) o; + return Objects.equals(s, that.s); + } + + @Override + public boolean containsAll(@NotNull Collection c) { + return s.containsAll(c); + } + + @Override + public boolean removeAll(Collection c) { + return s.removeAll(c); + } + + @Override + public boolean retainAll(@NotNull Collection c) { + return s.retainAll(c); + } + // addAll is the only inherited implementation + + @Override + public void forEach(Consumer action) { + s.forEach(action); + } + + @Override + public boolean removeIf(Predicate filter) { + return s.removeIf(filter); + } + + @Override + public Spliterator spliterator() { + return s.spliterator(); + } + + @Override + public Stream stream() { + return s.stream(); + } + + @Override + public Stream parallelStream() { + return s.parallelStream(); + } +} diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index e416b85be7..030667f7bd 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -25,3 +25,5 @@ public net.minecraft.world.entity.vehicle.Boat f_38285_ # DATA_ID_TYPE # for AE2 to be able to load (idk why but it needs these?) public net.minecraft.world.level.block.Blocks m_50774_(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/BlockGetter;Lnet/minecraft/core/BlockPos;)Z # always public net.minecraft.world.level.block.Blocks m_50805_(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/BlockGetter;Lnet/minecraft/core/BlockPos;)Z # never + +public net.minecraft.client.resources.model.ModelBakery$ModelBakerImpl diff --git a/src/main/resources/assets/gtceu/blockstates/cable.json b/src/main/resources/assets/gtceu/blockstates/cable.json new file mode 100644 index 0000000000..9357c18147 --- /dev/null +++ b/src/main/resources/assets/gtceu/blockstates/cable.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "gtceu:block/cube/all" + } + } +} diff --git a/src/main/resources/assets/gtceu/blockstates/pipe_activable.json b/src/main/resources/assets/gtceu/blockstates/pipe_activable.json new file mode 100644 index 0000000000..9357c18147 --- /dev/null +++ b/src/main/resources/assets/gtceu/blockstates/pipe_activable.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "gtceu:block/cube/all" + } + } +} diff --git a/src/main/resources/assets/gtceu/blockstates/pipe_material.json b/src/main/resources/assets/gtceu/blockstates/pipe_material.json new file mode 100644 index 0000000000..9357c18147 --- /dev/null +++ b/src/main/resources/assets/gtceu/blockstates/pipe_material.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "gtceu:block/cube/all" + } + } +} diff --git a/src/main/resources/assets/gtceu/lang/ja_jp.json b/src/main/resources/assets/gtceu/lang/ja_jp.json index 21807a8b78..5499d44623 100644 --- a/src/main/resources/assets/gtceu/lang/ja_jp.json +++ b/src/main/resources/assets/gtceu/lang/ja_jp.json @@ -2322,7 +2322,7 @@ "gtceu.item_filter.empty_item": "指定なし", "gtceu.item_filter.footer": "§e 書き換える項目でクリックする。", "gtceu.item_list.item_stored": "§7格納: %d", - "gtceu.item_pipe.priority": "§9優先順位: §f%d", + "gtceu.pipe.priority": "§9優先順位: §f%d", "gtceu.jade.cleaned_this_second": "汚染の洗浄度: %s/s", "gtceu.jade.energy_stored": "%d / %d EU", "gtceu.jade.progress_computation": "%s / %s CWU", diff --git a/src/main/resources/assets/gtceu/lang/ru_ru.json b/src/main/resources/assets/gtceu/lang/ru_ru.json index 59d0beaf20..3d7f454e75 100644 --- a/src/main/resources/assets/gtceu/lang/ru_ru.json +++ b/src/main/resources/assets/gtceu/lang/ru_ru.json @@ -1668,7 +1668,7 @@ "gtceu.item_filter.empty_item": "Пусто (нет предмета)", "gtceu.item_filter.footer": "§eНажмите с предметом, чтобы переопределить", "gtceu.item_list.item_stored": "§7Хранится: %d", - "gtceu.item_pipe.priority": "§9Приоритет: §f%d", + "gtceu.pipe.priority": "§9Приоритет: §f%d", "gtceu.jei.bedrock_fluid.heavy_oil_deposit": "Залежь тяжелой нефти", "gtceu.jei.bedrock_fluid.lava_deposit": "Залежь лавы", "gtceu.jei.bedrock_fluid.light_oil_deposit": "Залежь легкой нефти", diff --git a/src/main/resources/assets/gtceu/lang/zh_cn.json b/src/main/resources/assets/gtceu/lang/zh_cn.json index 48329a8a47..c3abd7426a 100644 --- a/src/main/resources/assets/gtceu/lang/zh_cn.json +++ b/src/main/resources/assets/gtceu/lang/zh_cn.json @@ -2368,7 +2368,7 @@ "gtceu.item_filter.empty_item":"空(无物品)", "gtceu.item_filter.footer":"§e手持物品右键覆盖设置", "gtceu.item_list.item_stored":"§7储量:%d", - "gtceu.item_pipe.priority":"§9优先级:§f%d", + "gtceu.pipe.priority":"§9优先级:§f%d", "gtceu.jade.cleaned_this_second":"污染清理速率:%s/s", "gtceu.jade.energy_stored":"%d / %d EU", "gtceu.jade.progress_computation":"%s / %s CWU", diff --git a/src/main/resources/assets/gtceu/lang/zh_tw.json b/src/main/resources/assets/gtceu/lang/zh_tw.json index 7b53e54639..a583cbc38e 100644 --- a/src/main/resources/assets/gtceu/lang/zh_tw.json +++ b/src/main/resources/assets/gtceu/lang/zh_tw.json @@ -2322,7 +2322,7 @@ "gtceu.item_filter.empty_item":"空(無物品)", "gtceu.item_filter.footer":"§e手持物品右鍵取代設定", "gtceu.item_list.item_stored":"§7儲量:%d", - "gtceu.item_pipe.priority":"§9優先順序:§f%d", + "gtceu.pipe.priority":"§9優先順序:§f%d", "gtceu.jade.cleaned_this_second":"污染清理速率:%s/s", "gtceu.jade.energy_stored":"%d / %d EU", "gtceu.jade.progress_computation":"計算進度:%s / %s", diff --git a/src/main/resources/assets/gtceu/models/item/invar_lighter.json b/src/main/resources/assets/gtceu/models/item/invar_lighter.json index a777fd4faf..06b961df48 100644 --- a/src/main/resources/assets/gtceu/models/item/invar_lighter.json +++ b/src/main/resources/assets/gtceu/models/item/invar_lighter.json @@ -1,7 +1,7 @@ { "parent": "item/generated", "textures": { - "layer0": "gtceu:items/invar_lighter" + "layer0": "gtceu:items/platinum_lighter_closed" }, "overrides": [ { diff --git a/src/main/resources/assets/gtceu/models/item/platinum_lighter.json b/src/main/resources/assets/gtceu/models/item/platinum_lighter.json index fb1913edac..1e856ee603 100644 --- a/src/main/resources/assets/gtceu/models/item/platinum_lighter.json +++ b/src/main/resources/assets/gtceu/models/item/platinum_lighter.json @@ -1,7 +1,7 @@ { "parent": "item/generated", "textures": { - "layer0": "gtceu:items/platinum_lighter" + "layer0": "gtceu:items/platinum_lighter_closed" }, "overrides": [ { diff --git a/src/main/resources/assets/gtceu/textures/block/fluids/fluid.oil_heavy.png b/src/main/resources/assets/gtceu/textures/block/fluids/fluid.heavy_oil.png similarity index 100% rename from src/main/resources/assets/gtceu/textures/block/fluids/fluid.oil_heavy.png rename to src/main/resources/assets/gtceu/textures/block/fluids/fluid.heavy_oil.png diff --git a/src/main/resources/assets/gtceu/textures/block/fluids/fluid.oil_heavy.png.mcmeta b/src/main/resources/assets/gtceu/textures/block/fluids/fluid.heavy_oil.png.mcmeta similarity index 100% rename from src/main/resources/assets/gtceu/textures/block/fluids/fluid.oil_heavy.png.mcmeta rename to src/main/resources/assets/gtceu/textures/block/fluids/fluid.heavy_oil.png.mcmeta diff --git a/src/main/resources/assets/gtceu/textures/block/fluids/fluid.oil_light.png b/src/main/resources/assets/gtceu/textures/block/fluids/fluid.light_oil.png similarity index 100% rename from src/main/resources/assets/gtceu/textures/block/fluids/fluid.oil_light.png rename to src/main/resources/assets/gtceu/textures/block/fluids/fluid.light_oil.png diff --git a/src/main/resources/assets/gtceu/textures/block/fluids/fluid.oil_light.png.mcmeta b/src/main/resources/assets/gtceu/textures/block/fluids/fluid.light_oil.png.mcmeta similarity index 100% rename from src/main/resources/assets/gtceu/textures/block/fluids/fluid.oil_light.png.mcmeta rename to src/main/resources/assets/gtceu/textures/block/fluids/fluid.light_oil.png.mcmeta diff --git a/src/main/resources/assets/gtceu/textures/block/fluids/fluid.oil_medium.png b/src/main/resources/assets/gtceu/textures/block/fluids/fluid.raw_oil.png similarity index 100% rename from src/main/resources/assets/gtceu/textures/block/fluids/fluid.oil_medium.png rename to src/main/resources/assets/gtceu/textures/block/fluids/fluid.raw_oil.png diff --git a/src/main/resources/assets/gtceu/textures/block/fluids/fluid.oil_medium.png.mcmeta b/src/main/resources/assets/gtceu/textures/block/fluids/fluid.raw_oil.png.mcmeta similarity index 100% rename from src/main/resources/assets/gtceu/textures/block/fluids/fluid.oil_medium.png.mcmeta rename to src/main/resources/assets/gtceu/textures/block/fluids/fluid.raw_oil.png.mcmeta diff --git a/src/main/resources/assets/gtceu/textures/block/pipe/pipe_laser_side.png b/src/main/resources/assets/gtceu/textures/block/pipe/pipe_laser_side.png index b371bdb0b0..dd3da3493c 100644 Binary files a/src/main/resources/assets/gtceu/textures/block/pipe/pipe_laser_side.png and b/src/main/resources/assets/gtceu/textures/block/pipe/pipe_laser_side.png differ diff --git a/src/main/resources/assets/gtceu/textures/block/pipe/pipe_optical_side_overlay.png b/src/main/resources/assets/gtceu/textures/block/pipe/pipe_optical_side_overlay.png new file mode 100644 index 0000000000..57fb4be366 Binary files /dev/null and b/src/main/resources/assets/gtceu/textures/block/pipe/pipe_optical_side_overlay.png differ diff --git a/src/main/resources/assets/gtceu/textures/block/pipe/pipe_optical_side_overlay_active.png b/src/main/resources/assets/gtceu/textures/block/pipe/pipe_optical_side_overlay_active.png new file mode 100644 index 0000000000..d0d5cdc54a Binary files /dev/null and b/src/main/resources/assets/gtceu/textures/block/pipe/pipe_optical_side_overlay_active.png differ diff --git a/src/main/resources/assets/gtceu/textures/block/pipe/pipe_optical_side_overlay_active.png.mcmeta b/src/main/resources/assets/gtceu/textures/block/pipe/pipe_optical_side_overlay_active.png.mcmeta new file mode 100644 index 0000000000..21972735d3 --- /dev/null +++ b/src/main/resources/assets/gtceu/textures/block/pipe/pipe_optical_side_overlay_active.png.mcmeta @@ -0,0 +1,5 @@ +{ + "animation": { + "frametime": 25 + } +} diff --git a/src/main/resources/assets/gtceu/textures/item/avanced_nanomuscle_chestplate.png b/src/main/resources/assets/gtceu/textures/item/advanced_nanomuscle_chestplate.png similarity index 100% rename from src/main/resources/assets/gtceu/textures/item/avanced_nanomuscle_chestplate.png rename to src/main/resources/assets/gtceu/textures/item/advanced_nanomuscle_chestplate.png diff --git a/src/main/resources/assets/gtceu/textures/item/dielectric_laser_mirror.png b/src/main/resources/assets/gtceu/textures/item/dielectric_laser_mirror.png new file mode 100644 index 0000000000..398c95041d Binary files /dev/null and b/src/main/resources/assets/gtceu/textures/item/dielectric_laser_mirror.png differ diff --git a/src/main/resources/gtceu.mixins.json b/src/main/resources/gtceu.mixins.json index f3d503ca48..52109573ab 100644 --- a/src/main/resources/gtceu.mixins.json +++ b/src/main/resources/gtceu.mixins.json @@ -12,6 +12,7 @@ "GuiGraphicsAccessor", "GuiGraphicsMixin", "GuiHeartTypeMixin", + "HotbarManagerMixin", "LevelRendererMixin", "ModelManagerMixin", "MultiPlayerGameModeMixin", @@ -29,23 +30,27 @@ "mixins": [ "AbstractRegistrateAccessor", "BlockBehaviourAccessor", + "BlockEntityMixin", "BlockMixin", "BlockPropertiesAccessor", "ChunkGeneratorMixin", "ChunkMixin", + "ChunkSerializerMixin", "CreeperMixin", + "DataFixTypesMixin", "EntityMixin", "IFoliagePlacerTypeAccessor", - "IHolderReferenceAccessor", "IngredientAccessor", "IntersectionIngredientAccessor", "InventoryMixin", + "IOWorkerMixin", "ItemValueAccessor", "ITrunkPlacerTypeAccessor", "LevelMixin", "LivingEntityMixin", "LootDataManagerMixin", "LootPoolAccessor", + "NbtUtilsMixin", "OreConfigurationMixin", "OreVeinifierMixin", "PartialNBTIngredientAccessor", @@ -60,6 +65,7 @@ "ShapedRecipeAccessor", "SidedRedstoneConnectivityMixin", "StrictNBTIngredientAccessor", + "StructureMixin", "TagLoaderMixin", "TagManagerMixin", "TagValueAccessor",