From 7f9856412244d156b5e09e2e9e406181273c8200 Mon Sep 17 00:00:00 2001 From: gnembon Date: Fri, 12 Jun 2020 22:46:03 -0300 Subject: [PATCH 1/4] seed looses resolution - returned as string. --- src/main/java/carpet/script/CarpetExpression.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java index 711d07d6..2f9fedca 100644 --- a/src/main/java/carpet/script/CarpetExpression.java +++ b/src/main/java/carpet/script/CarpetExpression.java @@ -2944,7 +2944,7 @@ public class CarpetExpression this.expr.addLazyFunction("seed", -1, (c, t, lv) -> { ServerCommandSource s = ((CarpetContext)c).s; - Value ret = new NumericValue(s.getWorld().getSeed()); + Value ret = new StringValue(Long.toString(s.getWorld().getSeed())); return (cc, tt) -> ret; }); From fff3ccf3f6c386faee4a206df9ac67f5286f615a Mon Sep 17 00:00:00 2001 From: gnembon Date: Fri, 12 Jun 2020 22:46:41 -0300 Subject: [PATCH 2/4] backbone for client API commands with test --- ...ClientPlayerEntity_clientCommandMixin.java | 5 ++ .../java/carpet/network/CarpetClient.java | 27 +++++++ .../carpet/network/ClientNetworkHandler.java | 16 ++++ .../carpet/network/ServerNetworkHandler.java | 80 +++++++++++++++++++ 4 files changed, 128 insertions(+) diff --git a/src/main/java/carpet/mixins/ClientPlayerEntity_clientCommandMixin.java b/src/main/java/carpet/mixins/ClientPlayerEntity_clientCommandMixin.java index 16a41708..94bd965b 100644 --- a/src/main/java/carpet/mixins/ClientPlayerEntity_clientCommandMixin.java +++ b/src/main/java/carpet/mixins/ClientPlayerEntity_clientCommandMixin.java @@ -14,6 +14,11 @@ public class ClientPlayerEntity_clientCommandMixin @Inject(method = "sendChatMessage", at = @At("HEAD")) private void inspectMessage(String string, CallbackInfo ci) { + if (string.startsWith("/call")) + { + String command = string.substring(6); + CarpetClient.sendClientCommand(command); + } if (CarpetServer.minecraft_server == null && !CarpetClient.isCarpet()) { ClientPlayerEntity playerSource = (ClientPlayerEntity)(Object) this; diff --git a/src/main/java/carpet/network/CarpetClient.java b/src/main/java/carpet/network/CarpetClient.java index 7010eb31..47f74fd4 100644 --- a/src/main/java/carpet/network/CarpetClient.java +++ b/src/main/java/carpet/network/CarpetClient.java @@ -1,8 +1,13 @@ package carpet.network; import carpet.CarpetServer; +import carpet.CarpetSettings; import carpet.script.utils.ShapesRenderer; import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.text.Text; import net.minecraft.util.Identifier; public class CarpetClient @@ -51,4 +56,26 @@ public class CarpetClient { return isServerCarpet; } + + public static boolean sendClientCommand(String command) + { + if (!isServerCarpet && CarpetServer.minecraft_server == null) return false; + ClientNetworkHandler.clientCommand(command); + return true; + } + + public static void onClientCommand(Tag t) + { + CarpetSettings.LOG.info("Server Response:"); + CompoundTag tag = (CompoundTag)t; + CarpetSettings.LOG.info(" - id: "+tag.getString("id")); + CarpetSettings.LOG.info(" - code: "+tag.getInt("code")); + if (tag.contains("error")) CarpetSettings.LOG.warn(" - error: "+tag.getString("error")); + if (tag.contains("output")) + { + ListTag outputTag = (ListTag) tag.get("output"); + for (int i = 0; i < outputTag.size(); i++) + CarpetSettings.LOG.info(" - response: " + Text.Serializer.fromJson(outputTag.getString(i)).getString()); + } + } } diff --git a/src/main/java/carpet/network/ClientNetworkHandler.java b/src/main/java/carpet/network/ClientNetworkHandler.java index c65e2ac6..e105069d 100644 --- a/src/main/java/carpet/network/ClientNetworkHandler.java +++ b/src/main/java/carpet/network/ClientNetworkHandler.java @@ -44,6 +44,9 @@ public class ClientNetworkHandler if (CarpetClient.shapes != null) CarpetClient.shapes.addShapes((ListTag) t); }); + put("clientCommand", (p, t) -> { + CarpetClient.onClientCommand(t); + }); }}; @@ -99,4 +102,17 @@ public class ClientNetworkHandler CarpetSettings.LOG.error("Unknown carpet data: "+key); } } + + public static void clientCommand(String command) + { + CompoundTag tag = new CompoundTag(); + tag.putString("id", command); + tag.putString("command", command); + CompoundTag outer = new CompoundTag(); + outer.put("clientCommand", tag); + CarpetClient.getPlayer().networkHandler.sendPacket(new CustomPayloadC2SPacket( + CarpetClient.CARPET_CHANNEL, + (new PacketByteBuf(Unpooled.buffer())).writeVarInt(CarpetClient.DATA).writeCompoundTag(outer) + )); + } } diff --git a/src/main/java/carpet/network/ServerNetworkHandler.java b/src/main/java/carpet/network/ServerNetworkHandler.java index 6de0b77b..b5427e6f 100644 --- a/src/main/java/carpet/network/ServerNetworkHandler.java +++ b/src/main/java/carpet/network/ServerNetworkHandler.java @@ -6,21 +6,35 @@ import carpet.helpers.TickSpeed; import carpet.settings.ParsedRule; import io.netty.buffer.Unpooled; import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.StringTag; import net.minecraft.nbt.Tag; import net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket; +import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.text.Text; import net.minecraft.util.PacketByteBuf; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.BiConsumer; public class ServerNetworkHandler { private static Map remoteCarpetPlayers = new HashMap<>(); private static Set validCarpetPlayers = new HashSet<>(); + private static Map> dataHandlers = new HashMap>(){{ + put("clientCommand", (p, t) -> { + handleClientCommand(p, (CompoundTag)t); + }); + }}; + public static void handleData(PacketByteBuf data, ServerPlayerEntity player) { if (data != null) @@ -28,8 +42,13 @@ public class ServerNetworkHandler int id = data.readVarInt(); if (id == CarpetClient.HELLO) onHello(player, data); + if (id == CarpetClient.DATA) + onClientData(player, data); } } + + + public static void onPlayerJoin(ServerPlayerEntity playerEntity) { if (!playerEntity.networkHandler.connection.isLocal()) @@ -60,6 +79,67 @@ public class ServerNetworkHandler CarpetServer.settingsManager.getRules().forEach(data::withRule); playerEntity.networkHandler.sendPacket(new CustomPayloadS2CPacket(CarpetClient.CARPET_CHANNEL, data.build() )); } + + private static void handleClientCommand(ServerPlayerEntity player, CompoundTag commandData) + { + String command = commandData.getString("command"); + String id = commandData.getString("id"); + List output = new ArrayList<>(); + String[] error = {null}; + int resultCode = -1; + if (player.getServer() == null) + { + error[0] = "No Server"; + } + else + { + resultCode = player.getServer().getCommandManager().execute( + new ServerCommandSource(player, player.getPos(), player.getRotationClient(), + player.world instanceof ServerWorld ? (ServerWorld) player.world : null, + player.server.getPermissionLevel(player.getGameProfile()), player.getName().getString(), player.getDisplayName(), + player.world.getServer(), player) + { + @Override + public void sendError(Text message) + { + error[0] = message.getString(); + } + @Override + public void sendFeedback(Text message, boolean broadcastToOps) + { + output.add(message); + } + }, + command + ); + }; + CompoundTag result = new CompoundTag(); + result.putString("id", id); + result.putInt("code", resultCode); + if (error[0] != null) result.putString("error", error[0]); + ListTag outputResult = new ListTag(); + for (Text line: output) outputResult.add(StringTag.of(Text.Serializer.toJson(line))); + if (!output.isEmpty()) result.put("output", outputResult); + player.networkHandler.sendPacket(new CustomPayloadS2CPacket( + CarpetClient.CARPET_CHANNEL, + DataBuilder.create().withCustomNbt("clientCommand", result).build() + )); + // run command plug to command output, + } + + + private static void onClientData(ServerPlayerEntity player, PacketByteBuf data) + { + CompoundTag compound = data.readCompoundTag(); + if (compound == null) return; + for (String key: compound.getKeys()) + { + if (dataHandlers.containsKey(key)) + dataHandlers.get(key).accept(player, compound.get(key)); + else + CarpetSettings.LOG.warn("Unknown carpet client data: "+key); + } + } public static void updateRuleWithConnectedClients(ParsedRule rule) { From 690c85f3b0d25cc438c9ab781c6ca9172ad6e0ba Mon Sep 17 00:00:00 2001 From: gnembon Date: Sun, 14 Jun 2020 01:15:10 -0300 Subject: [PATCH 3/4] added __on_player_chooses_recipe and __on_player_switches_slot events --- docs/scarpet/api/Inventories.md | 44 +++++++++++++++++++ ...PlayNetworkHandler_scarpetEventsMixin.java | 29 ++++++++++++ .../java/carpet/script/CarpetEventServer.java | 33 ++++++++++++++ .../assets/carpet/scripts/event_test.sc | 20 +++++++++ 4 files changed, 126 insertions(+) diff --git a/docs/scarpet/api/Inventories.md b/docs/scarpet/api/Inventories.md index 5ef96c7a..fcc1fd53 100644 --- a/docs/scarpet/api/Inventories.md +++ b/docs/scarpet/api/Inventories.md @@ -38,6 +38,50 @@ item_category('ender_pearl') => misc item_category('stone') => building_blocks +### `recipe_data(item, type?)`, `recipe_data(recipe, type?)` + +returns all recipes matching either an `item`, or represent actual `recipe` name. In vanilla datapack, for all items +that have one recipe available, the recipe name is the same as the item name but if an item has multiple recipes, its +direct name can be different. + +Recipe type can take one of the following options: + * `'crafting'` - default, crafting table recipe + * `'smelting'` - furnace recipe + * `'blasting'` - blast furnace recipe + * `'smoking'` - smoker recipe + * `'campfire_cooking'` - campfire recipe + * `'stonecutting'` - stonecutter recipe + + The return value is a list of available recipes (even if there is only one recipe available). Each recipe contains of + an item triple of the crafting result, list of ingredients, each containing a list of possible variants of the + ingredients in this slot, as item triples, or `null` if its a shaped recipe and a given slot in the patterns is left + empty, and recipe specification as another list. Possible recipe specs is: + * `['shaped', width, height]` - shaped crafting. `width` and `height` can be 1, 2 or 3. + * `['shapeless']` - shapeless crafting + * `['smelting', duration, xp]` - smelting/cooking recipes + * `['cutting']` - stonecutter recipe + * `['special']` - special crafting recipe, typically not present in the crafting menu + * `['custom']` - other recipe types + +Note that ingredients are specified as tripes, with count and nbt information. Currently all recipes require always one +of the ingredients, and for some recipes, even if the nbt data for the ingredient is specified (e.g. `dispenser`), it +can accept items of any tags. + +Also note that some recipes leave some products in the crafting window, and these can be determined using + `crafting_remaining_item()` function + + Examples: +
+ recipe_data('iron_ingot_from_nuggets')
+ recipe_data('iron_ingot')
+ recipe_data('glass', 'smelting')
+ 
+ +### `crafting_remaining_item(item)` + +returns `null` if the item has no remaining item in the crafting window when used as a crafting ingredient, or an +item name that serves as a replacement after crafting is done. Currently it can only be buckets and glass bottles. + ### `inventory_size(inventory)` Returns the size of the inventory for the entity or block in question. Returns null if the block or entity don't diff --git a/src/main/java/carpet/mixins/ServerPlayNetworkHandler_scarpetEventsMixin.java b/src/main/java/carpet/mixins/ServerPlayNetworkHandler_scarpetEventsMixin.java index 6128944c..e5ca554a 100644 --- a/src/main/java/carpet/mixins/ServerPlayNetworkHandler_scarpetEventsMixin.java +++ b/src/main/java/carpet/mixins/ServerPlayNetworkHandler_scarpetEventsMixin.java @@ -1,6 +1,9 @@ package carpet.mixins; import net.minecraft.item.ItemStack; +import net.minecraft.network.packet.c2s.play.ButtonClickC2SPacket; +import net.minecraft.network.packet.c2s.play.CraftRequestC2SPacket; +import net.minecraft.network.packet.c2s.play.UpdateSelectedSlotC2SPacket; import net.minecraft.server.network.ServerPlayNetworkHandler; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.network.packet.c2s.play.ClientCommandC2SPacket; @@ -29,10 +32,12 @@ import static carpet.script.CarpetEventServer.Event.PLAYER_RELEASED_ITEM; import static carpet.script.CarpetEventServer.Event.PLAYER_RIDES; import static carpet.script.CarpetEventServer.Event.PLAYER_JUMPS; import static carpet.script.CarpetEventServer.Event.PLAYER_RIGHT_CLICKS_BLOCK; +import static carpet.script.CarpetEventServer.Event.PLAYER_CHOOSES_RECIPE; import static carpet.script.CarpetEventServer.Event.PLAYER_STARTS_SNEAKING; import static carpet.script.CarpetEventServer.Event.PLAYER_STARTS_SPRINTING; import static carpet.script.CarpetEventServer.Event.PLAYER_STOPS_SNEAKING; import static carpet.script.CarpetEventServer.Event.PLAYER_STOPS_SPRINTING; +import static carpet.script.CarpetEventServer.Event.PLAYER_SWITCHES_SLOT; import static carpet.script.CarpetEventServer.Event.PLAYER_USES_ITEM; import static carpet.script.CarpetEventServer.Event.PLAYER_WAKES_UP; @@ -219,4 +224,28 @@ public class ServerPlayNetworkHandler_scarpetEventsMixin //todo add hit and hand in the future PLAYER_ATTACKS_ENTITY.onEntityAction(player, playerInteractEntityC2SPacket_1.getEntity(player.getServerWorld()), null); } + + @Inject(method = "onButtonClick", at = @At("HEAD")) + private void onItemBeingPickedFromInventory(ButtonClickC2SPacket packet, CallbackInfo ci) + { + // crafts not int the crafting window + //CarpetSettings.LOG.error("Player clicks button "+packet.getButtonId()); + } + @Inject(method = "onCraftRequest", at = @At("HEAD")) + private void onRecipeSelectedInRecipeManager(CraftRequestC2SPacket packet, CallbackInfo ci) + { + if (PLAYER_CHOOSES_RECIPE.isNeeded()) + { + PLAYER_CHOOSES_RECIPE.onRecipeSelected(player, packet.getRecipe(), packet.shouldCraftAll()); + } + } + + @Inject(method = "onUpdateSelectedSlot", at = @At("HEAD")) + private void onUpdatedSelectedSLot(UpdateSelectedSlotC2SPacket packet, CallbackInfo ci) + { + if (PLAYER_SWITCHES_SLOT.isNeeded()) + { + PLAYER_SWITCHES_SLOT.onSlotSwitch(player, player.inventory.selectedSlot, packet.getSelectedSlot()); + } + } } diff --git a/src/main/java/carpet/script/CarpetEventServer.java b/src/main/java/carpet/script/CarpetEventServer.java index 2eafcd6d..75171171 100644 --- a/src/main/java/carpet/script/CarpetEventServer.java +++ b/src/main/java/carpet/script/CarpetEventServer.java @@ -488,6 +488,37 @@ public class CarpetEventServer handler.call( () -> Collections.singletonList(((c, t) -> new EntityValue(player))), player::getCommandSource); } }, + PLAYER_CHOOSES_RECIPE("player_chooses_recipe", 3, false) + { + @Override + public void onRecipeSelected(ServerPlayerEntity player, Identifier recipe, boolean fullStack) + { + handler.call( () -> + { + return Arrays.asList( + ((c, t) -> new EntityValue(player)), + ((c, t) -> new StringValue(NBTSerializableValue.nameFromRegistryId(recipe))), + ((c, t) -> new NumericValue(fullStack)) + ); + }, player::getCommandSource); + } + }, + PLAYER_SWITCHES_SLOT("player_switches_slot", 3, false) + { + @Override + public void onSlotSwitch(ServerPlayerEntity player, int from, int to) + { + if (from == to) return; // initial slot update + handler.call( () -> + { + return Arrays.asList( + ((c, t) -> new EntityValue(player)), + ((c, t) -> new NumericValue(from)), + ((c, t) -> new NumericValue(to)) + ); + }, player::getCommandSource); + } + }, PLAYER_TAKES_DAMAGE("player_takes_damage", 4, false) { @Override @@ -647,6 +678,8 @@ public class CarpetEventServer public void onEntityAction(ServerPlayerEntity player, Entity entity, Hand enumhand) { } public void onDimensionChange(ServerPlayerEntity player, Vec3d from, Vec3d to, DimensionType fromDim, DimensionType dimTo) {} public void onDamage(Entity target, float amount, DamageSource source) { } + public void onRecipeSelected(ServerPlayerEntity player, Identifier recipe, boolean fullStack) {} + public void onSlotSwitch(ServerPlayerEntity player, int from, int to) {} public void onWorldEvent(ServerWorld world, BlockPos pos) { } diff --git a/src/main/resources/assets/carpet/scripts/event_test.sc b/src/main/resources/assets/carpet/scripts/event_test.sc index 1659d565..c6934911 100644 --- a/src/main/resources/assets/carpet/scripts/event_test.sc +++ b/src/main/resources/assets/carpet/scripts/event_test.sc @@ -244,3 +244,23 @@ __on_player_disconnects(player, reason) -> print('__on_player_disconnects(player)'); print('Player '+player+' disconnects because: '+reason) ); + +__on_player_chooses_recipe(player, recipe, full_stack) -> +( + print(''); + print('__on_player_chooses_recipe(player, recipe, full_stack)'); + print('player chooses recipe:'); + print(' - player: '+player); + print(' - recipe: '+recipe); + print(' - full stack: '+ full_stack); +); + +__on_player_switches_slot(player, from, to) -> +( + print(''); + print('__on_player_switches_slot(player, from, to)'); + print('player switches inventory slot:'); + print(' - player: '+player); + print(' - from: '+ from); + print(' - to: '+ to); +); From d098087e056264228c95bfca6b7ffd8ec25037c0 Mon Sep 17 00:00:00 2001 From: gnembon Date: Sun, 14 Jun 2020 01:17:36 -0300 Subject: [PATCH 4/4] added `recipe_data()` and `crafting_remaining_item()` - for convenient crafting scripts. --- build.gradle | 1 + docs/scarpet/Full.md | 46 +++++++ docs/scarpet/api/Events.md | 2 + docs/scarpet/resources/editors/idea/2.txt | 2 + docs/scarpet/resources/editors/idea/4.txt | 2 + .../scarpet/resources/editors/npp/scarpet.xml | 6 +- .../carpet/fakes/IngredientInterface.java | 11 ++ .../carpet/fakes/RecipeManagerInterface.java | 12 ++ .../mixins/Ingredient_scarpetMixin.java | 26 ++++ .../mixins/RecipeManager_scarpetMixin.java | 32 +++++ .../java/carpet/script/CarpetExpression.java | 125 ++++++++++++++++++ src/main/resources/carpet.accesswidener | 2 + src/main/resources/carpet.mixins.json | 3 + src/main/resources/fabric.mod.json | 1 + 14 files changed, 270 insertions(+), 1 deletion(-) create mode 100644 src/main/java/carpet/fakes/IngredientInterface.java create mode 100644 src/main/java/carpet/fakes/RecipeManagerInterface.java create mode 100644 src/main/java/carpet/mixins/Ingredient_scarpetMixin.java create mode 100644 src/main/java/carpet/mixins/RecipeManager_scarpetMixin.java create mode 100644 src/main/resources/carpet.accesswidener diff --git a/build.gradle b/build.gradle index 2dc45583..c9cfacd7 100644 --- a/build.gradle +++ b/build.gradle @@ -11,6 +11,7 @@ version = project.minecraft_version+'-'+project.mod_version group = project.maven_group minecraft { + accessWidener "src/main/resources/carpet.accesswidener" } dependencies { diff --git a/docs/scarpet/Full.md b/docs/scarpet/Full.md index 22deab0d..133ef35e 100644 --- a/docs/scarpet/Full.md +++ b/docs/scarpet/Full.md @@ -2940,6 +2940,50 @@ item_category('ender_pearl') => misc item_category('stone') => building_blocks +### `recipe_data(item, type?)`, `recipe_data(recipe, type?)` + +returns all recipes matching either an `item`, or represent actual `recipe` name. In vanilla datapack, for all items +that have one recipe available, the recipe name is the same as the item name but if an item has multiple recipes, its +direct name can be different. + +Recipe type can take one of the following options: + * `'crafting'` - default, crafting table recipe + * `'smelting'` - furnace recipe + * `'blasting'` - blast furnace recipe + * `'smoking'` - smoker recipe + * `'campfire_cooking'` - campfire recipe + * `'stonecutting'` - stonecutter recipe + + The return value is a list of available recipes (even if there is only one recipe available). Each recipe contains of + an item triple of the crafting result, list of ingredients, each containing a list of possible variants of the + ingredients in this slot, as item triples, or `null` if its a shaped recipe and a given slot in the patterns is left + empty, and recipe specification as another list. Possible recipe specs is: + * `['shaped', width, height]` - shaped crafting. `width` and `height` can be 1, 2 or 3. + * `['shapeless']` - shapeless crafting + * `['smelting', duration, xp]` - smelting/cooking recipes + * `['cutting']` - stonecutter recipe + * `['special']` - special crafting recipe, typically not present in the crafting menu + * `['custom']` - other recipe types + +Note that ingredients are specified as tripes, with count and nbt information. Currently all recipes require always one +of the ingredients, and for some recipes, even if the nbt data for the ingredient is specified (e.g. `dispenser`), it +can accept items of any tags. + +Also note that some recipes leave some products in the crafting window, and these can be determined using + `crafting_remaining_item()` function + + Examples: +
+ recipe_data('iron_ingot_from_nuggets')
+ recipe_data('iron_ingot')
+ recipe_data('glass', 'smelting')
+ 
+ +### `crafting_remaining_item(item)` + +returns `null` if the item has no remaining item in the crafting window when used as a crafting ingredient, or an +item name that serves as a replacement after crafting is done. Currently it can only be buckets and glass bottles. + ### `inventory_size(inventory)` Returns the size of the inventory for the entity or block in question. Returns null if the block or entity don't @@ -3055,6 +3099,8 @@ __on_player_right_clicks_block(player, item_tuple, hand, block, face, hitvec) / __on_player_interacts_with_block(player, hand, block, face, hitvec) //right click on a block resulted in activation of said block __on_player_places_block(player, item_tuple, hand, block) // player have just placed the block. __on_player_interacts_with_entity(player, entity, hand) +__on_player_chooses_recipe(player, recipe, full_stack) +__on_player_switches_slot(player, from, to) __on_player_attacks_entity(player, entity) __on_player_takes_damage(player, amount, source, source_entity) __on_player_deals_damage(player, amount, entity) diff --git a/docs/scarpet/api/Events.md b/docs/scarpet/api/Events.md index 6a616f30..05b8c4cd 100644 --- a/docs/scarpet/api/Events.md +++ b/docs/scarpet/api/Events.md @@ -32,6 +32,8 @@ __on_player_right_clicks_block(player, item_tuple, hand, block, face, hitvec) / __on_player_interacts_with_block(player, hand, block, face, hitvec) //right click on a block resulted in activation of said block __on_player_places_block(player, item_tuple, hand, block) // player have just placed the block. __on_player_interacts_with_entity(player, entity, hand) +__on_player_chooses_recipe(player, recipe, full_stack) +__on_player_switches_slot(player, from, to) __on_player_attacks_entity(player, entity) __on_player_takes_damage(player, amount, source, source_entity) __on_player_deals_damage(player, amount, entity) diff --git a/docs/scarpet/resources/editors/idea/2.txt b/docs/scarpet/resources/editors/idea/2.txt index 7e2a8d8a..2d2496c5 100644 --- a/docs/scarpet/resources/editors/idea/2.txt +++ b/docs/scarpet/resources/editors/idea/2.txt @@ -12,6 +12,7 @@ blocks_daylight blocks_movement brightness chunk_tickets +crafting_remaining_item create_marker current_dimension day_time @@ -67,6 +68,7 @@ power property query random_tick +recipe_data rect reload_chunk remove_all_markers diff --git a/docs/scarpet/resources/editors/idea/4.txt b/docs/scarpet/resources/editors/idea/4.txt index faf87463..1f659f99 100644 --- a/docs/scarpet/resources/editors/idea/4.txt +++ b/docs/scarpet/resources/editors/idea/4.txt @@ -6,6 +6,7 @@ __on_player_attacks_entity __on_player_breaks_block __on_player_clicks_block __on_player_changes_dimension +__on_player_chooses_recipe __on_player_deploys_elytra __on_player_drops_item __on_player_drops_stack @@ -21,6 +22,7 @@ __on_player_starts_sneaking __on_player_starts_sprinting __on_player_stops_sneaking __on_player_stops_sprinting +__on_player_switches_slot __on_player_uses_item __on_player_wakes_up __on_statistic diff --git a/docs/scarpet/resources/editors/npp/scarpet.xml b/docs/scarpet/resources/editors/npp/scarpet.xml index 33416a05..ffb50e58 100644 --- a/docs/scarpet/resources/editors/npp/scarpet.xml +++ b/docs/scarpet/resources/editors/npp/scarpet.xml @@ -62,7 +62,9 @@ player spawn entity_id entity_list entity_area entity_selector query modify entity_event - stack_limit item_category inventory_size inventory_has_items inventory_get inventory_set inventory_find inventory_remove drop_item + stack_limit item_category recipe_data crafting_remaining_item + + inventory_size inventory_has_items inventory_get inventory_set inventory_find inventory_remove drop_item scan volume neighbours rect diamond @@ -87,6 +89,7 @@ __on_player_breaks_block __on_player_clicks_block __on_player_changes_dimension + __on_player_chooses_recipe __on_player_deploys_elytra __on_player_drops_item __on_player_drops_stack @@ -102,6 +105,7 @@ __on_player_starts_sprinting __on_player_stops_sneaking __on_player_stops_sprinting + __on_player_switches_slot __on_player_uses_item __on_player_wakes_up __on_statistic diff --git a/src/main/java/carpet/fakes/IngredientInterface.java b/src/main/java/carpet/fakes/IngredientInterface.java new file mode 100644 index 00000000..d910883e --- /dev/null +++ b/src/main/java/carpet/fakes/IngredientInterface.java @@ -0,0 +1,11 @@ +package carpet.fakes; + +import net.minecraft.item.ItemStack; + +import java.util.Collection; +import java.util.List; + +public interface IngredientInterface +{ + List> getRecipeStacks(); +} diff --git a/src/main/java/carpet/fakes/RecipeManagerInterface.java b/src/main/java/carpet/fakes/RecipeManagerInterface.java new file mode 100644 index 00000000..77f45d21 --- /dev/null +++ b/src/main/java/carpet/fakes/RecipeManagerInterface.java @@ -0,0 +1,12 @@ +package carpet.fakes; + +import net.minecraft.recipe.Recipe; +import net.minecraft.recipe.RecipeType; +import net.minecraft.util.Identifier; + +import java.util.List; + +public interface RecipeManagerInterface +{ + List> getAllMatching(RecipeType type, Identifier output); +} diff --git a/src/main/java/carpet/mixins/Ingredient_scarpetMixin.java b/src/main/java/carpet/mixins/Ingredient_scarpetMixin.java new file mode 100644 index 00000000..f4b68577 --- /dev/null +++ b/src/main/java/carpet/mixins/Ingredient_scarpetMixin.java @@ -0,0 +1,26 @@ +package carpet.mixins; + +import carpet.fakes.IngredientInterface; +import net.minecraft.item.ItemStack; +import net.minecraft.recipe.Ingredient; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +@Mixin(Ingredient.class) +public class Ingredient_scarpetMixin implements IngredientInterface +{ + @Shadow @Final private Ingredient.Entry[] entries; + + @Override + public List> getRecipeStacks() + { + List> res = Arrays.stream(entries).map(Ingredient.Entry::getStacks).collect(Collectors.toList()); + return res; + } +} diff --git a/src/main/java/carpet/mixins/RecipeManager_scarpetMixin.java b/src/main/java/carpet/mixins/RecipeManager_scarpetMixin.java new file mode 100644 index 00000000..c31d4648 --- /dev/null +++ b/src/main/java/carpet/mixins/RecipeManager_scarpetMixin.java @@ -0,0 +1,32 @@ +package carpet.mixins; + +import carpet.fakes.RecipeManagerInterface; +import com.google.common.collect.Lists; +import net.minecraft.recipe.Recipe; +import net.minecraft.recipe.RecipeManager; +import net.minecraft.recipe.RecipeType; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Mixin(RecipeManager.class) +public class RecipeManager_scarpetMixin implements RecipeManagerInterface +{ + + @Shadow private Map, Map>> recipes; + + @Override + public List> getAllMatching(RecipeType type, Identifier output) + { + Map> typeRecipes = recipes.get(type); + if (typeRecipes.containsKey(output)) return Collections.singletonList(typeRecipes.get(output)); + return Lists.newArrayList(typeRecipes.values().stream().filter( + r -> Registry.ITEM.getId(r.getOutput().getItem()).equals(output)).collect(Collectors.toList())); + } +} diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java index 2f9fedca..7489ab81 100644 --- a/src/main/java/carpet/script/CarpetExpression.java +++ b/src/main/java/carpet/script/CarpetExpression.java @@ -2,8 +2,10 @@ package carpet.script; import carpet.CarpetServer; import carpet.fakes.ChunkTicketManagerInterface; +import carpet.fakes.IngredientInterface; import carpet.fakes.MinecraftServerInterface; import carpet.fakes.BiomeArrayInterface; +import carpet.fakes.RecipeManagerInterface; import carpet.fakes.ServerChunkManagerInterface; import carpet.fakes.StatTypeInterface; import carpet.fakes.ThreadedAnvilChunkStorageInterface; @@ -80,6 +82,13 @@ import net.minecraft.nbt.StringTag; import net.minecraft.nbt.Tag; import net.minecraft.particle.ParticleEffect; import net.minecraft.predicate.entity.EntityPredicates; +import net.minecraft.recipe.AbstractCookingRecipe; +import net.minecraft.recipe.CuttingRecipe; +import net.minecraft.recipe.Recipe; +import net.minecraft.recipe.RecipeType; +import net.minecraft.recipe.ShapedRecipe; +import net.minecraft.recipe.ShapelessRecipe; +import net.minecraft.recipe.SpecialCraftingRecipe; import net.minecraft.scoreboard.Scoreboard; import net.minecraft.scoreboard.ScoreboardCriterion; import net.minecraft.scoreboard.ScoreboardObjective; @@ -131,6 +140,7 @@ import org.apache.commons.lang3.tuple.Pair; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; @@ -1471,6 +1481,121 @@ public class CarpetExpression return (_c, _t) -> res; }); + this.expr.addLazyFunction("recipe_data", -1, (c, t, lv) -> + { + CarpetContext cc = (CarpetContext)c; + if (lv.size() < 1) throw new InternalExpressionException("'recipe_data' requires at least one argument"); + String recipeName = lv.get(0).evalValue(c).getString(); + RecipeType type = RecipeType.CRAFTING; + if (lv.size() > 1) + { + String recipeType = lv.get(1).evalValue(c).getString(); + try + { + type = Registry.RECIPE_TYPE.get(new Identifier(recipeType)); + } + catch (InvalidIdentifierException ignored) + { + throw new InternalExpressionException("Unknown crafting category: " + recipeType); + } + } + List> recipes; + try + { + recipes = ((RecipeManagerInterface) cc.s.getMinecraftServer().getRecipeManager()).getAllMatching(type, new Identifier(recipeName)); + } + catch (InvalidIdentifierException ignored) + { + return LazyValue.NULL; + } + if (recipes.isEmpty()) + return LazyValue.NULL; + List recipesOutput = new ArrayList<>(); + for (Recipe recipe: recipes) + { + ItemStack result = recipe.getOutput(); + List ingredientValue = new ArrayList<>(); + CarpetSettings.LOG.error("input size: "+recipe.getPreviewInputs().size()); + recipe.getPreviewInputs().forEach( + ingredient -> + { + // I am flattening ingredient lists per slot. + // consider recipe_data('wooden_sword','crafting') and ('iron_nugget', 'blasting') and notice difference + // in depths of lists. + List> stacks = ((IngredientInterface) (Object) ingredient).getRecipeStacks(); + if (stacks.isEmpty()) + { + ingredientValue.add(Value.NULL); + } + else + { + List alternatives = new ArrayList<>(); + stacks.forEach(col -> col.stream().map(ListValue::fromItemStack).forEach(alternatives::add)); + ingredientValue.add(ListValue.wrap(alternatives)); + } + } + ); + Value recipeSpec; + if (recipe instanceof ShapedRecipe) + { + recipeSpec = ListValue.of( + new StringValue("shaped"), + new NumericValue(((ShapedRecipe) recipe).getWidth()), + new NumericValue(((ShapedRecipe) recipe).getHeight()) + ); + } + else if (recipe instanceof ShapelessRecipe) + { + recipeSpec = ListValue.of(new StringValue("shapeless")); + } + else if (recipe instanceof AbstractCookingRecipe) + { + recipeSpec = ListValue.of( + new StringValue("smelting"), + new NumericValue(((AbstractCookingRecipe) recipe).getCookTime()), + new NumericValue(((AbstractCookingRecipe) recipe).getExperience()) + ); + } + else if (recipe instanceof CuttingRecipe) + { + recipeSpec = ListValue.of(new StringValue("cutting")); + } + else if (recipe instanceof SpecialCraftingRecipe) + { + recipeSpec = ListValue.of(new StringValue("special")); + } + else + { + recipeSpec = ListValue.of(new StringValue("custom")); + } + + recipesOutput.add(ListValue.of(ListValue.fromItemStack(result), ListValue.wrap(ingredientValue), recipeSpec)); + } + Value ret = ListValue.wrap(recipesOutput); + return (_c, _t) -> ret; + }); + + this.expr.addLazyFunction("crafting_remaining_item", 1, (c, t, lv) -> + { + String itemStr = lv.get(0).evalValue(c).getString(); + Item item; + try + { + Identifier id = new Identifier(itemStr); + item = Registry.ITEM.get(id); + if (item == Items.AIR && !id.getPath().equalsIgnoreCase("air")) + throw new InvalidIdentifierException("boo"); + } + catch (InvalidIdentifierException ignored) + { + throw new InternalExpressionException("Incorrect item: "+itemStr); + } + if (!item.hasRecipeRemainder()) return LazyValue.NULL; + Value ret = new StringValue(NBTSerializableValue.nameFromRegistryId(Registry.ITEM.getId(item.getRecipeRemainder()))); + return (_c, _t ) -> ret; + }); + + this.expr.addLazyFunction("inventory_size", -1, (c, t, lv) -> { CarpetContext cc = (CarpetContext) c; diff --git a/src/main/resources/carpet.accesswidener b/src/main/resources/carpet.accesswidener new file mode 100644 index 00000000..c03c46bb --- /dev/null +++ b/src/main/resources/carpet.accesswidener @@ -0,0 +1,2 @@ +accessWidener v1 named +Accessible class net/minecraft/recipe/Ingredient$Entry \ No newline at end of file diff --git a/src/main/resources/carpet.mixins.json b/src/main/resources/carpet.mixins.json index db91b5fe..ba447749 100644 --- a/src/main/resources/carpet.mixins.json +++ b/src/main/resources/carpet.mixins.json @@ -157,6 +157,9 @@ "ServerWorld_scarpetEventMixin", "HungerManagerMixin_scarpetEventsMixin", + "RecipeManager_scarpetMixin", + "Ingredient_scarpetMixin", + "PistonBlock_movableTEMixin", "PistonBlockEntity_movableTEMixin", "World_movableTEMixin", diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index b31f6f4c..bf4a65c6 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -24,6 +24,7 @@ "mixins": [ "carpet.mixins.json" ], + "accessWidener" : "carpet.accesswidener", "depends": { "minecraft": "1.15.x",