diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f7ec54d5..d339cb196 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Changed +- We enhanced the RS Bridge in a major way including new functions, a better crafting system and more. Refer to the [docs](https://docs.advanced-peripherals.de/0.7-bridges/guides/storage_system_functions/) + ### Fixed - [#711] Fixed that the block reader would not be able to access registries for component information diff --git a/build.gradle b/build.gradle index c6f00f5cd..6599f3fe3 100644 --- a/build.gradle +++ b/build.gradle @@ -175,15 +175,23 @@ repositories { } } maven { - url = uri("https://maven.pkg.github.com/refinedmods/refinedstorage") + url = uri("https://maven.pkg.github.com/refinedmods/refinedstorage2") credentials { username = "anything" password = "\u0067hp_oGjcDFCn8jeTzIj4Ke9pLoEVtpnZMP4VQgaX" } - content { - includeModule("com.refinedmods", "refinedstorage") + } + maven { + url = uri("https://maven.pkg.github.com/refinedmods/refinedstorage-mekanism-integration") + credentials { + username = "anything" + password = "\u0067hp_oGjcDFCn8jeTzIj4Ke9pLoEVtpnZMP4VQgaX" } } + maven { + name = "EMI" + url = uri("https://maven.terraformersmc.com/") + } maven { name = 'Kotlin for Forge' url = 'https://thedarkcolour.github.io/KotlinForForge/' @@ -214,13 +222,21 @@ dependencies { compileOnly "org.jetbrains:annotations:${jb_annotations}" implementation "net.neoforged:neoforge:${neo_version}" implementation "cc.tweaked:cc-tweaked-${minecraft_version}-forge:${cc_version}" - // Minimal requirements end + compileOnly("com.refinedmods.refinedstorage:refinedstorage-neoforge:${refinedstorage_version}") + runtimeOnly("com.refinedmods.refinedstorage:refinedstorage-neoforge:${refinedstorage_version}") + + compileOnly("com.refinedmods.refinedstorage:refinedstorage-mekanism-integration:${refinedstorage_mekanism_version}") + runtimeOnly("com.refinedmods.refinedstorage:refinedstorage-mekanism-integration:${refinedstorage_mekanism_version}") + + // Needed for the refined storage mek integration + compileOnlyApi("dev.emi:emi-neoforge:${emiVersion}") + // Extended requirements // We don't use the api since we need a specific class from mekanism compileOnly "mekanism:Mekanism:${mekanism_version}" - // runtimeOnly "mekanism:Mekanism:${mekanism_version}" + runtimeOnly "mekanism:Mekanism:${mekanism_version}" // Applied Energistics 2 compileOnly "appeng:appliedenergistics2:${appliedenergistics_version}" diff --git a/gradle.properties b/gradle.properties index 23543d16d..02593d2b1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,7 +10,7 @@ mod_id=advancedperipherals mod_version=0.7.49a minecraft_version=1.21.1 mod_artifact_suffix= -neo_version=21.1.115 +neo_version=21.1.133 parchment_minecraft_version=1.21.1 parchment_mappings_version=2024.11.17 loader_version=4 @@ -25,14 +25,16 @@ kotlinx_coroutines_version=1.6.0-RC3 ttoolkit_version=0.1.3 # Mod dependencies -cc_version=1.113.1 +cc_version=1.115.1 ae2things_version=5637783 appliedenergistics_version=19.1.2-beta appliedmekanistics_version=5978711 -mekanism_version=1.21.1-10.7.8.70 +refinedstorage_version=2.0.0-beta.2 +refinedstorage_mekanism_version=1.0.0 minecolonies_version=1.1.846-1.21.1-snapshot patchouli_version=1.21-87 +mekanism_version=1.21.1-10.7.12.77 powah_version=6143661 # Mod dependencies which are needed for other mods @@ -41,6 +43,8 @@ structurize_version=1.0.755-1.21.1-snapshot multipiston_version=1.2.51-1.21.1-snapshot blockui_version=1.0.192-1.21.1-snapshot domumornamentum_version=1.0.204-1.21.1-snapshot +# refined storage mek integration +emiVersion=1.1.11+1.21 # powah cloth_config_version=15.0.127 diff --git a/src/generated/resources/.cache/2f719b8031aed5e605ed8d6694b19245ca45c6ca b/src/generated/resources/.cache/2f719b8031aed5e605ed8d6694b19245ca45c6ca index f9b340e45..78f757f2c 100644 --- a/src/generated/resources/.cache/2f719b8031aed5e605ed8d6694b19245ca45c6ca +++ b/src/generated/resources/.cache/2f719b8031aed5e605ed8d6694b19245ca45c6ca @@ -1,2 +1,2 @@ -// 1.21.1 2025-01-15T03:22:26.2798836 Languages: en_us for mod: advancedperipherals -df95afe714aeff7559b0093257aeca501dabb1e2 assets/advancedperipherals/lang/en_us.json +// 1.21.1 2025-03-20T00:32:42.9859105 Languages: en_us for mod: advancedperipherals +d2006c7f1d9c5f1f768b50a3c445dc8b566bf9dd assets/advancedperipherals/lang/en_us.json diff --git a/src/generated/resources/.cache/59eb3dbb5f86130e09b3c62d89b9525ee01cf52d b/src/generated/resources/.cache/59eb3dbb5f86130e09b3c62d89b9525ee01cf52d index f4d319548..f5983bb9d 100644 --- a/src/generated/resources/.cache/59eb3dbb5f86130e09b3c62d89b9525ee01cf52d +++ b/src/generated/resources/.cache/59eb3dbb5f86130e09b3c62d89b9525ee01cf52d @@ -1,4 +1,4 @@ -// 1.21.1 2025-01-14T18:51:29.8897473 Loot Tables +// 1.21.1 2025-01-14T21:35:41.3183672 Loot Tables 9b54955770745d55de988a86cbd08aa9ae23ff86 data/advancedperipherals/loot_table/blocks/block_reader.json d4f1bc579f83dc67ce9f94d35926d34b8894c990 data/advancedperipherals/loot_table/blocks/chat_box.json 9ac96b8e3da827886d8da459a2f146e8ae3ee798 data/advancedperipherals/loot_table/blocks/colony_integrator.json @@ -11,3 +11,4 @@ ae62bfa75e39a59374602c8a87cc2afe8b4923dd data/advancedperipherals/loot_table/blo fa56de9a073f32ab8a65b5f17c57f360a157b5c1 data/advancedperipherals/loot_table/blocks/peripheral_casing.json d61b1c54dd6da8f0213d5eb8a5b40d78bff5ba90 data/advancedperipherals/loot_table/blocks/player_detector.json 6bad9bed7baa7af4b5ca3cae7c4cb8d47dc7b593 data/advancedperipherals/loot_table/blocks/redstone_integrator.json +76cdba2795aafc897a579670afb264b75147f498 data/advancedperipherals/loot_table/blocks/rs_bridge.json diff --git a/src/generated/resources/.cache/9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e b/src/generated/resources/.cache/9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e index 7d5df1cf6..abb520449 100644 --- a/src/generated/resources/.cache/9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e +++ b/src/generated/resources/.cache/9fb1092f32d4fcbf9e061ffd718d4ec689c6c95e @@ -1,4 +1,4 @@ -// 1.21.1 2025-01-14T19:12:56.350339 Recipes +// 1.21.1 2025-01-14T21:43:25.122686 Recipes f5ba1f7d129c88dca1a13c8deaae2d269644c7fa data/advancedperipherals/advancement/recipes/redstone/block_reader.json e172645262ade9fc4a1c999a2c0041711c2eab55 data/advancedperipherals/advancement/recipes/redstone/chat_box.json 213de9a7e340c5eb9c01d82a72e7b58921c5290a data/advancedperipherals/advancement/recipes/redstone/chunk_controller.json @@ -17,6 +17,7 @@ f218420f4c94524614229bb2d50e5c7f8e6beb28 data/advancedperipherals/advancement/re 2c38bc3468e8db1caac94d8ddfff68f50d596439 data/advancedperipherals/advancement/recipes/redstone/peripheral_casing.json 7dd96be9f8e31ce60752727119aa9fc738c43e3b data/advancedperipherals/advancement/recipes/redstone/player_detector.json 38be5210a7d2bb22d63674af46867a1b38c022e1 data/advancedperipherals/advancement/recipes/redstone/redstone_integrator.json +a0cab6b6139cd0d6fa86842031a59a3eff69a4ea data/advancedperipherals/advancement/recipes/redstone/rs_bridge.json 6489fff67f0d703c3a087d728eb4801413b686f5 data/advancedperipherals/advancement/recipes/redstone/weak_automata_core.json b48e0eed6f4cfbe62c8b7c411e92e3b38ad4e4f9 data/advancedperipherals/recipe/block_reader.json c8d06dd085c0477f94d90f3a5f0800c3efc08d02 data/advancedperipherals/recipe/chat_box.json @@ -36,4 +37,5 @@ ba80666226c2a0d1aa30709e31756ca27076069a data/advancedperipherals/recipe/overpow 0d659e0d2c832e4eef3e43cb77ddad64e071345a data/advancedperipherals/recipe/peripheral_casing.json 205166613824976d691cd0503f0e399e5b17616e data/advancedperipherals/recipe/player_detector.json b219e8902fcea47776cb23c85e25e58efecc8f00 data/advancedperipherals/recipe/redstone_integrator.json +e0228e5cddd9cc266fadb0fd7c9476285f79143c data/advancedperipherals/recipe/rs_bridge.json 898d1243e5d1038e934f3ef5522aeae2f0468f43 data/advancedperipherals/recipe/weak_automata_core.json diff --git a/src/generated/resources/.cache/b8526e444ae7356037f3a813274f6835d1f3dd16 b/src/generated/resources/.cache/b8526e444ae7356037f3a813274f6835d1f3dd16 index 0b5e6c2a4..9ee8b44df 100644 --- a/src/generated/resources/.cache/b8526e444ae7356037f3a813274f6835d1f3dd16 +++ b/src/generated/resources/.cache/b8526e444ae7356037f3a813274f6835d1f3dd16 @@ -1,4 +1,4 @@ -// 1.21.1 2025-01-15T03:30:04.6987737 Block States: advancedperipherals +// 1.21.1 2025-01-15T15:11:12.9901322 Block States: advancedperipherals 5e28ce1be9a6996d982641e5df1fa7162090b8cc assets/advancedperipherals/blockstates/block_reader.json f42bdde60f84fdb312f7cf3b2be461d9c11ebdc8 assets/advancedperipherals/blockstates/chat_box.json 1227aa092fcf1327547ace6ccc9db230e45891b0 assets/advancedperipherals/blockstates/colony_integrator.json @@ -11,6 +11,7 @@ d1fe6188b0b0ce8779cb9795a746177858cbaa41 assets/advancedperipherals/blockstates/ 51d1ce8b8772773acc82895ff6314370138c9d5a assets/advancedperipherals/blockstates/peripheral_casing.json ff12c7217911184266589813a2c8f9b0d46cfd65 assets/advancedperipherals/blockstates/player_detector.json 726cf2599b0c765bcfacda88a1943be74f985877 assets/advancedperipherals/blockstates/redstone_integrator.json +6b176e8fdb048f7b6678bfbc1c4baf2bcfa67a1f assets/advancedperipherals/blockstates/rs_bridge.json 544ff1ecb58622350b58940036b4b1908e1146da assets/advancedperipherals/models/block/block_reader.json dab55424ec184c5495c7ca11e5bbe77210e04c26 assets/advancedperipherals/models/block/chat_box.json 7e207db9b2b170f52565c8ed23bcc92762be6c4d assets/advancedperipherals/models/block/colony_integrator.json @@ -23,3 +24,4 @@ dab55424ec184c5495c7ca11e5bbe77210e04c26 assets/advancedperipherals/models/block 36b6ac01be085492aa6298eeb89e6ecaa3cb6f82 assets/advancedperipherals/models/block/peripheral_casing.json 120df29f21059aa9d4dff53bbb80953dee8d5214 assets/advancedperipherals/models/block/player_detector.json d08b8946e1eb01cc9c8af4fa297b582614d1034b assets/advancedperipherals/models/block/redstone_integrator.json +15685a3c58f78db663609d56ee6fdb29652eef5f assets/advancedperipherals/models/block/rs_bridge.json diff --git a/src/generated/resources/.cache/f95c7003282837dabaa33e3ffceec4e6865b5218 b/src/generated/resources/.cache/f95c7003282837dabaa33e3ffceec4e6865b5218 index 7f7c59f7e..45bd99a68 100644 --- a/src/generated/resources/.cache/f95c7003282837dabaa33e3ffceec4e6865b5218 +++ b/src/generated/resources/.cache/f95c7003282837dabaa33e3ffceec4e6865b5218 @@ -1,4 +1,4 @@ -// 1.21.1 2025-01-14T18:51:29.8886424 Block tags -6566cca83fe6d74441cb2b220aa517b93951dac9 data/minecraft/tags/blocks/mineable/pickaxe.json -5c9d90b6cb82fe772165b794f1eb511db72cb639 data/minecraft/tags/blocks/needs_iron_tool.json +// 1.21.1 2025-01-14T21:35:41.316368 Block tags +ad2dad8f4f2ec5a67735fe462d83d1ac4c3ef006 data/minecraft/tags/blocks/mineable/pickaxe.json +f4459900cde17548776562b1a6f3c12ccc86efa5 data/minecraft/tags/blocks/needs_iron_tool.json e1f71dcb4f9e7e36e29b0ad09d6520dc3adfa4a6 data/neoforge/tags/blocks/needs_wood_tool.json diff --git a/src/generated/resources/assets/advancedperipherals/blockstates/rs_bridge.json b/src/generated/resources/assets/advancedperipherals/blockstates/rs_bridge.json new file mode 100644 index 000000000..a1b1d565a --- /dev/null +++ b/src/generated/resources/assets/advancedperipherals/blockstates/rs_bridge.json @@ -0,0 +1,57 @@ +{ + "variants": { + "orientation=down_east": { + "model": "advancedperipherals:block/rs_bridge", + "x": 90, + "y": 90 + }, + "orientation=down_north": { + "model": "advancedperipherals:block/rs_bridge", + "x": 90 + }, + "orientation=down_south": { + "model": "advancedperipherals:block/rs_bridge", + "x": 90, + "y": 180 + }, + "orientation=down_west": { + "model": "advancedperipherals:block/rs_bridge", + "x": 90, + "y": 270 + }, + "orientation=east_up": { + "model": "advancedperipherals:block/rs_bridge", + "y": 90 + }, + "orientation=north_up": { + "model": "advancedperipherals:block/rs_bridge" + }, + "orientation=south_up": { + "model": "advancedperipherals:block/rs_bridge", + "y": 180 + }, + "orientation=up_east": { + "model": "advancedperipherals:block/rs_bridge", + "x": 270, + "y": 90 + }, + "orientation=up_north": { + "model": "advancedperipherals:block/rs_bridge", + "x": 270 + }, + "orientation=up_south": { + "model": "advancedperipherals:block/rs_bridge", + "x": 270, + "y": 180 + }, + "orientation=up_west": { + "model": "advancedperipherals:block/rs_bridge", + "x": 270, + "y": 270 + }, + "orientation=west_up": { + "model": "advancedperipherals:block/rs_bridge", + "y": 270 + } + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/advancedperipherals/lang/en_us.json b/src/generated/resources/assets/advancedperipherals/lang/en_us.json index 7ac6af552..ed66cc743 100644 --- a/src/generated/resources/assets/advancedperipherals/lang/en_us.json +++ b/src/generated/resources/assets/advancedperipherals/lang/en_us.json @@ -28,6 +28,7 @@ "block.advancedperipherals.peripheral_casing": "Peripheral Casing", "block.advancedperipherals.player_detector": "Player Detector", "block.advancedperipherals.redstone_integrator": "Redstone Integrator", + "block.advancedperipherals.rs_bridge": "RS Bridge", "entity.minecraft.villager.advancedperipherals.computer_scientist": "Computer Scientist", "item.advancedperipherals.chunk_controller": "Chunk Controller", "item.advancedperipherals.computer_tool": "Computer Tool", @@ -59,6 +60,7 @@ "item.advancedperipherals.tooltip.peripheral_casing": "&7An empty hull without the love it deserves. Used as a crafting ingredient", "item.advancedperipherals.tooltip.player_detector": "&7This peripheral can be used to interact with players, but don't be a stalker.", "item.advancedperipherals.tooltip.redstone_integrator": "&7This block is able to interact with redstone. Works exactly like the redstone api of an computer.", + "item.advancedperipherals.tooltip.rs_bridge": "&7The RS Bridge interacts with Refined Storage to manage your items.", "item.advancedperipherals.tooltip.show_desc": "&b[&7%s&b] &7For Description", "item.advancedperipherals.tooltip.weak_automata_core": "&7Upgrade for turtles, which makes turtles more useful.", "item.advancedperipherals.weak_automata_core": "Weak Automata Core", diff --git a/src/generated/resources/assets/advancedperipherals/models/block/rs_bridge.json b/src/generated/resources/assets/advancedperipherals/models/block/rs_bridge.json new file mode 100644 index 000000000..585be6406 --- /dev/null +++ b/src/generated/resources/assets/advancedperipherals/models/block/rs_bridge.json @@ -0,0 +1,9 @@ +{ + "parent": "minecraft:block/cube_all", + "textures": { + "all": "advancedperipherals:block/rs_bridge", + "north": "advancedperipherals:block/rs_bridge_front", + "particle": "advancedperipherals:block/rs_bridge_front", + "up": "advancedperipherals:block/rs_bridge_top" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/advancedperipherals/advancement/recipes/redstone/rs_bridge.json b/src/generated/resources/data/advancedperipherals/advancement/recipes/redstone/rs_bridge.json new file mode 100644 index 000000000..aff6bdd52 --- /dev/null +++ b/src/generated/resources/data/advancedperipherals/advancement/recipes/redstone/rs_bridge.json @@ -0,0 +1,38 @@ +{ + "neoforge:conditions": [ + { + "type": "neoforge:mod_loaded", + "modid": "refinedstorage" + } + ], + "parent": "minecraft:recipes/root", + "criteria": { + "has_item": { + "conditions": { + "items": [ + { + "items": "advancedperipherals:peripheral_casing" + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "advancedperipherals:rs_bridge" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_item" + ] + ], + "rewards": { + "recipes": [ + "advancedperipherals:rs_bridge" + ] + } +} \ No newline at end of file diff --git a/src/generated/resources/data/advancedperipherals/loot_table/blocks/rs_bridge.json b/src/generated/resources/data/advancedperipherals/loot_table/blocks/rs_bridge.json new file mode 100644 index 000000000..638de0eaf --- /dev/null +++ b/src/generated/resources/data/advancedperipherals/loot_table/blocks/rs_bridge.json @@ -0,0 +1,30 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "bonus_rolls": 0.0, + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ], + "entries": [ + { + "type": "minecraft:item", + "functions": [ + { + "function": "minecraft:copy_components", + "include": [ + "minecraft:custom_name" + ], + "source": "block_entity" + } + ], + "name": "advancedperipherals:rs_bridge" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "advancedperipherals:blocks/rs_bridge" +} \ No newline at end of file diff --git a/src/generated/resources/data/advancedperipherals/recipe/rs_bridge.json b/src/generated/resources/data/advancedperipherals/recipe/rs_bridge.json new file mode 100644 index 000000000..3d0952250 --- /dev/null +++ b/src/generated/resources/data/advancedperipherals/recipe/rs_bridge.json @@ -0,0 +1,39 @@ +{ + "neoforge:conditions": [ + { + "type": "neoforge:mod_loaded", + "modid": "refinedstorage" + } + ], + "type": "minecraft:crafting_shaped", + "category": "redstone", + "key": { + "C": { + "item": "advancedperipherals:peripheral_casing" + }, + "E": { + "tag": "refinedstorage:exporters" + }, + "I": { + "item": "refinedstorage:interface" + }, + "P": { + "item": "refinedstorage:advanced_processor" + }, + "R": { + "tag": "refinedstorage:importers" + }, + "X": { + "tag": "refinedstorage:external_storages" + } + }, + "pattern": [ + "PXP", + "ECR", + "PIP" + ], + "result": { + "count": 1, + "id": "advancedperipherals:rs_bridge" + } +} \ No newline at end of file diff --git a/src/generated/resources/data/minecraft/tags/blocks/mineable/pickaxe.json b/src/generated/resources/data/minecraft/tags/blocks/mineable/pickaxe.json index 7e45c1fe5..ce177bf6b 100644 --- a/src/generated/resources/data/minecraft/tags/blocks/mineable/pickaxe.json +++ b/src/generated/resources/data/minecraft/tags/blocks/mineable/pickaxe.json @@ -4,6 +4,7 @@ "advancedperipherals:chat_box", "advancedperipherals:player_detector", "advancedperipherals:me_bridge", + "advancedperipherals:rs_bridge", "advancedperipherals:energy_detector", "advancedperipherals:peripheral_casing", "advancedperipherals:inventory_manager", diff --git a/src/generated/resources/data/minecraft/tags/blocks/needs_iron_tool.json b/src/generated/resources/data/minecraft/tags/blocks/needs_iron_tool.json index 785a3def1..44fdcdc79 100644 --- a/src/generated/resources/data/minecraft/tags/blocks/needs_iron_tool.json +++ b/src/generated/resources/data/minecraft/tags/blocks/needs_iron_tool.json @@ -4,6 +4,7 @@ "advancedperipherals:chat_box", "advancedperipherals:player_detector", "advancedperipherals:me_bridge", + "advancedperipherals:rs_bridge", "advancedperipherals:energy_detector", "advancedperipherals:inventory_manager", "advancedperipherals:redstone_integrator", diff --git a/src/main/java/de/srendi/advancedperipherals/AdvancedPeripherals.java b/src/main/java/de/srendi/advancedperipherals/AdvancedPeripherals.java index 95a2f8441..8980ef8f6 100644 --- a/src/main/java/de/srendi/advancedperipherals/AdvancedPeripherals.java +++ b/src/main/java/de/srendi/advancedperipherals/AdvancedPeripherals.java @@ -3,6 +3,7 @@ import dan200.computercraft.api.peripheral.PeripheralCapability; import de.srendi.advancedperipherals.common.addons.APAddons; import de.srendi.advancedperipherals.common.addons.appliedenergistics.AppEngApi; +import de.srendi.advancedperipherals.common.addons.refinedstorage.RSApi; import de.srendi.advancedperipherals.common.blocks.base.ICapabilityProvider; import de.srendi.advancedperipherals.common.configuration.APConfig; import de.srendi.advancedperipherals.common.setup.Registration; @@ -49,6 +50,11 @@ public static void debug(String message, Level level) { LOGGER.log(level, "[DEBUG] {}", message); } + public static void debug(String message, Throwable throwable) { + if (APConfig.GENERAL_CONFIG.enableDebugMode.get()) + LOGGER.error("[DEBUG] " + message, throwable); + } + public static ResourceLocation getRL(String resource) { return ResourceLocation.fromNamespaceAndPath(MOD_ID, resource); } @@ -95,5 +101,7 @@ public void registerCapabilities(RegisterCapabilitiesEvent event) { if (APAddons.ae2Loaded) AppEngApi.registerCapabilities(event); + if (APAddons.refinedStorageLoaded) + RSApi.registerCapabilities(event); } } diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/APAddons.java b/src/main/java/de/srendi/advancedperipherals/common/addons/APAddons.java index ab1fabbc6..bfd2356b8 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/APAddons.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/APAddons.java @@ -10,16 +10,20 @@ public class APAddons { public static final String AE2_MODID = "ae2"; - public static final String APP_MEKANISTICS_MODID = "appmek"; public static final String CURIOS_MODID = "curios"; + public static final String REFINEDSTORAGE_MODID = "refinedstorage"; + public static final String REFINEDSTORAGE_MEKANISM_MODID = "refinedstorage_mekanism_integration"; + public static final String MEKANISM_MODID = "mekanism"; + public static final String APP_MEKANISTICS_MODID = "appmek"; public static final String MINECOLONIES_MODID = "minecolonies"; public static final String PATCHOULI_MODID = "patchouli"; public static final String POWAH_MODID = "powah"; - public static final String REFINEDSTORAGETWO_MODID = "refinedstorage2"; public static boolean ae2Loaded; public static boolean curiosLoaded; public static boolean refinedStorageLoaded; + public static boolean refinedStorageMekanismLoaded; + public static boolean mekanismLoaded; public static boolean appMekLoaded; public static boolean patchouliLoaded; public static boolean powahLoaded; @@ -31,12 +35,15 @@ private APAddons() { public static void setup() { ModList modList = ModList.get(); ae2Loaded = modList.isLoaded(AE2_MODID); + curiosLoaded = modList.isLoaded(CURIOS_MODID); + refinedStorageLoaded = modList.isLoaded(REFINEDSTORAGE_MODID); + refinedStorageMekanismLoaded = modList.isLoaded(REFINEDSTORAGE_MEKANISM_MODID); + mekanismLoaded = modList.isLoaded(MEKANISM_MODID); appMekLoaded = modList.isLoaded(APP_MEKANISTICS_MODID); curiosLoaded = modList.isLoaded(CURIOS_MODID); minecoloniesLoaded = modList.isLoaded(MINECOLONIES_MODID); patchouliLoaded = modList.isLoaded(PATCHOULI_MODID); powahLoaded = modList.isLoaded(POWAH_MODID); - refinedStorageLoaded = modList.isLoaded(REFINEDSTORAGETWO_MODID); } @SubscribeEvent diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/appliedenergistics/AppEngApi.java b/src/main/java/de/srendi/advancedperipherals/common/addons/appliedenergistics/AppEngApi.java index fb2d2d9ec..2f3c4de21 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/appliedenergistics/AppEngApi.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/appliedenergistics/AppEngApi.java @@ -165,7 +165,7 @@ public static Map getObjectFromStack(Pair { +public class MEBridgeEntityListener implements IGridNodeListener { - public static final MeBridgeEntityListener INSTANCE = new MeBridgeEntityListener(); + public static final MEBridgeEntityListener INSTANCE = new MEBridgeEntityListener(); @Override - public void onSaveChanges(MeBridgeEntity nodeOwner, IGridNode node) { + public void onSaveChanges(MEBridgeEntity nodeOwner, IGridNode node) { } } diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/appliedenergistics/MeFluidHandler.java b/src/main/java/de/srendi/advancedperipherals/common/addons/appliedenergistics/MEFluidHandler.java similarity index 88% rename from src/main/java/de/srendi/advancedperipherals/common/addons/appliedenergistics/MeFluidHandler.java rename to src/main/java/de/srendi/advancedperipherals/common/addons/appliedenergistics/MEFluidHandler.java index fb29729b1..1c554e442 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/appliedenergistics/MeFluidHandler.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/appliedenergistics/MEFluidHandler.java @@ -4,6 +4,7 @@ import appeng.api.networking.security.IActionSource; import appeng.api.stacks.AEFluidKey; import appeng.api.storage.MEStorage; +import de.srendi.advancedperipherals.common.addons.computercraft.peripheral.MEBridgePeripheral; import de.srendi.advancedperipherals.common.util.Pair; import de.srendi.advancedperipherals.common.util.inventory.FluidFilter; import de.srendi.advancedperipherals.common.util.inventory.IStorageSystemFluidHandler; @@ -12,16 +13,16 @@ /** * Used to transfer item between an inventory and the ME system. - * @see de.srendi.advancedperipherals.common.addons.computercraft.peripheral.MeBridgePeripheral + * @see MEBridgePeripheral */ -public class MeFluidHandler implements IStorageSystemFluidHandler { +public class MEFluidHandler implements IStorageSystemFluidHandler { @NotNull private final MEStorage storageMonitor; @NotNull private final IActionSource actionSource; - public MeFluidHandler(@NotNull MEStorage storageMonitor, @NotNull IActionSource actionSource) { + public MEFluidHandler(@NotNull MEStorage storageMonitor, @NotNull IActionSource actionSource) { this.storageMonitor = storageMonitor; this.actionSource = actionSource; } diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/appliedenergistics/MeItemHandler.java b/src/main/java/de/srendi/advancedperipherals/common/addons/appliedenergistics/MEItemHandler.java similarity index 88% rename from src/main/java/de/srendi/advancedperipherals/common/addons/appliedenergistics/MeItemHandler.java rename to src/main/java/de/srendi/advancedperipherals/common/addons/appliedenergistics/MEItemHandler.java index ef7c6f71d..cc806f907 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/appliedenergistics/MeItemHandler.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/appliedenergistics/MEItemHandler.java @@ -4,6 +4,7 @@ import appeng.api.networking.security.IActionSource; import appeng.api.stacks.AEItemKey; import appeng.api.storage.MEStorage; +import de.srendi.advancedperipherals.common.addons.computercraft.peripheral.MEBridgePeripheral; import de.srendi.advancedperipherals.common.util.Pair; import de.srendi.advancedperipherals.common.util.inventory.IStorageSystemItemHandler; import de.srendi.advancedperipherals.common.util.inventory.ItemFilter; @@ -13,16 +14,16 @@ /** * Used to transfer item between an inventory and the ME system. * - * @see de.srendi.advancedperipherals.common.addons.computercraft.peripheral.MeBridgePeripheral + * @see MEBridgePeripheral */ -public class MeItemHandler implements IStorageSystemItemHandler { +public class MEItemHandler implements IStorageSystemItemHandler { @NotNull private final MEStorage storageMonitor; @NotNull private final IActionSource actionSource; - public MeItemHandler(@NotNull MEStorage storageMonitor, @NotNull IActionSource actionSource) { + public MEItemHandler(@NotNull MEStorage storageMonitor, @NotNull IActionSource actionSource) { this.storageMonitor = storageMonitor; this.actionSource = actionSource; } diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/MeBridgePeripheral.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/MEBridgePeripheral.java similarity index 96% rename from src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/MeBridgePeripheral.java rename to src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/MEBridgePeripheral.java index 2897b031d..632024cbd 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/MeBridgePeripheral.java +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/MEBridgePeripheral.java @@ -14,10 +14,10 @@ import dan200.computercraft.api.peripheral.IComputerAccess; import de.srendi.advancedperipherals.common.addons.appliedenergistics.AppEngApi; import de.srendi.advancedperipherals.common.addons.appliedenergistics.CraftJob; -import de.srendi.advancedperipherals.common.addons.appliedenergistics.MeFluidHandler; -import de.srendi.advancedperipherals.common.addons.appliedenergistics.MeItemHandler; +import de.srendi.advancedperipherals.common.addons.appliedenergistics.MEFluidHandler; +import de.srendi.advancedperipherals.common.addons.appliedenergistics.MEItemHandler; import de.srendi.advancedperipherals.common.addons.computercraft.owner.BlockEntityPeripheralOwner; -import de.srendi.advancedperipherals.common.blocks.blockentities.MeBridgeEntity; +import de.srendi.advancedperipherals.common.blocks.blockentities.MEBridgeEntity; import de.srendi.advancedperipherals.common.configuration.APConfig; import de.srendi.advancedperipherals.common.util.Pair; import de.srendi.advancedperipherals.common.util.ServerWorker; @@ -35,13 +35,13 @@ import java.util.Iterator; import java.util.List; -public class MeBridgePeripheral extends BasePeripheral> { +public class MEBridgePeripheral extends BasePeripheral> { public static final String PERIPHERAL_TYPE = "meBridge"; - private final MeBridgeEntity tile; + private final MEBridgeEntity tile; private IGridNode node; - public MeBridgePeripheral(MeBridgeEntity tileEntity) { + public MEBridgePeripheral(MEBridgeEntity tileEntity) { super(PERIPHERAL_TYPE, new BlockEntityPeripheralOwner<>(tileEntity)); this.tile = tileEntity; this.node = tileEntity.getActionableNode(); @@ -69,7 +69,7 @@ private ICraftingService getCraftingService() { */ protected MethodResult exportToChest(@NotNull IArguments arguments, @Nullable IItemHandler targetInventory) throws LuaException { MEStorage monitor = AppEngApi.getMonitor(node); - MeItemHandler itemHandler = new MeItemHandler(monitor, tile); + MEItemHandler itemHandler = new MEItemHandler(monitor, tile); Pair filter = ItemFilter.parse(arguments.getTable(0)); if (filter.rightPresent()) @@ -90,7 +90,7 @@ protected MethodResult exportToChest(@NotNull IArguments arguments, @Nullable II */ protected MethodResult exportToTank(@NotNull IArguments arguments, @Nullable IFluidHandler targetTank) throws LuaException { MEStorage monitor = AppEngApi.getMonitor(node); - MeFluidHandler fluidHandler = new MeFluidHandler(monitor, tile); + MEFluidHandler fluidHandler = new MEFluidHandler(monitor, tile); Pair filter = FluidFilter.parse(arguments.getTable(0)); if (filter.rightPresent()) @@ -99,7 +99,7 @@ protected MethodResult exportToTank(@NotNull IArguments arguments, @Nullable IFl if (targetTank == null) return MethodResult.of(0, "Target Tank does not exist"); - return MethodResult.of(InventoryUtil.moveFluid(fluidHandler, targetTank, filter.getLeft()), null); + return MethodResult.of(FluidUtil.moveFluid(fluidHandler, targetTank, filter.getLeft()), null); } /** @@ -111,7 +111,7 @@ protected MethodResult exportToTank(@NotNull IArguments arguments, @Nullable IFl */ protected MethodResult importToME(@NotNull IArguments arguments, @Nullable IItemHandler targetInventory) throws LuaException { MEStorage monitor = AppEngApi.getMonitor(node); - MeItemHandler itemHandler = new MeItemHandler(monitor, tile); + MEItemHandler itemHandler = new MEItemHandler(monitor, tile); Pair filter = ItemFilter.parse(arguments.getTable(0)); if (filter.rightPresent()) @@ -132,7 +132,7 @@ protected MethodResult importToME(@NotNull IArguments arguments, @Nullable IItem */ protected MethodResult importToME(@NotNull IArguments arguments, @Nullable IFluidHandler targetTank) throws LuaException { MEStorage monitor = AppEngApi.getMonitor(node); - MeFluidHandler fluidHandler = new MeFluidHandler(monitor, tile); + MEFluidHandler fluidHandler = new MEFluidHandler(monitor, tile); Pair filter = FluidFilter.parse(arguments.getTable(0)); if (filter.rightPresent()) @@ -141,7 +141,7 @@ protected MethodResult importToME(@NotNull IArguments arguments, @Nullable IFlui if (targetTank == null) return MethodResult.of(0, "Target Tank does not exist"); - return MethodResult.of(InventoryUtil.moveFluid(targetTank, fluidHandler, filter.getLeft()), null); + return MethodResult.of(FluidUtil.moveFluid(targetTank, fluidHandler, filter.getLeft()), null); } private MethodResult notConnected() { diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/RSBridgePeripheral.java b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/RSBridgePeripheral.java new file mode 100644 index 000000000..da1a3c764 --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/computercraft/peripheral/RSBridgePeripheral.java @@ -0,0 +1,931 @@ +package de.srendi.advancedperipherals.common.addons.computercraft.peripheral; + +import com.refinedmods.refinedstorage.api.autocrafting.Pattern; +import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatus; +import com.refinedmods.refinedstorage.api.network.Network; +import com.refinedmods.refinedstorage.api.network.NetworkComponent; +import com.refinedmods.refinedstorage.api.network.autocrafting.AutocraftingNetworkComponent; +import com.refinedmods.refinedstorage.api.network.energy.EnergyNetworkComponent; +import com.refinedmods.refinedstorage.api.network.impl.node.AbstractNetworkNode; +import com.refinedmods.refinedstorage.api.resource.ResourceAmount; +import com.refinedmods.refinedstorage.common.support.resource.ItemResource; +import com.refinedmods.refinedstorage.mekanism.ChemicalResource; +import com.refinedmods.refinedstorage.neoforge.support.resource.VariantUtil; +import dan200.computercraft.api.lua.IArguments; +import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.lua.LuaFunction; +import dan200.computercraft.api.lua.MethodResult; +import dan200.computercraft.api.peripheral.IComputerAccess; +import dan200.computercraft.core.apis.TableHelper; +import de.srendi.advancedperipherals.common.addons.APAddons; +import de.srendi.advancedperipherals.common.addons.computercraft.owner.BlockEntityPeripheralOwner; +import de.srendi.advancedperipherals.common.addons.refinedstorage.RSApi; +import de.srendi.advancedperipherals.common.addons.refinedstorage.RSChemicalHandler; +import de.srendi.advancedperipherals.common.addons.refinedstorage.RSCraftJob; +import de.srendi.advancedperipherals.common.addons.refinedstorage.RSFluidHandler; +import de.srendi.advancedperipherals.common.addons.refinedstorage.RSItemHandler; +import de.srendi.advancedperipherals.common.addons.refinedstorage.RsStorageTypes; +import de.srendi.advancedperipherals.common.blocks.blockentities.RSBridgeEntity; +import de.srendi.advancedperipherals.common.configuration.APConfig; +import de.srendi.advancedperipherals.common.util.Pair; +import de.srendi.advancedperipherals.common.util.StatusConstants; +import de.srendi.advancedperipherals.common.util.inventory.ChemicalFilter; +import de.srendi.advancedperipherals.common.util.inventory.ChemicalUtil; +import de.srendi.advancedperipherals.common.util.inventory.FluidFilter; +import de.srendi.advancedperipherals.common.util.inventory.FluidUtil; +import de.srendi.advancedperipherals.common.util.inventory.GenericFilter; +import de.srendi.advancedperipherals.common.util.inventory.IStorageSystemPeripheral; +import de.srendi.advancedperipherals.common.util.inventory.InventoryUtil; +import de.srendi.advancedperipherals.common.util.inventory.ItemFilter; +import de.srendi.advancedperipherals.lib.peripherals.BasePeripheral; +import mekanism.api.chemical.IChemicalHandler; +import net.neoforged.neoforge.fluids.capability.IFluidHandler; +import net.neoforged.neoforge.items.IItemHandler; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class RSBridgePeripheral extends BasePeripheral> implements IStorageSystemPeripheral { + + public static final String PERIPHERAL_TYPE = "rsBridge"; + + private final RSBridgeEntity bridge; + + public RSBridgePeripheral(RSBridgeEntity owner) { + super(PERIPHERAL_TYPE, new BlockEntityPeripheralOwner<>(owner)); + this.bridge = owner; + } + + @Override + public boolean isEnabled() { + return APAddons.refinedStorageLoaded && APConfig.PERIPHERALS_CONFIG.enableRSBridge.get(); + } + + private AbstractNetworkNode getNode() { + return (AbstractNetworkNode) owner.tileEntity.getNode(); + } + + private Network getNetwork() { + return getNode().getNetwork(); + } + + private MethodResult notConnected(Object defaultValue) { + return MethodResult.of(defaultValue, "NOT_CONNECTED"); + } + + private I getComponent(@NotNull Class componentClass) { + return getNetwork().getComponent(componentClass); + } + + private boolean isAvailable() { + return true; + } + + /** + * exports an item out of the system to a valid inventory + * + * @param arguments the arguments given by the computer + * @param targetInventory the give inventory + * @return the exportable amount or null with a string if something went wrong + */ + protected MethodResult exportToChest(@NotNull IArguments arguments, @Nullable IItemHandler targetInventory) throws LuaException { + RSItemHandler itemHandler = new RSItemHandler(getNetwork()); + Pair filter = ItemFilter.parse(arguments.getTable(0)); + + if (filter.rightPresent()) + return MethodResult.of(0, filter.getRight()); + + if (targetInventory == null) + return MethodResult.of(0, "Target Inventory does not exist"); + + return MethodResult.of(InventoryUtil.moveItem(itemHandler, targetInventory, filter.getLeft())); + } + + /** + * exports a fluid out of the system to a valid tank + * + * @param arguments the arguments given by the computer + * @param targetTank the give tank + * @return the exportable amount or null with a string if something went wrong + */ + protected MethodResult exportToTank(@NotNull IArguments arguments, @Nullable IFluidHandler targetTank) throws LuaException { + RSFluidHandler fluidHandler = new RSFluidHandler(getNetwork()); + Pair filter = FluidFilter.parse(arguments.getTable(0)); + + if (filter.rightPresent()) + return MethodResult.of(0, filter.getRight()); + + if (targetTank == null) + return MethodResult.of(0, "Target Tank does not exist"); + + return MethodResult.of(FluidUtil.moveFluid(fluidHandler, targetTank, filter.getLeft())); + } + + /** + * exports a fluid out of the system to a valid tank + * + * @param arguments the arguments given by the computer + * @param targetTank the give tank + * @return the exportable amount or null with a string if something went wrong + */ + protected MethodResult exportToTank(@NotNull IArguments arguments, @Nullable IChemicalHandler targetTank) throws LuaException { + RSChemicalHandler chemicalHandler = new RSChemicalHandler(getNetwork()); + Pair filter = ChemicalFilter.parse(arguments.getTable(0)); + + if (filter.rightPresent()) + return MethodResult.of(0, filter.getRight()); + + if (targetTank == null) + return MethodResult.of(0, "Target Tank does not exist"); + + return MethodResult.of(ChemicalUtil.moveChemical(chemicalHandler, targetTank, filter.getLeft())); + } + + /** + * imports an item to the system from a valid inventory + * + * @param arguments the arguments given by the computer + * @param targetInventory the give inventory + * @return the imported amount or null with a string if something went wrong + */ + protected MethodResult importToRS(@NotNull IArguments arguments, @Nullable IItemHandler targetInventory) throws LuaException { + RSItemHandler itemHandler = new RSItemHandler(getNetwork()); + Pair filter = ItemFilter.parse(arguments.getTable(0)); + + if (filter.rightPresent()) + return MethodResult.of(0, filter.getRight()); + + if (targetInventory == null) + return MethodResult.of(0, "Target Inventory does not exist"); + + return MethodResult.of(InventoryUtil.moveItem(targetInventory, itemHandler, filter.getLeft())); + } + + /** + * imports a fluid to the system from a valid tank + * + * @param arguments the arguments given by the computer + * @param targetTank the give tank + * @return the imported amount or null with a string if something went wrong + */ + protected MethodResult importToRS(@NotNull IArguments arguments, @Nullable IFluidHandler targetTank) throws LuaException { + RSFluidHandler fluidHandler = new RSFluidHandler(getNetwork()); + Pair filter = FluidFilter.parse(arguments.getTable(0)); + + if (filter.rightPresent()) + return MethodResult.of(0, filter.getRight()); + + if (targetTank == null) + return MethodResult.of(0, "Target Tank does not exist"); + + return MethodResult.of(FluidUtil.moveFluid(targetTank, fluidHandler, filter.getLeft())); + } + + /** + * imports a fluid to the system from a valid tank + * + * @param arguments the arguments given by the computer + * @param targetTank the give tank + * @return the imported amount or null with a string if something went wrong + */ + protected MethodResult importToRS(@NotNull IArguments arguments, @Nullable IChemicalHandler targetTank) throws LuaException { + RSChemicalHandler chemicalHandler = new RSChemicalHandler(getNetwork()); + Pair filter = ChemicalFilter.parse(arguments.getTable(0)); + + if (filter.rightPresent()) + return MethodResult.of(0, filter.getRight()); + + if (targetTank == null) + return MethodResult.of(0, "Target Tank does not exist"); + + return MethodResult.of(ChemicalUtil.moveChemical(targetTank, chemicalHandler, filter.getLeft())); + } + + @Override + @LuaFunction(mainThread = true) + public final boolean isConnected() { + return isAvailable(); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult isOnline() { + if (!isAvailable()) + return notConnected(false); + + return MethodResult.of(getComponent(EnergyNetworkComponent.class).getStored() > 0); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult getItem(IArguments arguments) throws LuaException { + if (!isAvailable()) + return notConnected(null); + + Pair filter = ItemFilter.parse(arguments.getTable(0)); + if (filter.rightPresent()) + return MethodResult.of(null, filter.getRight()); + + ItemFilter parsedFilter = filter.getLeft(); + if (parsedFilter.isEmpty()) + return MethodResult.of(null, "EMPTY_FILTER"); + + Map resourceProperties = RSApi.getParsedItem(getNetwork(), parsedFilter); + if (resourceProperties == null) + return MethodResult.of(null, "NOT_FOUND"); + + return MethodResult.of(resourceProperties); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult getFluid(IArguments arguments) throws LuaException { + if (!isAvailable()) + return notConnected(null); + + Pair filter = FluidFilter.parse(arguments.getTable(0)); + if (filter.rightPresent()) + return MethodResult.of(null, filter.getRight()); + + FluidFilter parsedFilter = filter.getLeft(); + if (parsedFilter.isEmpty()) + return MethodResult.of(null, "EMPTY_FILTER"); + + Map resourceProperties = RSApi.getParsedFluid(getNetwork(), parsedFilter); + if (resourceProperties == null) + return MethodResult.of(null, "NOT_FOUND"); + + return MethodResult.of(resourceProperties); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult getChemical(IArguments arguments) throws LuaException { + if (!isAvailable()) + return notConnected(null); + + if (!APAddons.refinedStorageMekanismLoaded) + return MethodResult.of(null, StatusConstants.ADDON_NOT_LOADED.withInfo("RS_MEKANISM")); + + Pair filter = ChemicalFilter.parse(arguments.getTable(0)); + if (filter.rightPresent()) + return MethodResult.of(null, filter.getRight()); + + ChemicalFilter parsedFilter = filter.getLeft(); + if (parsedFilter.isEmpty()) + return MethodResult.of(null, "EMPTY_FILTER"); + + Map resourceProperties = RSApi.getParsedChemical(getNetwork(), parsedFilter); + if (resourceProperties == null) + return MethodResult.of(null, "NOT_FOUND"); + + return MethodResult.of(resourceProperties); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult listItems(IArguments arguments) throws LuaException { + if (!isAvailable()) + return notConnected(null); + + Pair filter = ItemFilter.parse(arguments.optTable(0, Collections.emptyMap())); + if (filter.rightPresent()) + return MethodResult.of(null, filter.getRight()); + + ItemFilter parsedFilter = filter.getLeft(); + + List> resourceProperties = RSApi.getParsedItems(getNetwork(), parsedFilter); + + return MethodResult.of(resourceProperties); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult listFluids(IArguments arguments) throws LuaException { + if (!isAvailable()) + return notConnected(null); + + Pair filter = FluidFilter.parse(arguments.optTable(0, Collections.emptyMap())); + if (filter.rightPresent()) + return MethodResult.of(null, filter.getRight()); + + FluidFilter parsedFilter = filter.getLeft(); + + List> resourceProperties = RSApi.getParsedFluids(getNetwork(), parsedFilter); + + return MethodResult.of(resourceProperties); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult listChemicals(IArguments arguments) throws LuaException { + if (!isAvailable()) + return notConnected(null); + + if (!APAddons.refinedStorageMekanismLoaded) + return MethodResult.of(null, StatusConstants.ADDON_NOT_LOADED.withInfo("RS_MEKANISM")); + + Pair filter = ChemicalFilter.parse(arguments.optTable(0, Collections.emptyMap())); + if (filter.rightPresent()) + return MethodResult.of(null, filter.getRight()); + + ChemicalFilter parsedFilter = filter.getLeft(); + + List> resourceProperties = RSApi.getParsedChemicals(getNetwork(), parsedFilter); + + return MethodResult.of(resourceProperties); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult listCraftableItems(IArguments arguments) throws LuaException { + if (!isAvailable()) + return notConnected(null); + + Pair filter = ItemFilter.parse(arguments.optTable(0, Collections.emptyMap())); + if (filter.rightPresent()) + return MethodResult.of(null, filter.getRight()); + + ItemFilter parsedFilter = filter.getLeft(); + + List> resourceProperties = RSApi.getCraftableItems(getNetwork(), parsedFilter); + + return MethodResult.of(resourceProperties); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult listCraftableFluids(IArguments arguments) throws LuaException { + if (!isAvailable()) + return notConnected(null); + + Pair filter = FluidFilter.parse(arguments.optTable(0, Collections.emptyMap())); + if (filter.rightPresent()) + return MethodResult.of(null, filter.getRight()); + + FluidFilter parsedFilter = filter.getLeft(); + + List> resourceProperties = RSApi.getCraftableFluids(getNetwork(), parsedFilter); + + return MethodResult.of(resourceProperties); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult listCraftableChemicals(IArguments arguments) throws LuaException { + if (!isAvailable()) + return notConnected(null); + + if (!APAddons.refinedStorageMekanismLoaded) + return MethodResult.of(null, StatusConstants.ADDON_NOT_LOADED.withInfo("RS_MEKANISM")); + + Pair filter = ChemicalFilter.parse(arguments.optTable(0, Collections.emptyMap())); + if (filter.rightPresent()) + return MethodResult.of(null, filter.getRight()); + + ChemicalFilter parsedFilter = filter.getLeft(); + + List> resourceProperties = RSApi.getCraftableChemicals(getNetwork(), parsedFilter); + + return MethodResult.of(resourceProperties); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult listCells() { + if (!isAvailable()) + return notConnected(null); + + return MethodResult.of(RSApi.listCells(getNetwork())); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult listDrives() { + if (!isAvailable()) + return notConnected(null); + + return MethodResult.of(RSApi.listDrives(getNetwork())); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult importItem(IComputerAccess computer, IArguments arguments) throws LuaException { + if (!isAvailable()) + return notConnected(0); + + IItemHandler inventory = InventoryUtil.getHandlerFromDirection(arguments.getString(1), owner); + + if (inventory == null) { + inventory = InventoryUtil.getHandlerFromName(computer, arguments.getString(1)); + if (inventory == null) { + return MethodResult.of(0, StatusConstants.INVENTORY_NOT_FOUND.name()); + } + } + + return importToRS(arguments, inventory); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult exportItem(IComputerAccess computer, IArguments arguments) throws LuaException { + if (!isAvailable()) + return notConnected(0); + + IItemHandler inventory = InventoryUtil.getHandlerFromDirection(arguments.getString(1), owner); + + if (inventory == null) { + inventory = InventoryUtil.getHandlerFromName(computer, arguments.getString(1)); + if (inventory == null) { + return MethodResult.of(0, StatusConstants.INVENTORY_NOT_FOUND.name()); + } + } + + return exportToChest(arguments, inventory); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult importFluid(IComputerAccess computer, IArguments arguments) throws LuaException { + if (!isAvailable()) + return notConnected(0); + + IFluidHandler handler = FluidUtil.getHandlerFromDirection(arguments.getString(1), owner); + if (handler == null) { + handler = FluidUtil.getHandlerFromName(computer, arguments.getString(1)); + if (handler == null) { + return MethodResult.of(0, StatusConstants.INVENTORY_NOT_FOUND.name()); + } + } + + return importToRS(arguments, handler); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult exportFluid(IComputerAccess computer, IArguments arguments) throws LuaException { + if (!isAvailable()) + return notConnected(0); + + IFluidHandler handler = FluidUtil.getHandlerFromDirection(arguments.getString(1), owner); + if (handler == null) { + handler = FluidUtil.getHandlerFromName(computer, arguments.getString(1)); + if (handler == null) { + return MethodResult.of(0, StatusConstants.INVENTORY_NOT_FOUND.name()); + } + } + + return exportToTank(arguments, handler); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult importChemical(IComputerAccess computer, IArguments arguments) throws LuaException { + if (!isAvailable()) + return notConnected(0); + + if (!APAddons.refinedStorageMekanismLoaded) + return MethodResult.of(0, StatusConstants.ADDON_NOT_LOADED.withInfo("RS_MEKANISM")); + + IChemicalHandler handler = ChemicalUtil.getHandlerFromDirection(arguments.getString(1), owner); + if (handler == null) { + handler = ChemicalUtil.getHandlerFromName(computer, arguments.getString(1)); + if (handler == null) { + return MethodResult.of(0, StatusConstants.INVENTORY_NOT_FOUND.name()); + } + } + + return importToRS(arguments, handler); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult exportChemical(IComputerAccess computer, IArguments arguments) throws LuaException { + if (!isAvailable()) + return notConnected(0); + + if (!APAddons.refinedStorageMekanismLoaded) + return MethodResult.of(0, StatusConstants.ADDON_NOT_LOADED.withInfo("RS_MEKANISM")); + + IChemicalHandler handler = ChemicalUtil.getHandlerFromDirection(arguments.getString(1), owner); + if (handler == null) { + handler = ChemicalUtil.getHandlerFromName(computer, arguments.getString(1)); + if (handler == null) { + return MethodResult.of(0, StatusConstants.INVENTORY_NOT_FOUND.name()); + } + } + + return exportToTank(arguments, handler); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult getStoredEnergy() { + if (!isAvailable()) + return notConnected(0); + + EnergyNetworkComponent energyComponent = getNetwork().getComponent(EnergyNetworkComponent.class); + + return MethodResult.of(energyComponent.getStored()); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult getEnergyCapacity() { + if (!isAvailable()) + return notConnected(0); + + EnergyNetworkComponent energyComponent = getNetwork().getComponent(EnergyNetworkComponent.class); + + return MethodResult.of(energyComponent.getCapacity()); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult getEnergyUsage() { + if (!isAvailable()) + return notConnected(0); + + return MethodResult.of(RSApi.getEnergyUsage(getNetwork())); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult getAverageEnergyInput() { + if (!isAvailable()) + return notConnected(0); + + // Not supported for Refined Storage + return MethodResult.of(0); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult getTotalExternalItemStorage() { + if (!isAvailable()) + return notConnected(0); + + return MethodResult.of(RSApi.getTotalExternalStorage(getNetwork(), RsStorageTypes.ITEM)); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult getTotalExternalFluidStorage() { + if (!isAvailable()) + return notConnected(0); + + return MethodResult.of(RSApi.getTotalExternalStorage(getNetwork(), RsStorageTypes.FLUID)); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult getTotalExternalChemicalStorage() { + if (!isAvailable()) + return notConnected(0); + + if (!APAddons.refinedStorageMekanismLoaded) + return MethodResult.of(0, StatusConstants.ADDON_NOT_LOADED.withInfo("RS_MEKANISM")); + + return MethodResult.of(RSApi.getTotalExternalStorage(getNetwork(), RsStorageTypes.CHEMICAL)); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult getTotalItemStorage() { + if (!isAvailable()) + return notConnected(0); + + return MethodResult.of(RSApi.getTotalStorage(getNetwork(), RsStorageTypes.ITEM)); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult getTotalFluidStorage() { + if (!isAvailable()) + return notConnected(0); + + return MethodResult.of(RSApi.getTotalStorage(getNetwork(), RsStorageTypes.FLUID)); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult getTotalChemicalStorage() { + if (!isAvailable()) + return notConnected(0); + + if (!APAddons.refinedStorageMekanismLoaded) + return MethodResult.of(0, StatusConstants.ADDON_NOT_LOADED.withInfo("RS_MEKANISM")); + + return MethodResult.of(RSApi.getTotalStorage(getNetwork(), RsStorageTypes.CHEMICAL)); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult getUsedExternalItemStorage() { + if (!isAvailable()) + return notConnected(0); + + return MethodResult.of(RSApi.getUsedExternalStorage(getNetwork(), RsStorageTypes.ITEM)); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult getUsedExternalFluidStorage() { + if (!isAvailable()) + return notConnected(0); + + return MethodResult.of(RSApi.getUsedExternalStorage(getNetwork(), RsStorageTypes.FLUID)); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult getUsedExternalChemicalStorage() { + if (!isAvailable()) + return notConnected(0); + + if (!APAddons.refinedStorageMekanismLoaded) + return MethodResult.of(0, StatusConstants.ADDON_NOT_LOADED.withInfo("RS_MEKANISM")); + + return MethodResult.of(RSApi.getUsedExternalStorage(getNetwork(), RsStorageTypes.CHEMICAL)); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult getUsedItemStorage() { + if (!isAvailable()) + return notConnected(0); + + return MethodResult.of(RSApi.getUsedStorage(getNetwork(), RsStorageTypes.ITEM)); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult getUsedFluidStorage() { + if (!isAvailable()) + return notConnected(0); + + return MethodResult.of(RSApi.getUsedStorage(getNetwork(), RsStorageTypes.FLUID)); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult getUsedChemicalStorage() { + if (!isAvailable()) + return notConnected(0); + + if (!APAddons.refinedStorageMekanismLoaded) + return MethodResult.of(0, StatusConstants.ADDON_NOT_LOADED.withInfo("RS_MEKANISM")); + + return MethodResult.of(RSApi.getUsedStorage(getNetwork(), RsStorageTypes.CHEMICAL)); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult getAvailableExternalItemStorage() { + if (!isAvailable()) + return notConnected(0); + + return MethodResult.of(RSApi.getTotalExternalStorage(getNetwork(), RsStorageTypes.ITEM) - RSApi.getUsedExternalStorage(getNetwork(), RsStorageTypes.ITEM)); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult getAvailableExternalFluidStorage() { + if (!isAvailable()) + return notConnected(0); + + return MethodResult.of(RSApi.getTotalExternalStorage(getNetwork(), RsStorageTypes.FLUID) - RSApi.getUsedExternalStorage(getNetwork(), RsStorageTypes.FLUID)); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult getAvailableExternalChemicalStorage() { + if (!isAvailable()) + return notConnected(0); + + if (!APAddons.refinedStorageMekanismLoaded) + return MethodResult.of(0, StatusConstants.ADDON_NOT_LOADED.withInfo("RS_MEKANISM")); + + return MethodResult.of(RSApi.getTotalExternalStorage(getNetwork(), RsStorageTypes.CHEMICAL) - RSApi.getUsedExternalStorage(getNetwork(), RsStorageTypes.CHEMICAL)); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult getAvailableItemStorage() { + if (!isAvailable()) + return notConnected(0); + + return MethodResult.of(RSApi.getTotalStorage(getNetwork(), RsStorageTypes.ITEM) - RSApi.getUsedStorage(getNetwork(), RsStorageTypes.ITEM)); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult getAvailableFluidStorage() { + if (!isAvailable()) + return notConnected(0); + + return MethodResult.of(RSApi.getTotalStorage(getNetwork(), RsStorageTypes.FLUID) - RSApi.getUsedStorage(getNetwork(), RsStorageTypes.FLUID)); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult getAvailableChemicalStorage() { + if (!isAvailable()) + return notConnected(0); + + if (!APAddons.refinedStorageMekanismLoaded) + return MethodResult.of(0, StatusConstants.ADDON_NOT_LOADED.withInfo("RS_MEKANISM")); + + return MethodResult.of(RSApi.getTotalStorage(getNetwork(), RsStorageTypes.CHEMICAL) - RSApi.getUsedStorage(getNetwork(), RsStorageTypes.CHEMICAL)); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult craftItem(IComputerAccess computer, IArguments arguments) throws LuaException { + if (!isAvailable()) + return notConnected(null); + + Pair filter = ItemFilter.parse(arguments.getTable(0)); + if (filter.rightPresent()) + return MethodResult.of(null, filter.getRight()); + + RSCraftJob job = new RSCraftJob(computer, getLevel(), filter.getLeft().getCount(), ItemResource.ofItemStack(filter.getLeft().toItemStack()), getNetwork().getComponent(AutocraftingNetworkComponent.class)); + bridge.addJob(job); + return MethodResult.of(job); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult craftFluid(IComputerAccess computer, IArguments arguments) throws LuaException { + if (!isAvailable()) + return notConnected(null); + + Pair filter = FluidFilter.parse(arguments.getTable(0)); + if (filter.rightPresent()) + return MethodResult.of(null, filter.getRight()); + + RSCraftJob job = new RSCraftJob(computer, getLevel(), filter.getLeft().getCount(), VariantUtil.ofFluidStack(filter.getLeft().toFluidStack()), getNetwork().getComponent(AutocraftingNetworkComponent.class)); + bridge.addJob(job); + return MethodResult.of(job); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult craftChemical(IComputerAccess computer, IArguments arguments) throws LuaException { + if (!isAvailable()) + return notConnected(null); + + if (!APAddons.refinedStorageMekanismLoaded) + return MethodResult.of(null, StatusConstants.ADDON_NOT_LOADED.withInfo("RS_MEKANISM")); + + Pair filter = ChemicalFilter.parse(arguments.getTable(0)); + if (filter.rightPresent()) + return MethodResult.of(null, filter.getRight()); + + RSCraftJob job = new RSCraftJob(computer, getLevel(), filter.getLeft().getCount(), ChemicalResource.ofChemicalStack(filter.getLeft().toChemicalStack()), getNetwork().getComponent(AutocraftingNetworkComponent.class)); + bridge.addJob(job); + return MethodResult.of(job); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult getCraftingTasks() { + if (!isAvailable()) + return notConnected(null); + + return MethodResult.of(RSApi.getCraftingTasks(getNetwork(), bridge)); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult getCraftingTask(int id) { + if (!isAvailable()) + return notConnected(null); + + for (RSCraftJob job : bridge.getJobs()) { + if (job.getId() == id) { + return MethodResult.of(job); + } + } + return MethodResult.of(null, StatusConstants.NOT_FOUND); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult cancelCraftingTasks(IArguments arguments) throws LuaException { + if (!isAvailable()) + return notConnected(0); + + Pair, String> filter = GenericFilter.parseGeneric(arguments.getTable(0)); + if (filter.getRight() != null) + return MethodResult.of(0, filter.getRight()); + + GenericFilter parsedFilter = filter.getLeft(); + + AutocraftingNetworkComponent craftingManager = getComponent(AutocraftingNetworkComponent.class); + int canceled = 0; + + for (TaskStatus status : craftingManager.getStatuses()) { + if (parsedFilter.testRS(new ResourceAmount(status.info().resource(), 1))) { + craftingManager.cancel(status.info().id()); + canceled++; + } + } + + return MethodResult.of(canceled); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult isCraftable(IArguments arguments) throws LuaException { + if (!isAvailable()) + return notConnected(false); + + Pair, String> filter = GenericFilter.parseGeneric(arguments.getTable(0)); + if (filter.getRight() != null) + return MethodResult.of(false, filter.getRight()); + + GenericFilter parsedFilter = filter.getLeft(); + + return MethodResult.of(RSApi.findPatternFromFilters(getNetwork(), null, parsedFilter).leftPresent()); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult isCrafting(IArguments arguments) throws LuaException { + if (!isAvailable()) + return notConnected(false); + + Pair, String> filter = GenericFilter.parseGeneric(arguments.getTable(0)); + if (filter.getRight() != null) + return MethodResult.of(false, filter.getRight()); + + GenericFilter parsedFilter = filter.getLeft(); + + AutocraftingNetworkComponent craftingManager = getComponent(AutocraftingNetworkComponent.class); + + for (TaskStatus status : craftingManager.getStatuses()) { + if (parsedFilter.testRS(new ResourceAmount(status.info().resource(), 1))) { + return MethodResult.of(true); + } + } + + return MethodResult.of(false); + } + + @Override + @LuaFunction(mainThread = true) + public final MethodResult getPatterns(IArguments arguments) throws LuaException { + if (!isAvailable()) + return notConnected(null); + + // Expected input is a table with either an input table, an output table or both to filter for both + // If no table is provided or it's empty, return every pattern + Map filterTable = arguments.optTable(0, Collections.emptyMap()); + if (filterTable.isEmpty()) { + return MethodResult.of(RSApi.getPatterns(getNetwork())); + } + + boolean hasInputFilter = filterTable.containsKey("input"); + boolean hasOutputFilter = filterTable.containsKey("output"); + boolean hasAnyFilter = hasInputFilter || hasOutputFilter; + + // If the player tries to filter for nothing, return nothing. + if (!hasAnyFilter) + return MethodResult.of(null, "NO_FILTER"); + + GenericFilter inputFilter = null; + GenericFilter outputFilter = null; + + if (hasInputFilter) { + Map inputFilterTable = TableHelper.getTableField(filterTable, "input"); + + Pair, String> parsedFilter = GenericFilter.parseGeneric(inputFilterTable); + + if (parsedFilter.rightPresent()) + return MethodResult.of(null, parsedFilter.getRight()); + + inputFilter = parsedFilter.getLeft(); + } + if (hasOutputFilter) { + Map outputFilterTable = TableHelper.getTableField(filterTable, "output"); + + Pair, String> parsedFilter = GenericFilter.parseGeneric(outputFilterTable); + + if (parsedFilter.rightPresent()) + return MethodResult.of(null, parsedFilter.getRight()); + + outputFilter = parsedFilter.getLeft(); + } + + Pair pattern = RSApi.findPatternFromFilters(getNetwork(), inputFilter, outputFilter); + + if (pattern.rightPresent()) + return MethodResult.of(null, pattern.getRight()); + + return MethodResult.of(RSApi.parsePattern(pattern.getLeft())); + } +} diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/refinedstorage/RSApi.java b/src/main/java/de/srendi/advancedperipherals/common/addons/refinedstorage/RSApi.java new file mode 100644 index 000000000..7613bd525 --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/refinedstorage/RSApi.java @@ -0,0 +1,785 @@ +package de.srendi.advancedperipherals.common.addons.refinedstorage; + +import com.refinedmods.refinedstorage.api.autocrafting.Ingredient; +import com.refinedmods.refinedstorage.api.autocrafting.Pattern; +import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatus; +import com.refinedmods.refinedstorage.api.network.Network; +import com.refinedmods.refinedstorage.api.network.autocrafting.AutocraftingNetworkComponent; +import com.refinedmods.refinedstorage.api.network.impl.node.AbstractNetworkNode; +import com.refinedmods.refinedstorage.api.network.impl.node.externalstorage.ExposedExternalStorage; +import com.refinedmods.refinedstorage.api.network.impl.node.externalstorage.ExternalStorageNetworkNode; +import com.refinedmods.refinedstorage.api.network.impl.node.storage.StorageNetworkNode; +import com.refinedmods.refinedstorage.api.network.impl.storage.StorageConfiguration; +import com.refinedmods.refinedstorage.api.network.node.GraphNetworkComponent; +import com.refinedmods.refinedstorage.api.network.storage.StorageNetworkComponent; +import com.refinedmods.refinedstorage.api.resource.ResourceAmount; +import com.refinedmods.refinedstorage.api.resource.ResourceKey; +import com.refinedmods.refinedstorage.api.storage.Actor; +import com.refinedmods.refinedstorage.api.storage.StateTrackedStorage; +import com.refinedmods.refinedstorage.api.storage.Storage; +import com.refinedmods.refinedstorage.api.storage.TrackedResourceAmount; +import com.refinedmods.refinedstorage.api.storage.composite.CompositeStorage; +import com.refinedmods.refinedstorage.common.api.storage.SerializableStorage; +import com.refinedmods.refinedstorage.common.api.support.network.InWorldNetworkNodeContainer; +import com.refinedmods.refinedstorage.common.api.support.resource.PlatformResourceKey; +import com.refinedmods.refinedstorage.common.storage.StorageTypes; +import com.refinedmods.refinedstorage.common.support.resource.FluidResource; +import com.refinedmods.refinedstorage.common.support.resource.ItemResource; +import com.refinedmods.refinedstorage.mekanism.ChemicalResource; +import com.refinedmods.refinedstorage.mekanism.ChemicalResourceType; +import com.refinedmods.refinedstorage.neoforge.api.RefinedStorageNeoForgeApi; +import com.refinedmods.refinedstorage.neoforge.support.resource.VariantUtil; +import de.srendi.advancedperipherals.AdvancedPeripherals; +import de.srendi.advancedperipherals.common.addons.APAddons; +import de.srendi.advancedperipherals.common.blocks.blockentities.RSBridgeEntity; +import de.srendi.advancedperipherals.common.setup.BlockEntityTypes; +import de.srendi.advancedperipherals.common.util.LuaConverter; +import de.srendi.advancedperipherals.common.util.Pair; +import de.srendi.advancedperipherals.common.util.inventory.ChemicalFilter; +import de.srendi.advancedperipherals.common.util.inventory.ChemicalUtil; +import de.srendi.advancedperipherals.common.util.inventory.FluidFilter; +import de.srendi.advancedperipherals.common.util.inventory.FluidUtil; +import de.srendi.advancedperipherals.common.util.inventory.GenericFilter; +import de.srendi.advancedperipherals.common.util.inventory.ItemFilter; +import de.srendi.advancedperipherals.common.util.inventory.ItemUtil; +import mekanism.api.MekanismAPI; +import mekanism.api.chemical.ChemicalStack; +import net.minecraft.world.item.ItemStack; +import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent; +import net.neoforged.neoforge.fluids.FluidStack; +import org.apache.logging.log4j.Level; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * Refined Storage Api helper methods and parsers + */ +public class RSApi { + + public static void registerCapabilities(@NotNull RegisterCapabilitiesEvent event) { + event.registerBlockEntity( + RefinedStorageNeoForgeApi.INSTANCE.getNetworkNodeContainerProviderCapability(), + BlockEntityTypes.RS_BRIDGE.get(), + (blockEntity, side) -> blockEntity); + } + + /** + * Returns the first item resource that fits to the filter + * + * @param network refined storage network + * @param filter item filter instance - can be an empty filter to get the first item of the system see {@link ItemFilter#createEmpty()} + * @return the first item in the system that fits the item filter or null + */ + @Nullable + public static ItemResource getItem(Network network, ItemFilter filter) { + StorageNetworkComponent storage = network.getComponent(StorageNetworkComponent.class); + for (TrackedResourceAmount trackedResource : storage.getResources(Actor.EMPTY.getClass())) { + if (trackedResource.resourceAmount().resource() instanceof ItemResource itemResource && filter.test(itemResource.toItemStack())) { + return itemResource; + } + } + return null; + } + + /** + * Returns the first fluid parsed to a lua object which fits to the filter + * + * @param network refined storage network + * @param filter fluid filter instance - can be an empty filter to get the first fluid of the system see {@link FluidFilter#createEmpty()} + * @return the first fluid in the system that fits the fluid filter or null + */ + @Nullable + public static FluidResource getFluid(Network network, FluidFilter filter) { + StorageNetworkComponent storage = network.getComponent(StorageNetworkComponent.class); + for (TrackedResourceAmount trackedResource : storage.getResources(Actor.EMPTY.getClass())) { + if (trackedResource.resourceAmount().resource() instanceof FluidResource fluidResource && filter.test(VariantUtil.toFluidStack(fluidResource, trackedResource.resourceAmount().amount()))) { + return fluidResource; + } + } + return null; + } + + /** + * Returns the first chemical parsed to a lua object which fits to the filter + * + * @param network refined storage network + * @param filter fluid filter instance - can be an empty filter to get the first fluid of the system see {@link FluidFilter#createEmpty()} + * @return the first fluid in the system that fits the fluid filter or null + */ + @Nullable + public static ChemicalResource getChemical(Network network, ChemicalFilter filter) { + StorageNetworkComponent storage = network.getComponent(StorageNetworkComponent.class); + for (TrackedResourceAmount trackedResource : storage.getResources(Actor.EMPTY.getClass())) { + if (trackedResource.resourceAmount().resource() instanceof ChemicalResource chemicalResource && filter.test(ChemicalUtil.toChemicalStack(chemicalResource.chemical(), trackedResource.resourceAmount().amount()))) { + return chemicalResource; + } + } + return null; + } + + /** + * Returns the first item parsed to a lua object which fits to the filter + * + * @param network refined storage network + * @param filter item filter instance - can be an empty filter to get the first item of the system see {@link ItemFilter#createEmpty()} + * @return the first item in the system that fits the item filter or null + */ + @Nullable + public static Map getParsedItem(Network network, ItemFilter filter) { + StorageNetworkComponent storage = network.getComponent(StorageNetworkComponent.class); + for (TrackedResourceAmount trackedResource : storage.getResources(Actor.EMPTY.getClass())) { + if (trackedResource.resourceAmount().resource() instanceof ItemResource itemResource && filter.test(itemResource.toItemStack())) { + return getObjectFromItemResource(trackedResource.resourceAmount()); + } + } + return null; + } + + /** + * Returns the first fluid parsed to a lua object which fits to the filter + * + * @param network refined storage network + * @param filter fluid filter instance - can be an empty filter to get the first fluid of the system see {@link FluidFilter#createEmpty()} + * @return the first fluid in the system that fits the fluid filter or null + */ + @Nullable + public static Map getParsedFluid(Network network, FluidFilter filter) { + StorageNetworkComponent storage = network.getComponent(StorageNetworkComponent.class); + for (TrackedResourceAmount trackedResource : storage.getResources(Actor.EMPTY.getClass())) { + if (trackedResource.resourceAmount().resource() instanceof FluidResource fluidResource && filter.test(VariantUtil.toFluidStack(fluidResource, trackedResource.resourceAmount().amount()))) { + return getObjectFromItemResource(trackedResource.resourceAmount()); + } + } + return null; + } + + /** + * Returns the first mekanism chemical parsed to a lua object which fits to the filter + * + * @param network refined storage network + * @param filter chemical filter instance - can be an empty filter to get the first chemical of the system see {@link ChemicalFilter#createEmpty()} + * @return the first chemical in the system that fits the chemical filter or null + */ + @Nullable + public static Map getParsedChemical(Network network, ChemicalFilter filter) { + StorageNetworkComponent storage = network.getComponent(StorageNetworkComponent.class); + for (TrackedResourceAmount trackedResource : storage.getResources(Actor.EMPTY.getClass())) { + if (trackedResource.resourceAmount().resource() instanceof ChemicalResource chemicalResource && filter.test(ChemicalUtil.toChemicalStack(chemicalResource.chemical(), trackedResource.resourceAmount().amount()))) { + return getObjectFromChemicalResource(trackedResource.resourceAmount()); + } + } + return null; + } + + /** + * Returns every item from the system while also checking if the filter test passes for the items + * The filter can be empty, see {@link ItemFilter#createEmpty()} + * + * @param network the rs network + * @param filter The filter here is optional, if an empty filter is provided, the method will return every resource + * @return a set of items + */ + public static List> getParsedItems(Network network, ItemFilter filter) { + List> items = new ArrayList<>(); + StorageNetworkComponent storage = network.getComponent(StorageNetworkComponent.class); + for (TrackedResourceAmount trackedResource : storage.getResources(Actor.EMPTY.getClass())) { + if (trackedResource.resourceAmount().resource() instanceof ItemResource itemResource && filter.test(itemResource.toItemStack())) { + items.add(getObjectFromItemResource(trackedResource.resourceAmount())); + } + } + return items; + } + + /** + * Returns every fluid from the system while also checking if the filter test passes for the fluids + * The filter can be empty, see {@link FluidFilter#createEmpty()} + * + * @param network the rs network + * @param filter The filter here is optional, if an empty filter is provided, the method will return every resource + * @return a set of fluid stacks + */ + public static List> getParsedFluids(Network network, FluidFilter filter) { + List> items = new ArrayList<>(); + StorageNetworkComponent storage = network.getComponent(StorageNetworkComponent.class); + for (TrackedResourceAmount trackedResource : storage.getResources(Actor.EMPTY.getClass())) { + if (trackedResource.resourceAmount().resource() instanceof FluidResource fluidResource && filter.test(VariantUtil.toFluidStack(fluidResource, trackedResource.resourceAmount().amount()))) { + items.add(getObjectFromFluidResource(trackedResource.resourceAmount())); + } + } + + return items; + } + + /** + * Returns every fluid from the system while also checking if the filter test passes for the fluids + * The filter can be empty, see {@link FluidFilter#createEmpty()} + * + * @param network the rs network + * @param filter The filter here is optional, if an empty filter is provided, the method will return every resource + * @return a set of fluid stacks + */ + public static List> getParsedChemicals(Network network, ChemicalFilter filter) { + List> items = new ArrayList<>(); + StorageNetworkComponent storage = network.getComponent(StorageNetworkComponent.class); + for (TrackedResourceAmount trackedResource : storage.getResources(Actor.EMPTY.getClass())) { + if (trackedResource.resourceAmount().resource() instanceof ChemicalResource fluidResource && filter.test(ChemicalUtil.toChemicalStack(fluidResource.chemical(), trackedResource.resourceAmount().amount()))) { + items.add(getObjectFromFluidResource(trackedResource.resourceAmount())); + } + } + + return items; + } + + /** + * Returns every craftable item from the system while also checking if the filter test passes for the items + * The filter can be empty, see {@link ItemFilter#createEmpty()} + * + * @param network the rs network + * @param filter The filter here is optional, if an empty filter is provided, the method will return every resource + * @return a set of parsed item stacks + */ + public static List> getCraftableItems(Network network, ItemFilter filter) { + List> items = new ArrayList<>(); + AutocraftingNetworkComponent autocrafting = network.getComponent(AutocraftingNetworkComponent.class); + StorageNetworkComponent storage = network.getComponent(StorageNetworkComponent.class); + for (ResourceKey key : autocrafting.getOutputs()) { + if (key instanceof ItemResource itemResource && filter.test(itemResource.toItemStack())) { + items.add(getObjectFromResourceKey(key, storage.get(key))); + } + } + return items; + } + + /** + * Returns every craftable fluid from the system while also checking if the filter test passes for the fluids + * The filter can be empty, see {@link FluidFilter#createEmpty()} + * + * @param network the rs network + * @param filter The filter here is optional, if an empty filter is provided, the method will return every resource + * @return a set of parsed fluids stacks + */ + public static List> getCraftableFluids(Network network, FluidFilter filter) { + List> items = new ArrayList<>(); + AutocraftingNetworkComponent autocrafting = network.getComponent(AutocraftingNetworkComponent.class); + StorageNetworkComponent storage = network.getComponent(StorageNetworkComponent.class); + for (ResourceKey key : autocrafting.getOutputs()) { + long amount = storage.get(key); + if (key instanceof FluidResource fluidResource && filter.test(VariantUtil.toFluidStack(fluidResource, amount))) { + items.add(getObjectFromResourceKey(key, amount)); + } + } + return items; + } + + /** + * Returns every craftable mekanism chemical from the system while also checking if the filter test passes for the chemicals + * The filter can be empty, see {@link ChemicalFilter#createEmpty()} + * + * @param network the rs network + * @param filter The filter here is optional, if an empty filter is provided, the method will return every resource + * @return a set of parsed chemical stacks + */ + public static List> getCraftableChemicals(Network network, ChemicalFilter filter) { + List> items = new ArrayList<>(); + AutocraftingNetworkComponent autocrafting = network.getComponent(AutocraftingNetworkComponent.class); + StorageNetworkComponent storage = network.getComponent(StorageNetworkComponent.class); + for (ResourceKey key : autocrafting.getOutputs()) { + long amount = storage.get(key); + if (key instanceof ChemicalResource chemicalResource && filter.test(ChemicalUtil.toChemicalStack(chemicalResource.chemical(), amount))) { + items.add(getObjectFromResourceKey(key, amount)); + } + } + return items; + } + + public static List getPatterns(Network network) { + List patterns = new ArrayList<>(); + AutocraftingNetworkComponent autocrafting = network.getComponent(AutocraftingNetworkComponent.class); + + for (Pattern pattern : autocrafting.getPatterns()) { + patterns.add(parsePattern(pattern)); + } + + return patterns; + } + + public static List listCells(Network network) { + List disks = new ArrayList<>(); + + GraphNetworkComponent graphNetworkComponent = network.getComponent(GraphNetworkComponent.class); + for (InWorldNetworkNodeContainer nodeContainer : graphNetworkComponent.getContainers(InWorldNetworkNodeContainer.class)) { + if (!(nodeContainer.getNode() instanceof StorageNetworkNode storageNetworkNode)) { + continue; + } + CompositeStorage storage = (CompositeStorage) storageNetworkNode.getStorage(); + for (Storage disk : storage.getSources()) { + if (disk instanceof StateTrackedStorage stateTrackedStorage) { + disks.add(parseStorageDisk(stateTrackedStorage)); + } + } + } + return disks; + } + + public static List listDrives(Network network) { + List drives = new ArrayList<>(); + + GraphNetworkComponent graphNetworkComponent = network.getComponent(GraphNetworkComponent.class); + for (InWorldNetworkNodeContainer nodeContainer : graphNetworkComponent.getContainers(InWorldNetworkNodeContainer.class)) { + if (nodeContainer.getNode() instanceof StorageNetworkNode storageNetworkNode) { + drives.add(parseDiskDrive(storageNetworkNode, nodeContainer)); + } + } + return drives; + } + + /** + * Finds a pattern from filters. + * + * @param network The network to search patterns from. + * @param inputFilter The input filter to apply, can be null to ignore input filter. + * @param outputFilter The output filter to apply, can be null to ignore output filter. + * @return A Pair object containing the matched pattern and an error message if no pattern is found. + * The pattern can be null if no pattern is found. + * The error message is "NO_PATTERN_FOUND" if no pattern is found. + */ + public static Pair findPatternFromFilters(Network network, @Nullable GenericFilter inputFilter, @Nullable GenericFilter outputFilter) { + AutocraftingNetworkComponent autocraftingComponent = network.getComponent(AutocraftingNetworkComponent.class); + for (Pattern pattern : autocraftingComponent.getPatterns()) { + if (pattern.layout().ingredients().isEmpty()) + continue; + if (pattern.layout().outputs().isEmpty()) + continue; + + boolean inputMatch = false; + boolean outputMatch = false; + + if (inputFilter != null) { + OUTERLOOP: + for (Ingredient input : pattern.layout().ingredients()) { + for (ResourceKey possibleInput : input.inputs()) { + if (inputFilter.testRS(new ResourceAmount(possibleInput, 1))) { + inputMatch = true; + break OUTERLOOP; + } + } + } + } else { + inputMatch = true; + } + + if (outputFilter != null) { + for (ResourceAmount output : pattern.layout().outputs()) { + if (outputFilter.testRS(output)) { + outputMatch = true; + break; + } + } + } else { + outputMatch = true; + } + + if (inputMatch && outputMatch) + return Pair.of(pattern, null); + } + + return Pair.of(null, "NO_PATTERN_FOUND"); + } + + public static long getTotalStorage(Network network, RsStorageTypes type) { + long total = 0; + + GraphNetworkComponent graphNetworkComponent = network.getComponent(GraphNetworkComponent.class); + for (InWorldNetworkNodeContainer nodeContainer : graphNetworkComponent.getContainers(InWorldNetworkNodeContainer.class)) { + if (nodeContainer.getNode() instanceof StorageNetworkNode storageNetworkNode) { + CompositeStorage storage = (CompositeStorage) storageNetworkNode.getStorage(); + for (Storage disk : storage.getSources()) { + if (!(disk instanceof StateTrackedStorage stateTrackedStorage)) + continue; + + if (stateTrackedStorage.getDelegate() instanceof SerializableStorage serializableStorage) { + if (type.getStorageType() != null && serializableStorage.getType() != type.getStorageType()) + continue; + } + + total += stateTrackedStorage.getCapacity(); + } + } + } + return total; + } + + public static long getUsedStorage(Network network, RsStorageTypes type) { + long used = 0; + + GraphNetworkComponent graphNetworkComponent = network.getComponent(GraphNetworkComponent.class); + for (InWorldNetworkNodeContainer nodeContainer : graphNetworkComponent.getContainers(InWorldNetworkNodeContainer.class)) { + if (nodeContainer.getNode() instanceof StorageNetworkNode storageNetworkNode) { + CompositeStorage storage = (CompositeStorage) storageNetworkNode.getStorage(); + for (Storage disk : storage.getSources()) { + if (!(disk instanceof StateTrackedStorage stateTrackedStorage)) + continue; + + if (stateTrackedStorage.getDelegate() instanceof SerializableStorage serializableStorage) { + if (type.getStorageType() != null && serializableStorage.getType() != type.getStorageType()) + continue; + } + + used += stateTrackedStorage.getStored(); + } + } + } + return used; + } + + public static long getTotalExternalStorage(Network network, RsStorageTypes type) { + long total = 0; + + GraphNetworkComponent graphNetworkComponent = network.getComponent(GraphNetworkComponent.class); + for (InWorldNetworkNodeContainer nodeContainer : graphNetworkComponent.getContainers(InWorldNetworkNodeContainer.class)) { + if (nodeContainer.getNode() instanceof ExternalStorageNetworkNode storageNetworkNode) { + ExposedExternalStorage storage = (ExposedExternalStorage) storageNetworkNode.getStorage(); + // TODO - Waiting for next rs beta/milestone patch + } + + } + return total; + } + + public static long getUsedExternalStorage(Network network, RsStorageTypes type) { + long used = 0; + + // If the storage type if null, it just means that the supplier returns no storage type. That happens when an addon is not loaded + if (type.getStorageType() == null) { + return used; + } + + GraphNetworkComponent graphNetworkComponent = network.getComponent(GraphNetworkComponent.class); + for (InWorldNetworkNodeContainer nodeContainer : graphNetworkComponent.getContainers(InWorldNetworkNodeContainer.class)) { + if (nodeContainer.getNode() instanceof ExternalStorageNetworkNode storageNetworkNode) { + ExposedExternalStorage storage = (ExposedExternalStorage) storageNetworkNode.getStorage(); + + used += storage.getAll().stream().filter(amount -> type.getStorageType().isAllowed(amount.resource())).mapToLong(ResourceAmount::amount).sum(); + } + + } + return used; + } + + public static List getCraftingTasks(Network network, RSBridgeEntity entity) { + List tasks = new ArrayList<>(); + + AutocraftingNetworkComponent autocrafting = network.getComponent(AutocraftingNetworkComponent.class); + + OUTERLOOP: + for (TaskStatus status : autocrafting.getStatuses()) { + for (RSCraftJob task : entity.getJobs()) { + if (status.info().id().equals(task.getCraftingTask().info().id())) { + tasks.add(parseCraftingTask(task, status)); + continue OUTERLOOP; + } + } + tasks.add(parseCraftingTask(null, status)); + } + + return tasks; + } + + public static long getEnergyUsage(Network network) { + long energyUsage = 0; + + GraphNetworkComponent graphNetworkComponent = network.getComponent(GraphNetworkComponent.class); + for (InWorldNetworkNodeContainer nodeContainer : graphNetworkComponent.getContainers(InWorldNetworkNodeContainer.class)) { + if (nodeContainer.getNode() instanceof AbstractNetworkNode abstractNetworkNode) { + energyUsage += abstractNetworkNode.getEnergyUsage(); + } + } + + return energyUsage; + } + + public static Map getObjectFromResourceKey(@NotNull ResourceKey resource) { + return getObjectFromResourceKey(resource, 0); + } + + /** + * Helper method to get a parsed lua resource key. + * + * @param resource the resource + * @param count count of the resource - can be 0 and lower + * @return the parsed key to a lua properties map + */ + public static Map getObjectFromResourceKey(@NotNull ResourceKey resource, long count) { + // Resource amounts can't be zero or the client will just crash, + // So we need to check that and set it to one + boolean countZeroOrLower = count <= 0; + if (resource instanceof ItemResource) { + if (countZeroOrLower) { + return getObjectFromItemResource(new ResourceAmount(resource, 1), count); + } + return getObjectFromItemResource(new ResourceAmount(resource, count)); + } + if (resource instanceof FluidResource) { + if (countZeroOrLower) { + return getObjectFromFluidResource(new ResourceAmount(resource, 1), count); + } + return getObjectFromFluidResource(new ResourceAmount(resource, count)); + } + if (APAddons.refinedStorageMekanismLoaded && resource instanceof ChemicalResource) { + if (countZeroOrLower) { + return getObjectFromChemicalResource(new ResourceAmount(resource, 1), count); + } + return getObjectFromChemicalResource(new ResourceAmount(resource, count)); + } + AdvancedPeripherals.debug("Could not create table from unknown resource " + resource.getClass() + " - Report this to the maintainer of ap", Level.WARN); + return Collections.emptyMap(); + } + + /** + * Helper method to get a parsed lua resourceAmount key. Currently only suppors item and fluid resources + * + * @param resourceAmount the resourceAmount + * @return the parsed key to a lua properties map + */ + public static Map getObjectFromResourceAmount(@NotNull ResourceAmount resourceAmount) { + if (resourceAmount.resource() instanceof ItemResource) { + return getObjectFromItemResource(resourceAmount); + } + if (resourceAmount.resource() instanceof FluidResource) { + return getObjectFromFluidResource(resourceAmount); + } + if (APAddons.refinedStorageMekanismLoaded && resourceAmount.resource() instanceof ChemicalResource) { + return getObjectFromChemicalResource(resourceAmount); + } + AdvancedPeripherals.debug("Could not create table from unknown resourceAmount " + resourceAmount.getClass() + " - Report this to the maintainer of ap", Level.WARN); + return Collections.emptyMap(); + } + + /** + * Tries to compare two resources + * + * @return true if the registry key of the resources matches + */ + public static boolean isSameResource(ResourceKey resource, ResourceKey toCompare) { + if (resource == null || toCompare == null) { + return false; + } + + // If the resource types do not match, return false early + if (resource instanceof PlatformResourceKey platformResource && toCompare instanceof PlatformResourceKey toComparePlatformResource) { + if (platformResource.getResourceType() != toComparePlatformResource.getResourceType()) { + return false; + } + } + + if (resource instanceof ItemResource itemResource && toCompare instanceof ItemResource toCompareItemResource) { + return ItemUtil.getRegistryKey(itemResource.item()).equals(ItemUtil.getRegistryKey(toCompareItemResource.item())); + } + + if (resource instanceof FluidResource fluidResource && toCompare instanceof FluidResource toCompareFluidResource) { + return FluidUtil.getRegistryKey(fluidResource.fluid()).equals(FluidUtil.getRegistryKey(toCompareFluidResource.fluid())); + } + + if (APAddons.refinedStorageMekanismLoaded) { + if (resource instanceof ChemicalResource chemicalResource && toCompare instanceof ChemicalResource toCompareChemicalResource) { + return ChemicalUtil.getRegistryKey(chemicalResource.chemical()).equals(ChemicalUtil.getRegistryKey(toCompareChemicalResource.chemical())); + } + } + + return false; + } + + public static Object parseDiskDrive(StorageNetworkNode diskDrive, InWorldNetworkNodeContainer nodeContainer) { + Map properties = new HashMap<>(); + + StorageConfiguration storageConfiguration = diskDrive.getStorageConfiguration(); + CompositeStorage storage = (CompositeStorage) diskDrive.getStorage(); + + List disks = new ArrayList<>(); + for (Storage disk : storage.getSources()) { + if (!(disk instanceof StateTrackedStorage stateTrackedStorage)) + continue; + + disks.add(parseStorageDisk(stateTrackedStorage)); + } + + properties.put("used", diskDrive.getStored()); + properties.put("total", diskDrive.getCapacity()); + properties.put("disks", disks); + properties.put("mode", storageConfiguration.getFilterMode().toString()); + properties.put("access_type", storageConfiguration.getAccessMode().toString()); + properties.put("position", LuaConverter.posToObject(nodeContainer.getLocalPosition())); + properties.put("priority", nodeContainer.getPriority()); + + return properties; + } + + public static Object parseStorageDisk(StateTrackedStorage disk) { + Map properties = new HashMap<>(); + + properties.put("used", disk.getStored()); + properties.put("capacity", disk.getCapacity()); + properties.put("state", disk.getState().toString()); + if (disk.getDelegate() instanceof SerializableStorage serializableStorage) { + String type; + if (serializableStorage.getType() == StorageTypes.ITEM) { + type = "item"; + } else if (serializableStorage.getType() == StorageTypes.FLUID) { + type = "fluid"; + } else if (APAddons.refinedStorageMekanismLoaded && serializableStorage.getType() == ChemicalResourceType.STORAGE_TYPE) { + type = "chemical"; + } else { + type = "unknown"; + } + properties.put("type", type); + } + + return properties; + } + + public static Object parseCraftingTask(@Nullable RSCraftJob task, TaskStatus status) { + Map properties = new HashMap<>(); + + properties.put("bridge_id", task == null ? -1 : task.getId()); + properties.put("id", status.info().id().toString()); + properties.put("quantity", status.info().amount()); + // The "stored" attribute of the Item does not always work. I guess this is a bug, I at least reported it. + // So currently we just calculate it by subtracting the currently crafting from the requested amount + properties.put("crafted", status.items().stream().filter(item -> isSameResource(item.resource(), status.info().resource())).map(item -> status.info().amount() - item.crafting()).findFirst().orElse(-1L)); + properties.put("resource", getObjectFromResourceKey(status.info().resource(), status.info().amount())); + properties.put("completion", status.percentageCompleted()); + + return properties; + } + + public static Object parsePattern(Pattern pattern) { + if (pattern == null) + return null; + + Map propeties = new HashMap<>(); + propeties.put("outputs", pattern.layout().outputs().stream().map(RSApi::getObjectFromResourceAmount).toList()); + + List>> inputs = pattern.layout().ingredients().stream() + .map(ingredient -> ingredient.inputs().stream() + .map(key -> getObjectFromResourceKey(key, ingredient.amount())) + .collect(Collectors.toList())) + .collect(Collectors.toList()); + + propeties.put("inputs", inputs); + propeties.put("patterntype", pattern.layout().type().toString()); + propeties.put("id", pattern.id().toString()); + return propeties; + } + + /** + * Parses an RS TrackedResourceAmount to a lua object + * This method assumes you did an instanceof check before that the {@link ResourceKey} is an {@link ItemResource} + * + * @param trackedResourceAmount the tracked resource amount containing an ItemResource + * @return a Map containing the properties which CC can parse to a lua table + */ + public static Map getObjectFromItemResource(ResourceAmount trackedResourceAmount) { + ItemResource resource = (ItemResource) trackedResourceAmount.resource(); + long count = trackedResourceAmount.amount(); + ItemStack stack = resource.toItemStack(); + Map properties = LuaConverter.itemStackToObject(stack, count); + properties.put("fingerprint", ItemUtil.getFingerprint(stack)); + return properties; + } + + /** + * Parses an RS TrackedResourceAmount to a lua object + * This method assumes you did an instanceof check before that the {@link ResourceKey} is an {@link ItemResource} + * + * @param trackedResourceAmount the tracked resource amount containing an ItemResource + * @return a Map containing the properties which CC can parse to a lua table + */ + public static Map getObjectFromItemResource(ResourceAmount trackedResourceAmount, long alternateCount) { + Map properties = getObjectFromItemResource(trackedResourceAmount); + properties.put("count", alternateCount); + return properties; + } + + /** + * Parses an RS TrackedResourceAmount to a lua object + * This method assumes you did an instanceof check before that the {@link ResourceKey} is an {@link ItemResource} + * + * @param trackedResourceAmount the tracked resource amount containing an ItemResource + * @return a Map containing the properties which CC can parse to a lua table + */ + public static Map getObjectFromFluidResource(ResourceAmount trackedResourceAmount) { + FluidResource resource = (FluidResource) trackedResourceAmount.resource(); + long count = trackedResourceAmount.amount(); + FluidStack stack = VariantUtil.toFluidStack(resource, count); + Map properties = LuaConverter.fluidStackToObject(stack, count); + properties.put("fingerprint", FluidUtil.getFingerprint(stack)); + return properties; + } + + /** + * Parses an RS TrackedResourceAmount to a lua object + * This method assumes you did an instanceof check before that the {@link ResourceKey} is an {@link ItemResource} + * + * @param trackedResourceAmount the tracked resource amount containing an ItemResource + * @param alternateCount a count can be passed to overwrite the count of the object. Useful for patterns and craftable stacks + * @return a Map containing the properties which CC can parse to a lua table + */ + public static Map getObjectFromFluidResource(ResourceAmount trackedResourceAmount, long alternateCount) { + Map properties = getObjectFromFluidResource(trackedResourceAmount); + properties.put("count", alternateCount); + return properties; + } + + + /** + * Parses an RS TrackedResourceAmount to a lua object + * This method assumes you did an instanceof check before that the {@link ResourceKey} is an {@link ChemicalResource} + * + * @param trackedResourceAmount the tracked resource amount containing a ChemicalResource + * @return a Map containing the properties which CC can parse to a lua table + */ + public static Map getObjectFromChemicalResource(ResourceAmount trackedResourceAmount) { + ChemicalResource resource = (ChemicalResource) trackedResourceAmount.resource(); + long count = trackedResourceAmount.amount(); + ChemicalStack stack = resourceToChemicalStack(resource, count); + Map properties = LuaConverter.chemicalStackToObject(stack, count); + properties.put("fingerprint", ChemicalUtil.getFingerprint(stack)); + return properties; + } + + /** + * Parses an RS TrackedResourceAmount to a lua object + * This method assumes you did an instanceof check before that the {@link ResourceKey} is an {@link ChemicalResource} + * + * @param trackedResourceAmount the tracked resource amount containing a ChemicalResource + * @param alternateCount a count can be passed to overwrite the count of the object. Useful for patterns and craftable stacks + * @return a Map containing the properties which CC can parse to a lua table + */ + public static Map getObjectFromChemicalResource(ResourceAmount trackedResourceAmount, long alternateCount) { + Map properties = getObjectFromChemicalResource(trackedResourceAmount); + properties.put("count", alternateCount); + return properties; + } + + public static ChemicalStack resourceToChemicalStack(ResourceAmount resourceAmount) { + if (resourceAmount.resource() instanceof ChemicalResource chemicalResource) + return new ChemicalStack(MekanismAPI.CHEMICAL_REGISTRY.wrapAsHolder(chemicalResource.chemical()), resourceAmount.amount()); + + return ChemicalStack.EMPTY; + } + + public static ChemicalStack resourceToChemicalStack(ChemicalResource resource, long alternateCount) { + return new ChemicalStack(MekanismAPI.CHEMICAL_REGISTRY.wrapAsHolder(resource.chemical()), alternateCount); + } + + public static ChemicalStack resourceToChemicalStack(ChemicalResource resource) { + return resourceToChemicalStack(resource, 1); + } +} diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/refinedstorage/RSChemicalHandler.java b/src/main/java/de/srendi/advancedperipherals/common/addons/refinedstorage/RSChemicalHandler.java new file mode 100644 index 000000000..5d287837e --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/refinedstorage/RSChemicalHandler.java @@ -0,0 +1,51 @@ +package de.srendi.advancedperipherals.common.addons.refinedstorage; + +import com.refinedmods.refinedstorage.api.core.Action; +import com.refinedmods.refinedstorage.api.network.Network; +import com.refinedmods.refinedstorage.api.network.storage.StorageNetworkComponent; +import com.refinedmods.refinedstorage.api.storage.Actor; +import com.refinedmods.refinedstorage.mekanism.ChemicalResource; +import de.srendi.advancedperipherals.AdvancedPeripherals; +import de.srendi.advancedperipherals.common.util.inventory.ChemicalFilter; +import de.srendi.advancedperipherals.common.util.inventory.ChemicalUtil; +import de.srendi.advancedperipherals.common.util.inventory.IStorageSystemChemicalHandler; +import mekanism.api.chemical.ChemicalStack; +import org.jetbrains.annotations.NotNull; + +public class RSChemicalHandler implements IStorageSystemChemicalHandler { + + @NotNull + private final Network network; + private final StorageNetworkComponent component; + + public RSChemicalHandler(@NotNull Network network) { + this.network = network; + this.component = network.getComponent(StorageNetworkComponent.class); + } + + @NotNull + @Override + public ChemicalStack insertChemical(int tank, ChemicalStack resource, @NotNull mekanism.api.Action action) { + if (resource.isEmpty()) + return resource; + + long amountInserted = component.insert(ChemicalResource.ofChemicalStack(resource), resource.getAmount(), action == mekanism.api.Action.SIMULATE ? Action.SIMULATE : Action.EXECUTE, Actor.EMPTY); + ChemicalStack remain = resource.copyWithAmount(resource.getAmount() - amountInserted); + return remain; + } + + @Override + public ChemicalStack extractChemical(ChemicalFilter filter, long count, mekanism.api.Action simulate) { + AdvancedPeripherals.debug("Trying to extract chemical from filter: " + filter); + ChemicalResource chemical = RSApi.getChemical(network, filter); + if (chemical == null) + return ChemicalStack.EMPTY; + + long amountExtracted = component.extract(chemical, filter.getCount(), simulate == mekanism.api.Action.SIMULATE ? Action.SIMULATE : Action.EXECUTE, Actor.EMPTY); + ChemicalStack extracted = ChemicalUtil.toChemicalStack(chemical.chemical(), amountExtracted); + + AdvancedPeripherals.debug("Extracted chemical: " + extracted + " from filter: " + filter); + return extracted; + } + +} diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/refinedstorage/RSCraftJob.java b/src/main/java/de/srendi/advancedperipherals/common/addons/refinedstorage/RSCraftJob.java new file mode 100644 index 000000000..f0ef889d7 --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/refinedstorage/RSCraftJob.java @@ -0,0 +1,250 @@ +package de.srendi.advancedperipherals.common.addons.refinedstorage; + +import com.refinedmods.refinedstorage.api.autocrafting.preview.Preview; +import com.refinedmods.refinedstorage.api.autocrafting.preview.PreviewType; +import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatus; +import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId; +import com.refinedmods.refinedstorage.api.network.autocrafting.AutocraftingNetworkComponent; +import com.refinedmods.refinedstorage.api.resource.ResourceKey; +import com.refinedmods.refinedstorage.api.storage.Actor; +import dan200.computercraft.api.peripheral.IComputerAccess; +import de.srendi.advancedperipherals.AdvancedPeripherals; +import de.srendi.advancedperipherals.common.util.StatusConstants; +import de.srendi.advancedperipherals.common.util.inventory.BasicCraftJob; +import net.minecraft.world.level.Level; + +import java.util.Collections; +import java.util.Optional; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.stream.Collectors; + +public class RSCraftJob extends BasicCraftJob { + + private final AutocraftingNetworkComponent autocraftingComponent; + private final ResourceKey toCraft; + + private TaskStatus craftingTask; + private Preview preview; + private Future> futureCalculationResult; + private Future> futureTask; + + public RSCraftJob(IComputerAccess computer, Level world, long amount, ResourceKey toCraft, AutocraftingNetworkComponent calculationResult) { + super(computer, "rs", world, amount); + this.autocraftingComponent = calculationResult; + this.toCraft = toCraft; + } + + @Override + protected boolean isJobDone() { + return craftingTask != null && craftingTask.percentageCompleted() >= 100; + } + + @Override + protected boolean isJobCanceled() { + return craftingTask != null && craftingTask.percentageCompleted() < 100 && autocraftingComponent.getStatuses().stream().noneMatch(task -> task.info().id() == craftingTask.info().id()); + } + + @Override + public Object getParsedRequestedItemImpl() { + return RSApi.getObjectFromResourceKey(toCraft, amount); + } + + @Override + public long getElapsedTimeImpl() { + if (craftingTask == null) { + return -1; + } + return System.currentTimeMillis() - craftingTask.info().startTime(); + } + + @Override + public long getTotalItemsImpl() { + if (craftingTask == null) { + return -1; + } + + return craftingTask.items().size(); + } + + @Override + public long getItemProgressImpl() { + return craftingTask == null ? 0 : craftingTask.items().stream().mapToLong(TaskStatus.Item::stored).sum(); + } + + @Override + public Object getEmittedItemsImpl() { + if (preview == null) { + return Collections.emptyList(); + } + + return preview.items().stream().filter(item -> item.toCraft() > 0).map(item -> RSApi.getObjectFromResourceKey(item.resource(), item.toCraft())).collect(Collectors.toList()); + } + + @Override + public Object getUsedItemsImpl() { + // Not supported for rs2 + return Collections.emptyList(); + } + + @Override + public Object getMissingItemsImpl() { + if (preview == null) { + return Collections.emptyList(); + } + + return preview.items().stream().filter(item -> item.missing() > 0).map(item -> RSApi.getObjectFromResourceKey(item.resource(), item.missing())).collect(Collectors.toList()); + } + + @Override + public boolean hasMultiplePathsImpl() { + // Not supported for rs2 + return false; + } + + @Override + public Object getFinalOutputImpl() { + if (preview == null) { + return Collections.emptyList(); + } + + return preview.outputsOfPatternWithCycle().stream().map(RSApi::getObjectFromResourceAmount).collect(Collectors.toList()); + } + + public TaskStatus getCraftingTask() { + return craftingTask; + } + + @Override + public boolean cancelImpl() { + if (isJobDone() || isJobCanceled()) { + return false; + } + autocraftingComponent.cancel(craftingTask.info().id()); + return true; + } + + @Override + public void tick() { + super.tick(); + // RS2 does re-create a new instance of the TaskStatus when something changes - so we actually need to re fetch that from the system + // It's cursed, but the only way currently + if (craftingTask != null) { + for (TaskStatus status : autocraftingComponent.getStatuses()) { + if (status.info().id().equals(craftingTask.info().id())) { + this.craftingTask = status; + break; + } + } + } + // The following is to get the TaskStatus from the issued task + if (futureTask == null || !futureTask.isDone() || craftingTask != null) { + return; + } + Optional optionalId; + + try { + optionalId = futureTask.get(); + } catch (InterruptedException | ExecutionException ex) { + AdvancedPeripherals.debug("Tried to get the task but the task was not done. Should be done", ex); + fireEvent(true, StatusConstants.UNKNOWN_ERROR); + return; + } + + if (optionalId.isEmpty()) { + // This indicates that the second calculation was not successful. Well I guess. There is no java doc and I currently + // don't get an answer from the maintainer. So maybe we want to fire the crafting event + return; + } + + TaskId id = optionalId.get(); + for (TaskStatus status : autocraftingComponent.getStatuses()) { + if (status.info().id().equals(id)) { + this.craftingTask = status; + // And only now we set that the crafting is started. + setStartedCrafting(); + break; + } + } + } + + @Override + protected void maybeCraft() { + if (startedCrafting || futureTask != null || futureCalculationResult == null || !futureCalculationResult.isDone()) { + return; + } + + Optional optionalPreview; + + try { + optionalPreview = futureCalculationResult.get(); + } catch (ExecutionException | InterruptedException ex) { + AdvancedPeripherals.debug("Tried to get preview, but preview calculation is not done. Should be done.", ex); + fireEvent(true, StatusConstants.UNKNOWN_ERROR); + return; + } + + // TODO: I currently don't exactly know when the optional can be empty after the future is done. So I need to evaluate this. + if (optionalPreview.isEmpty()) { + AdvancedPeripherals.debug("preview optional is empty.", org.apache.logging.log4j.Level.ERROR); + fireEvent(true, StatusConstants.UNKNOWN_ERROR); + return; + } + + Preview preview = optionalPreview.get(); + this.preview = preview; + + PreviewType previewType = preview.type(); + + if (previewType == PreviewType.MISSING_RESOURCES) { + calculationNotSuccessful = true; + fireEvent(true, StatusConstants.MISSING_ITEMS); + return; + } + + if (previewType != PreviewType.SUCCESS) { + calculationNotSuccessful = true; + fireEvent(true, previewType.toString()); + return; + } + + // How RS2 handles crafting is a bit cursed. We first create a preview which calculates the recipes, and then we check if the preview was successful + // If it was, we again start a task which again calculates the recipes, and then we hope nothing changed from the first calculation + futureTask = autocraftingComponent.startTask(toCraft, amount, Actor.EMPTY, false); + } + + @Override + protected void startCalculation() { + if (startedCalculation) { + return; + } + startedCalculation = true; + + if (autocraftingComponent.getPatternsByOutput(toCraft).isEmpty()) { + fireEvent(true, StatusConstants.NOT_CRAFTABLE); + return; + } + + futureCalculationResult = autocraftingComponent.getPreview(toCraft, amount); + fireEvent(false, StatusConstants.CALCULATION_STARTED); + } + + @Override + public void jobStateChanged() { + if (this.craftingTask == null) { + fireEvent(true, StatusConstants.UNKNOWN_ERROR); + return; + } + + if (isJobCanceled() && !isJobCanceled) { + fireEvent(false, StatusConstants.JOB_CANCELED); + setJobCanceled(); + return; + } + + if (isJobDone() && !isJobDone) { + fireEvent(true, StatusConstants.JOB_DONE); + setJobDone(); + } + } +} diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/refinedstorage/RSFluidHandler.java b/src/main/java/de/srendi/advancedperipherals/common/addons/refinedstorage/RSFluidHandler.java new file mode 100644 index 000000000..e0fb3362e --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/refinedstorage/RSFluidHandler.java @@ -0,0 +1,48 @@ +package de.srendi.advancedperipherals.common.addons.refinedstorage; + +import com.refinedmods.refinedstorage.api.core.Action; +import com.refinedmods.refinedstorage.api.network.Network; +import com.refinedmods.refinedstorage.api.network.storage.StorageNetworkComponent; +import com.refinedmods.refinedstorage.api.storage.Actor; +import com.refinedmods.refinedstorage.common.support.resource.FluidResource; +import com.refinedmods.refinedstorage.neoforge.support.resource.VariantUtil; +import de.srendi.advancedperipherals.AdvancedPeripherals; +import de.srendi.advancedperipherals.common.util.inventory.FluidFilter; +import de.srendi.advancedperipherals.common.util.inventory.IStorageSystemFluidHandler; +import net.neoforged.neoforge.fluids.FluidStack; +import org.jetbrains.annotations.NotNull; + +public class RSFluidHandler implements IStorageSystemFluidHandler { + + @NotNull + private final Network network; + private final StorageNetworkComponent component; + + public RSFluidHandler(@NotNull Network network) { + this.network = network; + this.component = network.getComponent(StorageNetworkComponent.class); + } + + @Override + public int fill(FluidStack resource, @NotNull FluidAction action) { + if (resource.isEmpty()) + return 0; + long inserted = component.insert(VariantUtil.ofFluidStack(resource), resource.getAmount(), action == FluidAction.SIMULATE ? Action.SIMULATE : Action.EXECUTE, Actor.EMPTY); + return (int) (resource.getAmount() - inserted); + } + + @NotNull + @Override + public FluidStack drain(FluidFilter filter, FluidAction simulate) { + AdvancedPeripherals.debug("Trying to extract fluid from filter: " + filter); + FluidResource fluid = RSApi.getFluid(network, filter); + if (fluid == null) + return FluidStack.EMPTY; + + long amountExtracted = component.extract(fluid, filter.getCount(), simulate == FluidAction.SIMULATE ? Action.SIMULATE : Action.EXECUTE, Actor.EMPTY); + FluidStack extracted = VariantUtil.toFluidStack(fluid, (int) amountExtracted); + + AdvancedPeripherals.debug("Extracted fluid: " + extracted + " from filter: " + filter); + return extracted; + } +} diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/refinedstorage/RSItemHandler.java b/src/main/java/de/srendi/advancedperipherals/common/addons/refinedstorage/RSItemHandler.java new file mode 100644 index 000000000..7753a3701 --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/refinedstorage/RSItemHandler.java @@ -0,0 +1,52 @@ +package de.srendi.advancedperipherals.common.addons.refinedstorage; + +import com.refinedmods.refinedstorage.api.network.Network; +import com.refinedmods.refinedstorage.api.network.storage.StorageNetworkComponent; +import com.refinedmods.refinedstorage.api.storage.Actor; +import com.refinedmods.refinedstorage.common.support.resource.ItemResource; +import de.srendi.advancedperipherals.AdvancedPeripherals; +import de.srendi.advancedperipherals.common.util.inventory.IStorageSystemItemHandler; +import de.srendi.advancedperipherals.common.util.inventory.ItemFilter; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; +import com.refinedmods.refinedstorage.api.core.Action; + +/** + * Used to transfer item between an inventory and the RS system. + * + * @see de.srendi.advancedperipherals.common.addons.computercraft.peripheral.RSBridgePeripheral + */ +public class RSItemHandler implements IStorageSystemItemHandler { + + @NotNull + private final Network network; + private final StorageNetworkComponent component; + + public RSItemHandler(@NotNull Network network) { + this.network = network; + this.component = network.getComponent(StorageNetworkComponent.class); + } + + @NotNull + @Override + public ItemStack insertItem(int slot, @NotNull ItemStack stack, boolean simulate) { + long insertedAmount = component.insert(ItemResource.ofItemStack(stack), stack.getCount(), simulate ? Action.SIMULATE : Action.EXECUTE, Actor.EMPTY); + ItemStack remain = stack.copyWithCount((int) (stack.getCount() - insertedAmount)); + return remain; + } + + @Override + public ItemStack extractItem(ItemFilter filter, int count, boolean simulate) { + AdvancedPeripherals.debug("Trying to extract item from filter: " + filter); + ItemResource itemResource = RSApi.getItem(network, filter); + if (itemResource == null) + return ItemStack.EMPTY; + + long extractedAmount = component.extract(itemResource, count, simulate ? Action.SIMULATE : Action.EXECUTE, Actor.EMPTY); + ItemStack extracted = itemResource.toItemStack(extractedAmount); + + AdvancedPeripherals.debug("Extracted item: " + extracted + " from filter: " + filter); + return extracted; + } + +} diff --git a/src/main/java/de/srendi/advancedperipherals/common/addons/refinedstorage/RsStorageTypes.java b/src/main/java/de/srendi/advancedperipherals/common/addons/refinedstorage/RsStorageTypes.java new file mode 100644 index 000000000..d61ff4424 --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/common/addons/refinedstorage/RsStorageTypes.java @@ -0,0 +1,32 @@ +package de.srendi.advancedperipherals.common.addons.refinedstorage; + +import com.refinedmods.refinedstorage.common.api.storage.StorageType; +import com.refinedmods.refinedstorage.common.storage.StorageTypes; +import com.refinedmods.refinedstorage.mekanism.ChemicalResourceType; +import de.srendi.advancedperipherals.common.addons.APAddons; + +import java.util.function.Supplier; + +/** + * To better support third party RS addons and to prevent any jvm loading issues when third party addons are not loaded + */ +public enum RsStorageTypes { + + ITEM(() -> StorageTypes.ITEM), + FLUID(() -> StorageTypes.FLUID), + CHEMICAL(() -> { + if (APAddons.refinedStorageMekanismLoaded) + return ChemicalResourceType.STORAGE_TYPE; + return null; + }); + + private final Supplier storageType; + + RsStorageTypes(Supplier storageType) { + this.storageType = storageType; + } + + public StorageType getStorageType() { + return storageType.get(); + } +} diff --git a/src/main/java/de/srendi/advancedperipherals/common/blocks/blockentities/MeBridgeEntity.java b/src/main/java/de/srendi/advancedperipherals/common/blocks/blockentities/MEBridgeEntity.java similarity index 93% rename from src/main/java/de/srendi/advancedperipherals/common/blocks/blockentities/MeBridgeEntity.java rename to src/main/java/de/srendi/advancedperipherals/common/blocks/blockentities/MEBridgeEntity.java index 8e7255c64..82f7116bb 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/blocks/blockentities/MeBridgeEntity.java +++ b/src/main/java/de/srendi/advancedperipherals/common/blocks/blockentities/MEBridgeEntity.java @@ -12,8 +12,8 @@ import appeng.api.util.AECableType; import appeng.me.helpers.IGridConnectedBlockEntity; import de.srendi.advancedperipherals.common.addons.appliedenergistics.CraftJob; -import de.srendi.advancedperipherals.common.addons.appliedenergistics.MeBridgeEntityListener; -import de.srendi.advancedperipherals.common.addons.computercraft.peripheral.MeBridgePeripheral; +import de.srendi.advancedperipherals.common.addons.appliedenergistics.MEBridgeEntityListener; +import de.srendi.advancedperipherals.common.addons.computercraft.peripheral.MEBridgePeripheral; import de.srendi.advancedperipherals.common.blocks.base.PeripheralBlockEntity; import de.srendi.advancedperipherals.common.configuration.APConfig; import de.srendi.advancedperipherals.common.setup.BlockEntityTypes; @@ -34,21 +34,21 @@ import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; -public class MeBridgeEntity extends PeripheralBlockEntity implements IActionSource, IActionHost, IInWorldGridNodeHost, IGridConnectedBlockEntity, ICraftingSimulationRequester { +public class MEBridgeEntity extends PeripheralBlockEntity implements IActionSource, IActionHost, IInWorldGridNodeHost, IGridConnectedBlockEntity, ICraftingSimulationRequester { private final List jobs = new CopyOnWriteArrayList<>(); private boolean initialized = false; - private final IManagedGridNode mainNode = GridHelper.createManagedNode(this, MeBridgeEntityListener.INSTANCE); + private final IManagedGridNode mainNode = GridHelper.createManagedNode(this, MEBridgeEntityListener.INSTANCE); - public MeBridgeEntity(BlockPos pos, BlockState state) { + public MEBridgeEntity(BlockPos pos, BlockState state) { super(BlockEntityTypes.ME_BRIDGE.get(), pos, state); getMainNode().setExposedOnSides(getGridConnectableSides(null)); } @NotNull @Override - protected MeBridgePeripheral createPeripheral() { - return new MeBridgePeripheral(this); + protected MEBridgePeripheral createPeripheral() { + return new MEBridgePeripheral(this); } @Override diff --git a/src/main/java/de/srendi/advancedperipherals/common/blocks/blockentities/RSBridgeEntity.java b/src/main/java/de/srendi/advancedperipherals/common/blocks/blockentities/RSBridgeEntity.java new file mode 100644 index 000000000..b2385707e --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/common/blocks/blockentities/RSBridgeEntity.java @@ -0,0 +1,129 @@ +package de.srendi.advancedperipherals.common.blocks.blockentities; + +import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatus; +import com.refinedmods.refinedstorage.api.autocrafting.status.TaskStatusListener; +import com.refinedmods.refinedstorage.api.autocrafting.task.TaskId; +import com.refinedmods.refinedstorage.api.network.autocrafting.AutocraftingNetworkComponent; +import com.refinedmods.refinedstorage.api.network.impl.node.SimpleNetworkNode; +import com.refinedmods.refinedstorage.api.network.node.NetworkNode; +import com.refinedmods.refinedstorage.common.api.support.network.ConnectionStrategy; +import com.refinedmods.refinedstorage.common.api.support.network.InWorldNetworkNodeContainer; +import com.refinedmods.refinedstorage.common.api.support.network.NetworkNodeContainerProvider; +import com.refinedmods.refinedstorage.common.support.network.InWorldNetworkNodeContainerImpl; +import com.refinedmods.refinedstorage.common.support.network.SimpleConnectionStrategy; +import de.srendi.advancedperipherals.common.addons.computercraft.peripheral.RSBridgePeripheral; +import de.srendi.advancedperipherals.common.addons.refinedstorage.RSCraftJob; +import de.srendi.advancedperipherals.common.blocks.base.PeripheralBlockEntity; +import de.srendi.advancedperipherals.common.configuration.APConfig; +import de.srendi.advancedperipherals.common.setup.BlockEntityTypes; +import de.srendi.advancedperipherals.common.util.inventory.BasicCraftJob; +import de.srendi.advancedperipherals.lib.peripherals.IPeripheralTileEntity; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.level.ServerPlayer; +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.NotNull; + +import java.util.List; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArrayList; + +public class RSBridgeEntity extends PeripheralBlockEntity implements IPeripheralTileEntity, NetworkNodeContainerProvider, TaskStatusListener { + + protected CompoundTag peripheralSettings; + private final NetworkNode node; + private final InWorldNetworkNodeContainer networkNodeContainer; + private final List jobs = new CopyOnWriteArrayList<>(); + private boolean addedListener = false; + + public RSBridgeEntity(BlockPos pos, BlockState state) { + super(BlockEntityTypes.RS_BRIDGE.get(), pos, state); + peripheralSettings = new CompoundTag(); + ConnectionStrategy connectionStrategy = new SimpleConnectionStrategy(pos); + node = new SimpleNetworkNode(APConfig.PERIPHERALS_CONFIG.rsConsumption.get()); + networkNodeContainer = new InWorldNetworkNodeContainerImpl(this, node, "RS Bridge", 1, connectionStrategy, null); + } + + @NotNull + @Override + protected RSBridgePeripheral createPeripheral() { + return new RSBridgePeripheral(this); + } + + @Override + public CompoundTag getPeripheralSettings() { + return peripheralSettings; + } + + @Override + public void markSettingsChanged() { + setChanged(); + } + + @Override + public void handleTick(Level level, BlockState state, BlockEntityType type) { + if (getNode().getNetwork() != null) { + AutocraftingNetworkComponent manager = getNode().getNetwork().getComponent(AutocraftingNetworkComponent.class); + if (!this.addedListener) { + manager.addListener(this); + this.addedListener = true; + } + } + + // Try to start the job if the job calculation finished + jobs.forEach(BasicCraftJob::tick); + + // Remove the job if the crafting calculation failed, we can't do anything with it anymore + jobs.removeIf(BasicCraftJob::canBePurged); + } + + public void addJob(RSCraftJob job) { + jobs.add(job); + } + + public List getJobs() { + return jobs; + } + + public NetworkNode getNode() { + return node; + } + + public void clearRemoved() { + super.clearRemoved(); + initialize(this.level, null); + } + + @NotNull + @Override + public Set getContainers() { + return Set.of(networkNodeContainer); + } + + @Override + public void addContainer(@NotNull InWorldNetworkNodeContainer inWorldNetworkNodeContainer) { + } + + @Override + public boolean canBuild(@NotNull ServerPlayer serverPlayer) { + return true; + } + + @Override + public void taskStatusChanged(@NotNull TaskStatus taskStatus) { + jobs.stream().filter(BasicCraftJob::isCraftingStarted).filter(job -> job.getCraftingTask().info().id().equals(taskStatus.info().id())).forEach(BasicCraftJob::jobStateChanged); + } + + @Override + public void taskRemoved(@NotNull TaskId taskId) { + jobs.stream().filter(BasicCraftJob::isCraftingStarted).filter(job -> job.getCraftingTask().info().id().equals(taskId)).forEach(BasicCraftJob::jobStateChanged); + } + + @Override + public void taskAdded(@NotNull TaskStatus taskStatus) { + jobs.stream().filter(BasicCraftJob::isCraftingStarted).filter(job -> job.getCraftingTask().info().id().equals(taskStatus.info().id())).forEach(BasicCraftJob::jobStateChanged); + } +} diff --git a/src/main/java/de/srendi/advancedperipherals/common/data/BlockStatesAndModelsProvider.java b/src/main/java/de/srendi/advancedperipherals/common/data/BlockStatesAndModelsProvider.java index abb69071b..0cceba301 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/data/BlockStatesAndModelsProvider.java +++ b/src/main/java/de/srendi/advancedperipherals/common/data/BlockStatesAndModelsProvider.java @@ -31,6 +31,7 @@ protected void registerStatesAndModels() { peripheralBlock(Blocks.ENERGY_DETECTOR.get(), "front", "back", "top", "east"); peripheralBlock(Blocks.INVENTORY_MANAGER.get(), "front", "top"); peripheralBlock(Blocks.GEO_SCANNER.get(), "front", "top"); + peripheralBlock(Blocks.RS_BRIDGE.get(), "front", "top"); peripheralBlock(Blocks.COLONY_INTEGRATOR.get(), "front", "top"); peripheralBlock(Blocks.NBT_STORAGE.get(), "front", "top"); diff --git a/src/main/java/de/srendi/advancedperipherals/common/data/EnUsLanguageProvider.java b/src/main/java/de/srendi/advancedperipherals/common/data/EnUsLanguageProvider.java index ed91f98d8..92ff691c9 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/data/EnUsLanguageProvider.java +++ b/src/main/java/de/srendi/advancedperipherals/common/data/EnUsLanguageProvider.java @@ -60,6 +60,7 @@ private void addBlocks() { addBlock(Blocks.GEO_SCANNER, "Geo Scanner"); addBlock(Blocks.INVENTORY_MANAGER, "Inventory Manager"); addBlock(Blocks.ME_BRIDGE, "ME Bridge"); + addBlock(Blocks.RS_BRIDGE, "RS Bridge"); addBlock(Blocks.NBT_STORAGE, "NBT Storage"); addBlock(Blocks.PERIPHERAL_CASING, "Peripheral Casing"); addBlock(Blocks.PLAYER_DETECTOR, "Player Detector"); @@ -109,6 +110,7 @@ private void addTooltips() { addTooltip(Blocks.ENVIRONMENT_DETECTOR.get(), "&7This peripheral interacts with the minecraft world."); addTooltip(Blocks.PLAYER_DETECTOR.get(), "&7This peripheral can be used to interact with players, but don't be a stalker."); addTooltip(Blocks.ME_BRIDGE.get(), "&7The ME Bridge interacts with Applied Energistics to manage your items."); + addTooltip(Blocks.RS_BRIDGE.get(), "&7The RS Bridge interacts with Refined Storage to manage your items."); addTooltip(Blocks.CHAT_BOX.get(), "&7Interacts with the ingame chat, can read and write messages."); addTooltip(Blocks.PERIPHERAL_CASING.get(), "&7An empty hull without the love it deserves. Used as a crafting ingredient"); addTooltip(Items.MEMORY_CARD.get(), "&7Can save the rights of a player to use it in an inventory manager."); diff --git a/src/main/java/de/srendi/advancedperipherals/common/data/RecipesProvider.java b/src/main/java/de/srendi/advancedperipherals/common/data/RecipesProvider.java index 42af11920..cfcd6be71 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/data/RecipesProvider.java +++ b/src/main/java/de/srendi/advancedperipherals/common/data/RecipesProvider.java @@ -3,6 +3,7 @@ import appeng.core.definitions.AEBlocks; import com.minecolonies.api.blocks.ModBlocks; import com.minecolonies.api.items.ModItems; +import com.refinedmods.refinedstorage.common.misc.ProcessorItem; import dan200.computercraft.shared.ModRegistry; import de.srendi.advancedperipherals.common.addons.APAddons; import de.srendi.advancedperipherals.common.setup.Blocks; @@ -68,6 +69,20 @@ protected void buildRecipes(RecipeOutput recipeOutput) { ShapedRecipeBuilder.shaped(RecipeCategory.REDSTONE, Blocks.ME_BRIDGE.get()).define('F', AEBlocks.FLUIX_BLOCK.asItem()).define('A', CASING).define('I', AEBlocks.INTERFACE.asItem()).pattern("FIF").pattern("IAI").pattern("FIF").unlockedBy(HAS_ITEM, has(CASING)).save(recipeOutput.withConditions(new ModLoadedCondition(APAddons.AE2_MODID))); + + com.refinedmods.refinedstorage.common.content.Items rsItems = com.refinedmods.refinedstorage.common.content.Items.INSTANCE; + com.refinedmods.refinedstorage.common.content.Blocks rsBlocks = com.refinedmods.refinedstorage.common.content.Blocks.INSTANCE; + ShapedRecipeBuilder.shaped(RecipeCategory.REDSTONE, Blocks.RS_BRIDGE.get()) + .define('P', rsItems.getProcessor(ProcessorItem.Type.ADVANCED)) + .define('C', CASING) + .define('I', rsBlocks.getInterface()) + .define('X', com.refinedmods.refinedstorage.common.content.Tags.EXTERNAL_STORAGES) + .define('E', com.refinedmods.refinedstorage.common.content.Tags.EXPORTERS) + .define('R', com.refinedmods.refinedstorage.common.content.Tags.IMPORTERS) + .pattern("PXP") + .pattern("ECR") + .pattern("PIP").unlockedBy(HAS_ITEM, has(CASING)).save(recipeOutput.withConditions(new ModLoadedCondition(APAddons.REFINEDSTORAGE_MODID))); + ShapedRecipeBuilder.shaped(RecipeCategory.REDSTONE, Blocks.COLONY_INTEGRATOR.get()).define('O', ItemTags.LOGS).define('A', CASING).define('B', ModItems.buildGoggles).define('S', com.ldtteam.structurize.items.ModItems.buildTool).define('R', ModBlocks.blockRack).pattern("ORO").pattern("BAS").pattern("ORO").unlockedBy(HAS_ITEM, has(CASING)).save(recipeOutput.withConditions(new ModLoadedCondition(APAddons.MINECOLONIES_MODID))); ShapedRecipeBuilder.shaped(RecipeCategory.REDSTONE, de.srendi.advancedperipherals.common.setup.Items.WEAK_AUTOMATA_CORE.get()) diff --git a/src/main/java/de/srendi/advancedperipherals/common/setup/BlockEntityTypes.java b/src/main/java/de/srendi/advancedperipherals/common/setup/BlockEntityTypes.java index f2fc2fcfe..4f9dc859e 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/setup/BlockEntityTypes.java +++ b/src/main/java/de/srendi/advancedperipherals/common/setup/BlockEntityTypes.java @@ -9,10 +9,11 @@ import de.srendi.advancedperipherals.common.blocks.blockentities.EnvironmentDetectorEntity; import de.srendi.advancedperipherals.common.blocks.blockentities.GeoScannerEntity; import de.srendi.advancedperipherals.common.blocks.blockentities.InventoryManagerEntity; -import de.srendi.advancedperipherals.common.blocks.blockentities.MeBridgeEntity; +import de.srendi.advancedperipherals.common.blocks.blockentities.MEBridgeEntity; import de.srendi.advancedperipherals.common.blocks.blockentities.NBTStorageEntity; import de.srendi.advancedperipherals.common.blocks.blockentities.PlayerDetectorEntity; import de.srendi.advancedperipherals.common.blocks.blockentities.RedstoneIntegratorEntity; +import de.srendi.advancedperipherals.common.blocks.blockentities.RSBridgeEntity; import net.minecraft.world.level.block.entity.BlockEntityType; import net.neoforged.neoforge.registries.DeferredHolder; @@ -21,7 +22,8 @@ public class BlockEntityTypes { public static final DeferredHolder, BlockEntityType> CHAT_BOX = Registration.BLOCK_ENTITIES.register("chat_box", () -> new BlockEntityType<>(ChatBoxEntity::new, Sets.newHashSet(Blocks.CHAT_BOX.get()), null)); public static final DeferredHolder, BlockEntityType> ENVIRONMENT_DETECTOR = Registration.BLOCK_ENTITIES.register("environment_detector", () -> new BlockEntityType<>(EnvironmentDetectorEntity::new, Sets.newHashSet(Blocks.ENVIRONMENT_DETECTOR.get()), null)); public static final DeferredHolder, BlockEntityType> PLAYER_DETECTOR = Registration.BLOCK_ENTITIES.register("player_detector", () -> new BlockEntityType<>(PlayerDetectorEntity::new, Sets.newHashSet(Blocks.PLAYER_DETECTOR.get()), null)); - public static final DeferredHolder, BlockEntityType> ME_BRIDGE = APAddons.ae2Loaded ? Registration.BLOCK_ENTITIES.register("me_bridge", () -> new BlockEntityType<>(MeBridgeEntity::new, Sets.newHashSet(Blocks.ME_BRIDGE.get()), null)) : null; + public static final DeferredHolder, BlockEntityType> ME_BRIDGE = APAddons.ae2Loaded ? Registration.BLOCK_ENTITIES.register("me_bridge", () -> new BlockEntityType<>(MEBridgeEntity::new, Sets.newHashSet(Blocks.ME_BRIDGE.get()), null)) : null; + public static final DeferredHolder, BlockEntityType> RS_BRIDGE = APAddons.refinedStorageLoaded ? Registration.BLOCK_ENTITIES.register("rs_bridge", () -> new BlockEntityType<>(RSBridgeEntity::new, Sets.newHashSet(Blocks.RS_BRIDGE.get()), null)) : null; public static final DeferredHolder, BlockEntityType> ENERGY_DETECTOR = Registration.BLOCK_ENTITIES.register("energy_detector", () -> new BlockEntityType<>(EnergyDetectorEntity::new, Sets.newHashSet(Blocks.ENERGY_DETECTOR.get()), null)); public static final DeferredHolder, BlockEntityType> INVENTORY_MANAGER = Registration.BLOCK_ENTITIES.register("inventory_manager", () -> new BlockEntityType<>(InventoryManagerEntity::new, Sets.newHashSet(Blocks.INVENTORY_MANAGER.get()), null)); public static final DeferredHolder, BlockEntityType> REDSTONE_INTEGRATOR = Registration.BLOCK_ENTITIES.register("redstone_integrator", () -> new BlockEntityType<>(RedstoneIntegratorEntity::new, Sets.newHashSet(Blocks.REDSTONE_INTEGRATOR.get()), null)); diff --git a/src/main/java/de/srendi/advancedperipherals/common/setup/Blocks.java b/src/main/java/de/srendi/advancedperipherals/common/setup/Blocks.java index 52de7d557..636a6b99e 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/setup/Blocks.java +++ b/src/main/java/de/srendi/advancedperipherals/common/setup/Blocks.java @@ -23,6 +23,7 @@ public class Blocks { public static final DeferredHolder> CHAT_BOX = register("chat_box", () -> new APBlockEntityBlock<>(BlockEntityTypes.CHAT_BOX, true), () -> new APBlockItem(Blocks.CHAT_BOX.get(), APConfig.PERIPHERALS_CONFIG.enableChatBox::get)); public static final DeferredHolder> PLAYER_DETECTOR = register("player_detector", PlayerDetectorBlock::new, () -> new APBlockItem(Blocks.PLAYER_DETECTOR.get(), APConfig.PERIPHERALS_CONFIG.enablePlayerDetector::get)); public static final DeferredHolder> ME_BRIDGE = register("me_bridge", () -> new APBlockEntityBlock<>(APAddons.ae2Loaded ? BlockEntityTypes.ME_BRIDGE : null, APAddons.ae2Loaded), () -> new APBlockItem(Blocks.ME_BRIDGE.get(), APConfig.PERIPHERALS_CONFIG.enableMEBridge::get)); + public static final DeferredHolder> RS_BRIDGE = register("rs_bridge", () -> new APBlockEntityBlock<>(APAddons.refinedStorageLoaded ? BlockEntityTypes.RS_BRIDGE : null, APAddons.refinedStorageLoaded), () -> new APBlockItem(Blocks.RS_BRIDGE.get(), APConfig.PERIPHERALS_CONFIG.enableRSBridge::get)); public static final DeferredHolder> ENERGY_DETECTOR = register("energy_detector", () -> new APBlockEntityBlock<>(BlockEntityTypes.ENERGY_DETECTOR, true), () -> new APBlockItem(Blocks.ENERGY_DETECTOR.get(), APConfig.PERIPHERALS_CONFIG.enableEnergyDetector::get)); public static final DeferredHolder PERIPHERAL_CASING = register("peripheral_casing", BaseBlock::new, () -> new APBlockItem(Blocks.PERIPHERAL_CASING.get(), new Item.Properties().stacksTo(16), () -> true)); public static final DeferredHolder> INVENTORY_MANAGER = register("inventory_manager", () -> new APBlockEntityBlock<>(BlockEntityTypes.INVENTORY_MANAGER, false), () -> new APBlockItem(Blocks.INVENTORY_MANAGER.get(), APConfig.PERIPHERALS_CONFIG.enableInventoryManager::get)); diff --git a/src/main/java/de/srendi/advancedperipherals/common/util/CoordUtil.java b/src/main/java/de/srendi/advancedperipherals/common/util/CoordUtil.java index d953cbbfd..186aa9beb 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/util/CoordUtil.java +++ b/src/main/java/de/srendi/advancedperipherals/common/util/CoordUtil.java @@ -104,9 +104,10 @@ public static boolean isInRange(@Nullable BlockPos blockPos, @Nullable Player pl return world.getNearbyPlayers(TargetingConditions.forNonCombat(), null, new AABB(firstPos.getX(), firstPos.getY(), firstPos.getZ(), secondPos.getX(), secondPos.getY(), secondPos.getZ())).contains(player); } + @Nullable public static Direction getDirection(FrontAndTop orientation, String computerSide) throws LuaException { if (computerSide == null) { - throw new LuaException("null is not a valid side"); + return null; } computerSide = computerSide.toLowerCase(Locale.ROOT); @@ -118,7 +119,7 @@ public static Direction getDirection(FrontAndTop orientation, String computerSid final ComputerSide side = ComputerSide.valueOfInsensitive(computerSide); if (side == null) { - throw new LuaException(computerSide + " is not a valid side"); + return null; } if (front.getAxis() == Direction.Axis.Y) { diff --git a/src/main/java/de/srendi/advancedperipherals/common/util/LuaConverter.java b/src/main/java/de/srendi/advancedperipherals/common/util/LuaConverter.java index 95311e639..ed97fd425 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/util/LuaConverter.java +++ b/src/main/java/de/srendi/advancedperipherals/common/util/LuaConverter.java @@ -2,9 +2,15 @@ import dan200.computercraft.api.lua.LuaException; import dan200.computercraft.shared.util.NBTUtil; +import de.srendi.advancedperipherals.common.addons.APAddons; import de.srendi.advancedperipherals.common.addons.computercraft.peripheral.InventoryManagerPeripheral; +import de.srendi.advancedperipherals.common.util.inventory.ChemicalUtil; import de.srendi.advancedperipherals.common.util.inventory.FluidUtil; import de.srendi.advancedperipherals.common.util.inventory.ItemUtil; +import mekanism.api.MekanismAPI; +import mekanism.api.MekanismAPITags; +import mekanism.api.chemical.Chemical; +import mekanism.api.chemical.ChemicalStack; import net.minecraft.core.BlockPos; import net.minecraft.core.component.DataComponentPatch; import net.minecraft.tags.TagKey; @@ -95,7 +101,9 @@ public static Object stateToObject(Comparable blockStateValue) { } public static Object posToObject(BlockPos pos) { - if (pos == null) return null; + if (pos == null) { + return null; + } Map map = new HashMap<>(3); map.put("x", pos.getX()); @@ -132,6 +140,22 @@ public static Map fluidStackToObject(@NotNull FluidStack stack) return map; } + public static Map chemicalStackToObject(@NotNull ChemicalStack stack) { + // In theory should not be called if the addon is not installed, but just to be save + if (!APAddons.refinedStorageMekanismLoaded) { + return null; + } + + if (stack.isEmpty()) { + return null; + } + Map map = chemicalToObject(stack.getChemical()); + map.put("count", stack.getAmount()); + map.put("displayName", stack.getTextComponent().getString()); + map.put("fingerprint", ChemicalUtil.getFingerprint(stack)); + return map; + } + public static Map fluidTypeToObject(FluidType type) { Map map = new HashMap<>(); map.put("viscosity", type.getViscosity()); @@ -147,10 +171,36 @@ public static Map fluidTypeToObject(FluidType type) { return map; } - public static Map itemStackToObject(@NotNull ItemStack itemStack, int amount) { - ItemStack stack = itemStack.copy(); - stack.setCount(amount); - return itemStackToObject(stack); + public static Map itemStackToObject(@NotNull ItemStack itemStack, long count) { + if (itemStack.isEmpty()) { + return null; + } + Map map = itemStackToObject(itemStack); + map.put("count", count); + return map; + } + + public static Map fluidStackToObject(@NotNull FluidStack fluidStack, long count) { + if (fluidStack.isEmpty()) { + return null; + } + Map map = fluidStackToObject(fluidStack); + map.put("count", count); + return map; + } + + public static Map chemicalStackToObject(@NotNull ChemicalStack chemicalStack, long count) { + // In theory should not be called if the addon is not installed, but just to be save + if (!APAddons.refinedStorageMekanismLoaded) { + return null; + } + + if (chemicalStack.isEmpty()) { + return null; + } + Map map = chemicalStackToObject(chemicalStack); + map.put("count", count); + return map; } /** @@ -184,6 +234,20 @@ public static Map fluidToObject(@NotNull Fluid fluid) { return map; } + public static Map chemicalToObject(@NotNull Chemical chemical) { + // In theory should not be called if the addon is not installed, but just to be save + if (!APAddons.refinedStorageMekanismLoaded) { + return null; + } + + Map map = new HashMap<>(); + map.put("tags", tagsToList(() -> MekanismAPI.CHEMICAL_REGISTRY.wrapAsHolder(chemical).tags())); + map.put("isGaseous", MekanismAPI.CHEMICAL_REGISTRY.wrapAsHolder(chemical).is(MekanismAPITags.Chemicals.GASEOUS)); + map.put("radioactivity", chemical.getRadioactivity()); + map.put("name", ChemicalUtil.getRegistryKey(chemical).toString()); + return map; + } + public static List tagsToList(@NotNull Supplier>> tags) { if (tags.get().findAny().isEmpty()) { return Collections.emptyList(); diff --git a/src/main/java/de/srendi/advancedperipherals/common/util/StatusConstants.java b/src/main/java/de/srendi/advancedperipherals/common/util/StatusConstants.java new file mode 100644 index 000000000..561fac552 --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/common/util/StatusConstants.java @@ -0,0 +1,45 @@ +package de.srendi.advancedperipherals.common.util; + +/** + * A collection of constants used as return types for several peripherals + */ +public enum StatusConstants { + + // Crafting Jobs + CALCULATION_STARTED, + CRAFTING_STARTED, + JOB_CANCELED, + JOB_DONE, + NOT_CRAFTABLE, + MISSING_ITEMS, // If there are missing items for a crafting recipe/job after the calculation is done + CPU_NOT_FOUND, + // Filters + EMPTY_FILTER, + FILTER_ITEM_NOT_FOUND, // Item could not be found in registry + FILTER_FLUID_NOT_FOUND, // Fluid could not be found in registry + FILTER_CHEMICAL_NOT_FOUND, // Chemical could not be found in registry + NO_VALID_FLUID, // Fluid property of filter is not a string + NO_VALID_ITEM, // Item property of filter is not a string + NO_VALID_FROMSLOT, + NO_VALID_TOSLOT, + NO_VALID_NBT_HASH, + NO_VALID_NBT, + NO_VALID_FINGERPRINT, + NO_VALID_COUNT, + NO_VALID_FILTER_TYPE, + // Inventory, + INVENTORY_NOT_FOUND, + ITEM_NOT_FOUND, // Debug message when an item couldn't be found in an/the target inventory + FLUID_NOT_FOUND, + CHEMICAL_NOT_FOUND, + // Misc + NOT_CONNECTED, + NOT_FOUND, // Generic not found state + ADDON_NOT_LOADED, + UNKNOWN_ERROR; + + public String withInfo(String extraInfo) { + return this + "_" + extraInfo; + } + +} diff --git a/src/main/java/de/srendi/advancedperipherals/common/util/inventory/BasicCraftJob.java b/src/main/java/de/srendi/advancedperipherals/common/util/inventory/BasicCraftJob.java new file mode 100644 index 000000000..1ab36f607 --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/common/util/inventory/BasicCraftJob.java @@ -0,0 +1,206 @@ +package de.srendi.advancedperipherals.common.util.inventory; + +import dan200.computercraft.api.lua.LuaFunction; +import dan200.computercraft.api.peripheral.IComputerAccess; +import de.srendi.advancedperipherals.common.util.StatusConstants; +import net.minecraft.world.level.Level; + +import java.util.concurrent.atomic.AtomicLong; + +public abstract class BasicCraftJob { + + private static final String EVENT = "_crafting"; + private static final int JOB_DONE_PURGE_TIME = 5 * 60 * 1000; + + public static final AtomicLong ID_SEQ = new AtomicLong(); + + protected final long id = ID_SEQ.incrementAndGet(); + protected final IComputerAccess computer; + protected final String eventName; + protected final long amount; + protected final Level world; + + protected boolean startedCrafting = false; + protected boolean startedCalculation = false; + protected boolean calculationNotSuccessful = false; + protected boolean errorOccurred = false; + protected boolean isJobDone = false; + protected long jobDoneTime = 0; + protected boolean isJobCanceled = false; + protected String debugMessage = null; + + public BasicCraftJob(IComputerAccess computer, String eventName, Level world, long amount) { + this.computer = computer; + this.eventName = eventName; + this.world = world; + this.amount = amount; + } + + @LuaFunction + public final long getId() { + return id; + } + + protected abstract boolean isJobDone(); + + @LuaFunction + public final boolean isDone() { + return isJobDone(); + } + + protected abstract boolean isJobCanceled(); + + @LuaFunction + public final boolean isCanceled() { + return isJobCanceled(); + } + + @LuaFunction + public final boolean isCraftingStarted() { + return startedCrafting; + } + + @LuaFunction + public final boolean isCalculationStarted() { + return startedCalculation; + } + + @LuaFunction + public final boolean isCalculationNotSuccessful() { + return calculationNotSuccessful; + } + + @LuaFunction + public final boolean hasErrorOccurred() { + return errorOccurred; + } + + @LuaFunction + public final String getDebugMessage() { + return debugMessage; + } + + @LuaFunction + public final Object getRequestedItem() { + return getParsedRequestedItemImpl(); + } + + @LuaFunction + public final long getElapsedTime() { + return getElapsedTimeImpl(); + } + + @LuaFunction + public final long getTotalItems() { + return getTotalItemsImpl(); + } + + @LuaFunction + public final long getItemProgress() { + return getItemProgressImpl(); + } + + @LuaFunction + public final Object getEmittedItems() { + return getEmittedItemsImpl(); + } + + @LuaFunction + public final Object getUsedItems() { + return getUsedItemsImpl(); + } + + @LuaFunction + public final Object getMissingItems() { + return getMissingItemsImpl(); + } + + @LuaFunction + public final boolean hasMultiplePaths() { + return hasMultiplePathsImpl(); + } + + @LuaFunction + public final Object getFinalOutput() { + return getFinalOutputImpl(); + } + + @LuaFunction + public final boolean cancel() { + return cancelImpl(); + } + + protected abstract Object getParsedRequestedItemImpl(); + + protected abstract long getElapsedTimeImpl(); + + protected abstract long getTotalItemsImpl(); + + protected abstract long getItemProgressImpl(); + + protected abstract Object getEmittedItemsImpl(); + + protected abstract Object getUsedItemsImpl(); + + protected abstract Object getMissingItemsImpl(); + + protected abstract boolean hasMultiplePathsImpl(); + + protected abstract Object getFinalOutputImpl(); + + protected abstract boolean cancelImpl(); + + protected Level getWorld() { + return world; + } + + protected long getAmount() { + return amount; + } + + public boolean canBePurged() { + return calculationNotSuccessful || ((isJobDone || isJobCanceled) && jobDoneTime + JOB_DONE_PURGE_TIME < System.currentTimeMillis()); + } + + protected void fireNotConnected() { + fireEvent(true, StatusConstants.NOT_CONNECTED); + } + + protected void setStartedCrafting() { + this.startedCrafting = true; + fireEvent(false, StatusConstants.CRAFTING_STARTED); + } + + protected void setJobCanceled() { + this.isJobCanceled = true; + this.jobDoneTime = System.currentTimeMillis(); + } + + protected void setJobDone() { + this.isJobDone = true; + this.jobDoneTime = System.currentTimeMillis(); + } + + protected void fireEvent(boolean error, StatusConstants message) { + this.computer.queueEvent(eventName + EVENT, error, this.id, message.toString()); + this.debugMessage = message.toString(); + this.errorOccurred = error; + } + + protected void fireEvent(boolean error, String message) { + this.computer.queueEvent(eventName + EVENT, error, this.id, message); + this.debugMessage = message; + this.errorOccurred = error; + } + + public void tick() { + startCalculation(); + maybeCraft(); + } + + protected abstract void maybeCraft(); + + protected abstract void startCalculation(); + + public abstract void jobStateChanged(); +} diff --git a/src/main/java/de/srendi/advancedperipherals/common/util/inventory/ChemicalFilter.java b/src/main/java/de/srendi/advancedperipherals/common/util/inventory/ChemicalFilter.java new file mode 100644 index 000000000..26d2f97b0 --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/common/util/inventory/ChemicalFilter.java @@ -0,0 +1,166 @@ +package de.srendi.advancedperipherals.common.util.inventory; + +import appeng.api.stacks.GenericStack; +import com.refinedmods.refinedstorage.api.resource.ResourceAmount; +import com.refinedmods.refinedstorage.mekanism.ChemicalResource; +import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.core.apis.TableHelper; +import de.srendi.advancedperipherals.AdvancedPeripherals; +import de.srendi.advancedperipherals.common.addons.APAddons; +import de.srendi.advancedperipherals.common.addons.refinedstorage.RSApi; +import de.srendi.advancedperipherals.common.util.Pair; +import mekanism.api.MekanismAPI; +import mekanism.api.chemical.Chemical; +import mekanism.api.chemical.ChemicalStack; +import net.minecraft.core.Holder; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.TagKey; + +import java.util.Map; + +public class ChemicalFilter extends GenericFilter { + + public static final ChemicalFilter EMPTY = new ChemicalFilter(); + + private Holder chemical = MekanismAPI.EMPTY_CHEMICAL_HOLDER; + private TagKey tag = null; + private long count = 64; + private String fingerprint = ""; + public int fromSlot = -1; + public int toSlot = -1; + + private ChemicalFilter() { + } + + public static Pair parse(Map item) { + // If the map is empty, return a filter without any filters + if (item.isEmpty()) + return Pair.of(EMPTY, null); + + ChemicalFilter chemicalFilter = createEmpty(); + + if (item.containsKey("name")) { + try { + String name = TableHelper.getStringField(item, "name"); + if (name.startsWith("#")) { + chemicalFilter.tag = TagKey.create(MekanismAPI.CHEMICAL_REGISTRY_NAME, ResourceLocation.parse(name.substring(1))); + } else if ((chemicalFilter.chemical = MekanismAPI.CHEMICAL_REGISTRY.getHolder(ResourceLocation.parse(name)).orElse(null)) == null) { + return Pair.of(null, "CHEMICAL_NOT_FOUND"); + } + } catch (LuaException luaException) { + return Pair.of(null, "NO_VALID_ITEM"); + } + } + if (item.containsKey("fingerprint")) { + try { + chemicalFilter.fingerprint = TableHelper.getStringField(item, "fingerprint"); + } catch (LuaException luaException) { + return Pair.of(null, "NO_VALID_FINGERPRINT"); + } + } + if (item.containsKey("fromSlot")) { + try { + chemicalFilter.fromSlot = TableHelper.getIntField(item, "fromSlot"); + } catch (LuaException luaException) { + return Pair.of(null, "NO_VALID_FROMSLOT"); + } + } + if (item.containsKey("toSlot")) { + try { + chemicalFilter.toSlot = TableHelper.getIntField(item, "toSlot"); + } catch (LuaException luaException) { + return Pair.of(null, "NO_VALID_TOSLOT"); + } + } + if (item.containsKey("count")) { + try { + chemicalFilter.count = TableHelper.getIntField(item, "count"); + } catch (LuaException luaException) { + return Pair.of(null, "NO_VALID_COUNT"); + } + } + + AdvancedPeripherals.debug("Parsed item filter: " + chemicalFilter); + return Pair.of(chemicalFilter, null); + } + + public static ChemicalFilter fromStack(ChemicalStack stack) { + ChemicalFilter filter = createEmpty(); + filter.chemical = stack.getChemicalHolder(); + return filter; + } + + public static ChemicalFilter createEmpty() { + return new ChemicalFilter(); + } + + public boolean isEmpty() { + return this == EMPTY || (fingerprint.isEmpty() && chemical.is(MekanismAPI.EMPTY_CHEMICAL_KEY) && tag == null); + } + + @Override + public boolean testAE(GenericStack genericStack) { + return false; + } + + @Override + public boolean testRS(ResourceAmount resourceAmount) { + if (!APAddons.mekanismLoaded || !APAddons.refinedStorageLoaded || !APAddons.refinedStorageMekanismLoaded) + return false; + if (resourceAmount.resource() instanceof ChemicalResource chemicalResource) { + return test(RSApi.resourceToChemicalStack(chemicalResource)); + } + return false; + } + + public ChemicalStack toChemicalStack() { + return new ChemicalStack(chemical, count); + } + + public boolean test(ChemicalStack stack) { + if (isEmpty()) + return true; + + if (!fingerprint.isEmpty()) { + String testFingerprint = ChemicalUtil.getFingerprint(stack); + return fingerprint.equals(testFingerprint); + } + + if (!chemical.is(MekanismAPI.EMPTY_CHEMICAL_KEY) && !stack.is(chemical)) { + return false; + } + if (tag != null && !stack.is(tag)) { + return false; + } + + return true; + } + + public long getCount() { + return count; + } + + public Holder getChemical() { + return chemical; + } + + public int getFromSlot() { + return fromSlot; + } + + public int getToSlot() { + return toSlot; + } + + @Override + public String toString() { + return "ChemicalFilter{" + + "item=" + chemical.getRegisteredName() + + ", tag=" + tag + + ", count=" + count + + ", fingerprint='" + fingerprint + '\'' + + ", fromSlot=" + fromSlot + + ", toSlot=" + toSlot + + '}'; + } +} diff --git a/src/main/java/de/srendi/advancedperipherals/common/util/inventory/ChemicalUtil.java b/src/main/java/de/srendi/advancedperipherals/common/util/inventory/ChemicalUtil.java new file mode 100644 index 000000000..4cb2bf72c --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/common/util/inventory/ChemicalUtil.java @@ -0,0 +1,131 @@ +package de.srendi.advancedperipherals.common.util.inventory; + +import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.peripheral.IComputerAccess; +import dan200.computercraft.api.peripheral.IPeripheral; +import de.srendi.advancedperipherals.AdvancedPeripherals; +import de.srendi.advancedperipherals.common.addons.computercraft.owner.IPeripheralOwner; +import de.srendi.advancedperipherals.common.util.CoordUtil; +import de.srendi.advancedperipherals.common.util.StringUtil; +import mekanism.api.Action; +import mekanism.api.MekanismAPI; +import mekanism.api.chemical.Chemical; +import mekanism.api.chemical.ChemicalStack; +import mekanism.api.chemical.IChemicalHandler; +import mekanism.common.capabilities.Capabilities; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Objects; + +public class ChemicalUtil { + + public static long moveChemical(IChemicalHandler inventoryFrom, IChemicalHandler inventoryTo, ChemicalFilter filter) { + if (inventoryFrom == null) return 0; + + long amount = filter.getCount(); + long transferableAmount = 0; + + // The logic changes with storage systems since these systems do not have slots + if (inventoryFrom instanceof IStorageSystemChemicalHandler storageSystemHandler) { + ChemicalStack extracted = storageSystemHandler.extractChemical(filter, amount, Action.SIMULATE); + ChemicalStack remaining = inventoryTo.insertChemical(extracted, Action.EXECUTE); + + transferableAmount += storageSystemHandler.extractChemical(filter, amount - remaining.getAmount(), Action.EXECUTE).getAmount(); + + return transferableAmount; + } + + if (inventoryTo instanceof IStorageSystemChemicalHandler storageSystemHandler) { + if (filter.test(inventoryFrom.getChemicalInTank(0))) { + ChemicalStack toExtract = inventoryFrom.getChemicalInTank(0).copy(); + toExtract.setAmount(amount); + ChemicalStack extracted = inventoryFrom.extractChemical(toExtract, Action.SIMULATE); + if (extracted.isEmpty()) + return 0; + long remaining = storageSystemHandler.insertChemical(extracted, Action.EXECUTE).getAmount(); + + extracted.setAmount(extracted.getAmount() + remaining); + transferableAmount += inventoryFrom.extractChemical(extracted, Action.EXECUTE).getAmount(); + } + + return transferableAmount; + } + + return transferableAmount; + } + + public static IChemicalHandler extractHandler(@Nullable Object object, @Nullable Level level, @Nullable BlockPos pos, @Nullable Direction direction) { + if (object instanceof IChemicalHandler itemHandler) + return itemHandler; + if (object instanceof BlockEntity blockEntity && level == null && pos == null) { + pos = blockEntity.getBlockPos(); + level = blockEntity.getLevel(); + } + if (level != null && pos != null) { + return level.getCapability(Capabilities.CHEMICAL.block(), pos, direction != null ? direction : Direction.NORTH); + } + return null; + } + + @Nullable + public static IChemicalHandler getHandlerFromDirection(@NotNull String direction, @NotNull IPeripheralOwner owner) throws LuaException { + Level level = owner.getLevel(); + Objects.requireNonNull(level); + Direction relativeDirection = CoordUtil.getDirection(owner.getOrientation(), direction); + BlockEntity target = level.getBlockEntity(owner.getPos().relative(relativeDirection)); + if (target == null) + return null; + + return extractHandler(target, level, owner.getPos().relative(relativeDirection), relativeDirection); + } + + @Nullable + public static IChemicalHandler getHandlerFromName(@NotNull IComputerAccess access, String name) throws LuaException { + IPeripheral location = access.getAvailablePeripheral(name); + + // Tanks/Block Entities can't be accessed if the bridge is not exposed to the same network as the target tank/block entity + // This can occur when the bridge was wrapped via a side and not via modems + if (location == null) + return null; + + IChemicalHandler handler = extractHandler(location.getTarget(), null, null, null); + if (handler == null) + throw new LuaException("Target '" + name + "' is not a chemical handler"); + return handler; + } + + public static ChemicalStack toChemicalStack(Chemical chemical, long amount) { + return new ChemicalStack(MekanismAPI.CHEMICAL_REGISTRY.wrapAsHolder(chemical), amount); + } + + @NotNull + public static String getFingerprint(@NotNull ChemicalStack stack) { + // A pretty lame fingerprint, a chemical stack does not have any components or other stuff + String fingerprint = getRegistryKey(stack).toString(); + try { + byte[] bytesOfHash = fingerprint.getBytes(StandardCharsets.UTF_8); + MessageDigest md = MessageDigest.getInstance("MD5"); + return StringUtil.toHexString(md.digest(bytesOfHash)); + } catch (NoSuchAlgorithmException ex) { + AdvancedPeripherals.debug("Could not parse fingerprint", ex); + } + return ""; + } + + public static ResourceLocation getRegistryKey(Chemical chemical) { + return MekanismAPI.CHEMICAL_REGISTRY.getKey(chemical); + } + + public static ResourceLocation getRegistryKey(ChemicalStack chemicalStack) { + return MekanismAPI.CHEMICAL_REGISTRY.getKey(chemicalStack.getChemical()); + } +} diff --git a/src/main/java/de/srendi/advancedperipherals/common/util/inventory/FluidFilter.java b/src/main/java/de/srendi/advancedperipherals/common/util/inventory/FluidFilter.java index ef9b87acc..ada5f1c9f 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/util/inventory/FluidFilter.java +++ b/src/main/java/de/srendi/advancedperipherals/common/util/inventory/FluidFilter.java @@ -1,8 +1,14 @@ package de.srendi.advancedperipherals.common.util.inventory; +import appeng.api.stacks.AEFluidKey; +import appeng.api.stacks.GenericStack; +import com.refinedmods.refinedstorage.api.resource.ResourceAmount; +import com.refinedmods.refinedstorage.common.support.resource.FluidResource; +import com.refinedmods.refinedstorage.neoforge.support.resource.VariantUtil; import dan200.computercraft.api.lua.LuaException; import dan200.computercraft.core.apis.TableHelper; import de.srendi.advancedperipherals.AdvancedPeripherals; +import de.srendi.advancedperipherals.common.addons.APAddons; import de.srendi.advancedperipherals.common.util.DataComponentUtil; import de.srendi.advancedperipherals.common.util.NBTUtil; import de.srendi.advancedperipherals.common.util.Pair; @@ -18,8 +24,9 @@ import java.util.Map; -//TODO tag -public class FluidFilter { +public class FluidFilter extends GenericFilter { + + public static final FluidFilter EMPTY = new FluidFilter(); private Fluid fluid = Fluids.EMPTY; private TagKey tag = null; @@ -32,10 +39,12 @@ private FluidFilter() { } public static Pair parse(Map item) { - FluidFilter fluidFilter = empty(); // If the map is empty, return a filter without any filters if (item.isEmpty()) - return Pair.of(fluidFilter, null); + return Pair.of(EMPTY, null); + + FluidFilter fluidFilter = createEmpty(); + if (item.containsKey("name")) { try { String name = TableHelper.getStringField(item, "name"); @@ -79,19 +88,41 @@ public static Pair parse(Map item) { } public static FluidFilter fromStack(FluidStack stack) { - FluidFilter filter = empty(); + FluidFilter filter = createEmpty(); filter.fluid = stack.getFluid(); filter.componentsAsNbt = DataComponentUtil.toNbt(stack.getComponentsPatch()); filter.components = stack.getComponents(); return filter; } - public static FluidFilter empty() { + public static FluidFilter createEmpty() { return new FluidFilter(); } public boolean isEmpty() { - return fingerprint.isEmpty() && fluid == Fluids.EMPTY && tag == null && componentsAsNbt == null; + return this == EMPTY || (fingerprint.isEmpty() && fluid == Fluids.EMPTY && tag == null && componentsAsNbt == null); + } + + @Override + public boolean testAE(GenericStack genericStack) { + if (!APAddons.ae2Loaded) + return false; + + if (genericStack.what() instanceof AEFluidKey aeFluidKey) { + return test(aeFluidKey.toStack(1)); + } + return false; + } + + @Override + public boolean testRS(ResourceAmount resourceAmount) { + if (!APAddons.refinedStorageLoaded) + return false; + + if (resourceAmount.resource() instanceof FluidResource fluidResource) { + return test(VariantUtil.toFluidStack(fluidResource, 1)); + } + return false; } public FluidStack toFluidStack() { diff --git a/src/main/java/de/srendi/advancedperipherals/common/util/inventory/FluidUtil.java b/src/main/java/de/srendi/advancedperipherals/common/util/inventory/FluidUtil.java index 67b6e5a08..da5dda72b 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/util/inventory/FluidUtil.java +++ b/src/main/java/de/srendi/advancedperipherals/common/util/inventory/FluidUtil.java @@ -30,6 +30,41 @@ public class FluidUtil { private FluidUtil() { } + public static int moveFluid(IFluidHandler inventoryFrom, IFluidHandler inventoryTo, FluidFilter filter) { + if (inventoryFrom == null) return 0; + + int amount = filter.getCount(); + int transferableAmount = 0; + + // The logic changes with storage systems since these systems do not have slots + if (inventoryFrom instanceof IStorageSystemFluidHandler storageSystemHandler) { + FluidStack extracted = storageSystemHandler.drain(filter, IFluidHandler.FluidAction.SIMULATE); + int inserted = inventoryTo.fill(extracted, IFluidHandler.FluidAction.EXECUTE); + + transferableAmount += storageSystemHandler.drain(filter.setCount(inserted), IFluidHandler.FluidAction.EXECUTE).getAmount(); + + return transferableAmount; + } + + if (inventoryTo instanceof IStorageSystemFluidHandler storageSystemHandler) { + if (filter.test(inventoryFrom.getFluidInTank(0))) { + FluidStack toExtract = inventoryFrom.getFluidInTank(0).copy(); + toExtract.setAmount(amount); + FluidStack extracted = inventoryFrom.drain(toExtract, IFluidHandler.FluidAction.SIMULATE); + if (extracted.isEmpty()) + return 0; + int inserted = storageSystemHandler.fill(extracted, IFluidHandler.FluidAction.EXECUTE); + + extracted.setAmount(inserted); + transferableAmount += inventoryFrom.drain(extracted, IFluidHandler.FluidAction.EXECUTE).getAmount(); + } + + return transferableAmount; + } + + return transferableAmount; + } + public static IFluidHandler extractHandler(@Nullable Object object, @Nullable Level level, @Nullable BlockPos pos, @Nullable Direction direction) { if (object instanceof IFluidHandler itemHandler) return itemHandler; @@ -43,19 +78,16 @@ public static IFluidHandler extractHandler(@Nullable Object object, @Nullable Le return null; } - @NotNull + @Nullable public static IFluidHandler getHandlerFromDirection(@NotNull String direction, @NotNull IPeripheralOwner owner) throws LuaException { Level level = owner.getLevel(); Objects.requireNonNull(level); Direction relativeDirection = CoordUtil.getDirection(owner.getOrientation(), direction); BlockEntity target = level.getBlockEntity(owner.getPos().relative(relativeDirection)); if (target == null) - throw new LuaException("Target '" + direction + "' is empty or not a fluid handler"); + return null; - IFluidHandler handler = extractHandler(target, level, owner.getPos().relative(relativeDirection), relativeDirection); - if (handler == null) - throw new LuaException("Target '" + direction + "' is not a fluid handler"); - return handler; + return extractHandler(target, level, owner.getPos().relative(relativeDirection), relativeDirection); } @Nullable diff --git a/src/main/java/de/srendi/advancedperipherals/common/util/inventory/GenericFilter.java b/src/main/java/de/srendi/advancedperipherals/common/util/inventory/GenericFilter.java new file mode 100644 index 000000000..e7cc4cce4 --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/common/util/inventory/GenericFilter.java @@ -0,0 +1,89 @@ +package de.srendi.advancedperipherals.common.util.inventory; + +import appeng.api.stacks.GenericStack; +import com.refinedmods.refinedstorage.api.resource.ResourceAmount; +import de.srendi.advancedperipherals.common.util.Pair; +import mekanism.api.MekanismAPI; +import net.minecraft.core.registries.BuiltInRegistries; + +import java.util.Map; + +public abstract class GenericFilter { + + private static final GenericFilter EMPTY = new GenericFilter<>() { + @Override + public boolean isEmpty() { + return true; + } + + @Override + public boolean testAE(GenericStack genericStack) { + return false; + } + + @Override + public boolean testRS(ResourceAmount resourceAmount) { + return false; + } + + @Override + public boolean test(Object toTest) { + return false; + } + }; + + /** + * Try to parse a raw filter table to any existing filter type. Could be a fluid filter, an item filter, maybe something else + * in the future. + *

+ * If the function can't find a valid type for the given name/resource location, it will return an empty filter with + * a proper error message. + * + * @param rawFilter The raw filter, which is a map of strings and objects + * @return A pair of the parsed filter and an error message, if there is one + */ + public static Pair, String> parseGeneric(Map rawFilter) { + + if (rawFilter.containsKey("type") && rawFilter.get("type") instanceof String type) { + switch (type) { + case "item": + return ItemFilter.parse(rawFilter); + case "fluid": + return FluidFilter.parse(rawFilter); + case "chemical": + return ChemicalFilter.parse(rawFilter); + } + } + if (!rawFilter.containsKey("name")) + return Pair.of(empty(), "NO_NAME_OR_TYPE"); + + String name = rawFilter.get("name").toString(); + + // Let's check in which registry this thing is + if (ItemUtil.getRegistryEntry(name, BuiltInRegistries.ITEM) != null) { + return ItemFilter.parse(rawFilter); + } else if (ItemUtil.getRegistryEntry(name, BuiltInRegistries.FLUID) != null) { + return FluidFilter.parse(rawFilter); + } else if (ItemUtil.getRegistryEntry(name, MekanismAPI.CHEMICAL_REGISTRY) != null) { + return ChemicalFilter.parse(rawFilter); + } else { + // If the name is in neither of the registries, we will just return an empty filter + return Pair.of(empty(), "NO_VALID_FILTER_TYPE"); + } + } + + public abstract boolean isEmpty(); + + // AE2 stuff + public abstract boolean testAE(GenericStack genericStack); + + // RS stuff + public abstract boolean testRS(ResourceAmount resourceAmount); + + public abstract boolean test(T toTest); + + public static GenericFilter empty() { + return EMPTY; + } + +} diff --git a/src/main/java/de/srendi/advancedperipherals/common/util/inventory/IStorageSystemChemicalHandler.java b/src/main/java/de/srendi/advancedperipherals/common/util/inventory/IStorageSystemChemicalHandler.java new file mode 100644 index 000000000..a87de0b8a --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/common/util/inventory/IStorageSystemChemicalHandler.java @@ -0,0 +1,60 @@ +package de.srendi.advancedperipherals.common.util.inventory; + +import mekanism.api.Action; +import mekanism.api.chemical.ChemicalStack; +import mekanism.api.chemical.IChemicalHandler; + +public interface IStorageSystemChemicalHandler extends IChemicalHandler { + + /** + * Used to extract an item from the system via a peripheral. + * Uses a filter to find the right item. The amount should never be greater than 64 + * stack sizes greater than 64. + * + * @param filter The parsed filter + * @param count The amount to extract + * @param simulate Should this action be simulated + * @return extracted from the slot, must be empty if nothing can be extracted. The returned ItemStack can be safely modified after, so item handlers should return a new or copied stack. + */ + ChemicalStack extractChemical(ChemicalFilter filter, long count, Action simulate); + + @Override + default int getChemicalTanks() { + return 1; + } + + @Override + default long getChemicalTankCapacity(int tank) { + return Integer.MAX_VALUE; + } + + @Override + default ChemicalStack getChemicalInTank(int tank) { + return ChemicalStack.EMPTY; + } + + @Override + default ChemicalStack extractChemical(long amount, Action action) { + return ChemicalStack.EMPTY; + } + + @Override + default ChemicalStack extractChemical(ChemicalStack stack, Action action) { + return ChemicalStack.EMPTY; + } + + @Override + default ChemicalStack extractChemical(int tank, long amount, Action action) { + return ChemicalStack.EMPTY; + } + + @Override + default void setChemicalInTank(int tank, ChemicalStack stack) { + + } + + @Override + default boolean isValid(int tank, ChemicalStack stack) { + return true; + } +} diff --git a/src/main/java/de/srendi/advancedperipherals/common/util/inventory/IStorageSystemPeripheral.java b/src/main/java/de/srendi/advancedperipherals/common/util/inventory/IStorageSystemPeripheral.java new file mode 100644 index 000000000..656755901 --- /dev/null +++ b/src/main/java/de/srendi/advancedperipherals/common/util/inventory/IStorageSystemPeripheral.java @@ -0,0 +1,125 @@ +package de.srendi.advancedperipherals.common.util.inventory; + +import dan200.computercraft.api.lua.IArguments; +import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.lua.MethodResult; +import dan200.computercraft.api.peripheral.IComputerAccess; +import de.srendi.advancedperipherals.common.addons.computercraft.peripheral.MEBridgePeripheral; + +/** + * This class is stolen from the dev/0.8 branch to make the 0.8 port + * to 1.21.1 a bit easier + *

+ * Implementation for common storage peripheral functions. Used for AE2 {@link MEBridgePeripheral} + * and RS {@link de.srendi.advancedperipherals.common.addons.computercraft.peripheral.RSBridgePeripheral} + *

+ * This ensures that these both bridges use the same methods. This makes it easier to support both in the same script + * In case there is a new mod which adds new ways to store and craft items, this ensures that the new peripheral + * has the same functions as the other ones + *

+ * Implemented functions need to override {@link dan200.computercraft.api.lua.LuaFunction} + */ +public interface IStorageSystemPeripheral { + + boolean isConnected(); + + MethodResult isOnline(); + + MethodResult getItem(IArguments arguments) throws LuaException; + + MethodResult getFluid(IArguments arguments) throws LuaException; + + MethodResult getChemical(IArguments arguments) throws LuaException; + + MethodResult listItems(IArguments arguments) throws LuaException; + + MethodResult listFluids(IArguments arguments) throws LuaException; + + MethodResult listChemicals(IArguments arguments) throws LuaException; + + MethodResult listCraftableItems(IArguments arguments) throws LuaException; + + MethodResult listCraftableFluids(IArguments arguments) throws LuaException; + + MethodResult listCraftableChemicals(IArguments arguments) throws LuaException; + + MethodResult listCells(); + + MethodResult listDrives(); + + MethodResult importItem(IComputerAccess computer, IArguments arguments) throws LuaException; + + MethodResult exportItem(IComputerAccess computer, IArguments arguments) throws LuaException; + + MethodResult importFluid(IComputerAccess computer, IArguments arguments) throws LuaException; + + MethodResult exportFluid(IComputerAccess computer, IArguments arguments) throws LuaException; + + MethodResult importChemical(IComputerAccess computer, IArguments arguments) throws LuaException; + + MethodResult exportChemical(IComputerAccess computer, IArguments arguments) throws LuaException; + + MethodResult getStoredEnergy(); + + MethodResult getEnergyCapacity(); + + MethodResult getEnergyUsage(); + + MethodResult getAverageEnergyInput(); + + MethodResult getTotalExternalItemStorage(); + + MethodResult getTotalExternalFluidStorage(); + + MethodResult getTotalExternalChemicalStorage(); + + MethodResult getTotalItemStorage(); + + MethodResult getTotalFluidStorage(); + + MethodResult getTotalChemicalStorage(); + + MethodResult getUsedExternalItemStorage(); + + MethodResult getUsedExternalFluidStorage(); + + MethodResult getUsedExternalChemicalStorage(); + + MethodResult getUsedItemStorage(); + + MethodResult getUsedFluidStorage(); + + MethodResult getUsedChemicalStorage(); + + MethodResult getAvailableExternalItemStorage(); + + MethodResult getAvailableExternalFluidStorage(); + + MethodResult getAvailableExternalChemicalStorage(); + + MethodResult getAvailableItemStorage(); + + MethodResult getAvailableFluidStorage(); + + MethodResult getAvailableChemicalStorage(); + + MethodResult getCraftingTasks(); + + // A function to get our BasicCraftJob object with the id + MethodResult getCraftingTask(int id); + + MethodResult cancelCraftingTasks(IArguments arguments) throws LuaException; + + MethodResult craftItem(IComputerAccess computer, IArguments arguments) throws LuaException; + + MethodResult craftFluid(IComputerAccess computer, IArguments arguments) throws LuaException; + + MethodResult craftChemical(IComputerAccess computer, IArguments arguments) throws LuaException; + + MethodResult isCraftable(IArguments arguments) throws LuaException; + + MethodResult isCrafting(IArguments arguments) throws LuaException; + + MethodResult getPatterns(IArguments arguments) throws LuaException; + +} diff --git a/src/main/java/de/srendi/advancedperipherals/common/util/inventory/InventoryUtil.java b/src/main/java/de/srendi/advancedperipherals/common/util/inventory/InventoryUtil.java index 9f4a1819a..e8a6fb7a1 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/util/inventory/InventoryUtil.java +++ b/src/main/java/de/srendi/advancedperipherals/common/util/inventory/InventoryUtil.java @@ -12,8 +12,6 @@ import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; import net.neoforged.neoforge.capabilities.Capabilities; -import net.neoforged.neoforge.fluids.FluidStack; -import net.neoforged.neoforge.fluids.capability.IFluidHandler; import net.neoforged.neoforge.items.IItemHandler; import net.neoforged.neoforge.items.ItemHandlerHelper; import net.neoforged.neoforge.items.wrapper.InvWrapper; @@ -77,10 +75,10 @@ public static int moveItem(IItemHandler inventoryFrom, IItemHandler inventoryTo, ItemStack extracted = inventoryFrom.extractItem(i, amount - transferableAmount, true); if (extracted.isEmpty()) continue; - ItemStack inserted = storageSystemHandler.insertItem(toSlot, extracted, false); + ItemStack remaining = storageSystemHandler.insertItem(toSlot, extracted, false); - amount -= inserted.getCount(); - transferableAmount += inventoryFrom.extractItem(i, extracted.getCount() - inserted.getCount(), false).getCount(); + amount -= remaining.getCount(); + transferableAmount += inventoryFrom.extractItem(i, extracted.getCount() - remaining.getCount(), false).getCount(); if (transferableAmount >= filter.getCount()) break; } @@ -108,42 +106,6 @@ public static int moveItem(IItemHandler inventoryFrom, IItemHandler inventoryTo, return transferableAmount; } - public static int moveFluid(IFluidHandler inventoryFrom, IFluidHandler inventoryTo, FluidFilter filter) { - if (inventoryFrom == null) return 0; - - int amount = filter.getCount(); - int transferableAmount = 0; - - // The logic changes with storage systems since these systems do not have slots - if (inventoryFrom instanceof IStorageSystemFluidHandler storageSystemHandler) { - FluidStack extracted = storageSystemHandler.drain(filter, IFluidHandler.FluidAction.SIMULATE); - int inserted = inventoryTo.fill(extracted, IFluidHandler.FluidAction.EXECUTE); - - transferableAmount += storageSystemHandler.drain(filter.setCount(inserted), IFluidHandler.FluidAction.EXECUTE).getAmount(); - - return transferableAmount; - } - - if (inventoryTo instanceof IStorageSystemFluidHandler storageSystemHandler) { - if (filter.test(inventoryFrom.getFluidInTank(0))) { - FluidStack toExtract = inventoryFrom.getFluidInTank(0).copy(); - toExtract.setAmount(amount); - FluidStack extracted = inventoryFrom.drain(toExtract, IFluidHandler.FluidAction.SIMULATE); - if (extracted.isEmpty()) - return 0; - int inserted = storageSystemHandler.fill(extracted, IFluidHandler.FluidAction.EXECUTE); - - extracted.setAmount(inserted); - transferableAmount += inventoryFrom.drain(extracted, IFluidHandler.FluidAction.EXECUTE).getAmount(); - } - - return transferableAmount; - } - - return transferableAmount; - } - - @Nullable public static IItemHandler getHandlerFromName(@NotNull IComputerAccess access, String name) throws LuaException { IPeripheral location = access.getAvailablePeripheral(name); @@ -158,6 +120,8 @@ public static IItemHandler getHandlerFromDirection(@NotNull String direction, @N Level level = owner.getLevel(); Objects.requireNonNull(level); Direction relativeDirection = CoordUtil.getDirection(owner.getOrientation(), direction); + if (relativeDirection == null) + return null; BlockEntity target = level.getBlockEntity(owner.getPos().relative(relativeDirection)); if (target == null) return null; diff --git a/src/main/java/de/srendi/advancedperipherals/common/util/inventory/ItemFilter.java b/src/main/java/de/srendi/advancedperipherals/common/util/inventory/ItemFilter.java index 10eabea12..b73897f58 100644 --- a/src/main/java/de/srendi/advancedperipherals/common/util/inventory/ItemFilter.java +++ b/src/main/java/de/srendi/advancedperipherals/common/util/inventory/ItemFilter.java @@ -1,8 +1,13 @@ package de.srendi.advancedperipherals.common.util.inventory; +import appeng.api.stacks.AEItemKey; +import appeng.api.stacks.GenericStack; +import com.refinedmods.refinedstorage.api.resource.ResourceAmount; +import com.refinedmods.refinedstorage.common.support.resource.ItemResource; import dan200.computercraft.api.lua.LuaException; import dan200.computercraft.core.apis.TableHelper; import de.srendi.advancedperipherals.AdvancedPeripherals; +import de.srendi.advancedperipherals.common.addons.APAddons; import de.srendi.advancedperipherals.common.util.DataComponentUtil; import de.srendi.advancedperipherals.common.util.NBTUtil; import de.srendi.advancedperipherals.common.util.Pair; @@ -18,8 +23,9 @@ import java.util.Map; -//TODO tag -public class ItemFilter { +public class ItemFilter extends GenericFilter { + + public static final ItemFilter EMPTY = new ItemFilter(); private Item item = Items.AIR; private TagKey tag = null; @@ -34,10 +40,12 @@ private ItemFilter() { } public static Pair parse(Map item) { - ItemFilter itemFilter = empty(); // If the map is empty, return a filter without any filters if (item.isEmpty()) - return Pair.of(itemFilter, null); + return Pair.of(EMPTY, null); + + ItemFilter itemFilter = createEmpty(); + if (item.containsKey("name")) { try { String name = TableHelper.getStringField(item, "name"); @@ -95,19 +103,41 @@ public static Pair parse(Map item) { } public static ItemFilter fromStack(ItemStack stack) { - ItemFilter filter = empty(); + ItemFilter filter = createEmpty(); filter.item = stack.getItem(); filter.componentsAsNbt = DataComponentUtil.toNbt(stack.getComponentsPatch()); filter.components = (PatchedDataComponentMap) stack.getComponents(); return filter; } - public static ItemFilter empty() { + public static ItemFilter createEmpty() { return new ItemFilter(); } public boolean isEmpty() { - return fingerprint.isEmpty() && item == Items.AIR && tag == null && componentsAsNbt == null; + return this == EMPTY || (fingerprint.isEmpty() && item == Items.AIR && tag == null && componentsAsNbt == null); + } + + @Override + public boolean testAE(GenericStack genericStack) { + if (!APAddons.ae2Loaded) + return false; + + if (genericStack.what() instanceof AEItemKey aeItemKey) { + return test(aeItemKey.toStack()); + } + return false; + } + + @Override + public boolean testRS(ResourceAmount resourceAmount) { + if (!APAddons.refinedStorageLoaded) + return false; + + if (resourceAmount.resource() instanceof ItemResource itemResource) { + return test(itemResource.toItemStack(1)); + } + return false; } public ItemStack toItemStack() { @@ -119,6 +149,9 @@ public ItemStack toItemStack() { } public boolean test(ItemStack stack) { + if (isEmpty()) + return true; + if (!fingerprint.isEmpty()) { String testFingerprint = ItemUtil.getFingerprint(stack); return fingerprint.equals(testFingerprint);