Jump to content

BlockyPenguin

Members
  • Posts

    138
  • Joined

  • Last visited

Everything posted by BlockyPenguin

  1. Ok, well, easier said than done. I've taken a look different crafting blocks in vanilla's code (mostly AbstractFunaceTileEntity, as the other blocks are done without a tile), and I keep writing code, getting stuck, then rewriting it, over and over. How would I go about making this work? I have a custom cauldron block, and I want a player to right-click it to add one of the items in the ItemStack they're holding to the tile's inventory, or if they're holding nothing then drop all the items in the inventory. Here's my code so far: package ... import ... public class CauldronTileEntity extends TileEntity { private LazyOptional<IItemHandler> itemHandler = LazyOptional.of(this::createItemHandler); private ArrayList<CauldronRecipe> recipes = CauldronRecipe.getAllCauldronRecipes(); private CauldronRecipe currentRecipe; public CauldronTileEntity() { super(ModRegistry.TileTypes.CAULDRON.get()); } @Override @SuppressWarnings("unchecked") public void read(CompoundNBT tag) { CompoundNBT invTag = tag.getCompound("inv"); itemHandler.ifPresent(h -> ((INBTSerializable<CompoundNBT>) h).deserializeNBT(invTag)); super.read(tag); } @Override @SuppressWarnings("unchecked") public CompoundNBT write(CompoundNBT tag) { itemHandler.ifPresent(h -> { CompoundNBT compound = ((INBTSerializable<CompoundNBT>) h).serializeNBT(); tag.put("inv", compound); }); return super.write(tag); } private IItemHandler createItemHandler() { return new ItemStackHandler(50) { @Override protected void onContentsChanged(int slot) { markDirty(); } @Override public boolean isItemValid(int slot, @Nonnull ItemStack stack) { return true; } }; } public void handleRightClick(PlayerEntity player, Hand hand) { ItemStack stack = player.getHeldItem(hand); if(stack.isEmpty()) { popOutAllItems(); }else { itemHandler.ifPresent(h -> { for(int i = 0; i <= h.getSlots(); i++) { if(h.insertItem(i, stack.split(1), true) != stack.split(1)) { player.setHeldItem(hand, h.insertItem(i, stack.split(1), false)); }else { player.sendStatusMessage(ITextComponent.Serializer.fromJsonLenient("Cauldron Full!"), true); } } }); checkRecipe(); } } private void popOutAllItems() { itemHandler.ifPresent(h -> { ItemEntity[] itemEntities = {}; for(int i = 0; i <= h.getSlots(); i++) { itemEntities[i] = new ItemEntity(world, pos.getX(), pos.getY(), pos.getZ(), h.getStackInSlot(i)); } for(ItemEntity e : itemEntities) { world.addEntity(e); } }); } private void checkRecipe() { } } handleRightClick is called from onBlockActivated in the Cauldron's block class. As you can see, checkRecipe() is empty. I'd like it so that whenever a player changes the items in the inventory, it checks whether it can do a recipe (which is why I call it from handleRightClick). How would I go about it? I've changed the json format to be this instead: { "type": "labkit:cauldron", "inputs": [ { "item": "minecraft:dirt", "count": 1 }, { "tag": "forge:seeds", "count": 3 } ], "cooking_delay": 50, "output": { "item": "minecraft:grass_block", "count": 1 } } the order of the inputs doesn't matter to me, I'd just like to know how to check if they equal a recipe's ingredients. (Also, I've just tested running the game, and my cauldron doesn't have a tile entity... I'm using DeferredRegister, and I've got these two methods in my block's class: @Override public boolean hasTileEntity(BlockState state) { return true; } @Override public TileEntity createTileEntity(BlockState state, IBlockReader world) { return new CauldronTileEntity(); } which as far as i'm aware, should do the trick...) Thanks!
  2. Ok, thanks I think I've managed to do that correctly now, as the game loads with no errors. I have no way of checking if everything is serialized/deserialized properly yet though, as I have no block to use this crafting system with. I'll make that, and post my results
  3. Ah, I get it now! Thanks! Do you know how I could write an array of CauldronRecipeSteps? buffer.writeArray or something similar doesn't seem to exist...
  4. Ok, so as it stands, I've changed some things around, and hopefully simplified the system, but I still haven't tested it, as there are still errors in my code that would stop the game from launching. Here's the problem code: @Override public CauldronRecipe read(ResourceLocation recipeId, PacketBuffer buffer) { ItemStack output = buffer.readItemStack(); return new CauldronRecipe(recipeId, output, steps); } Here's the thing, the steps variable doesn't exist, as I don't know how to get a JSONArray from a PacketBuffer (if it's even possible). I have a method I can call which takes in a JSONArray and returns an array of CauldronRecipeSteps, which is what I need to pass in. So, if it is possible, how would I get a JSONArray from a PacketBuffer, and if it's not, how else could I do this? Thanks!
  5. I did, but even so, the game wouldn't load due to the errors in my code. I guess what I probably need to know, is how to get the json data out of the PacketBuffer at so that I can insert it into "CauldronRecipeStep"s, and insert that into a "CauldronRecipeMethod". Is that possible?
  6. Hi, so, I'm trying to make my own recipe type that uses a format like this, but with as few or as many steps as you like: { "type": "labkit:cauldron", "method": { "steps": [ "one": { "input": { "item": "minecraft:dirt", "count": 1 }, "wait": 100, "waterused": 0 }, "two": { "input": { "item": "minecraft:seeds", "count": 1 }, "wait": 20, "waterused": 1 } ], "output": { "item": "minecraft:grass_block", "count": 1 } } } I've tried following some tutorials on YouTube and adapting it to use my idea of steps, but I always hit roadblocks. Any ideas? Thanks! EDIT: Here's my code: package ... import ... public class CauldronRecipe implements ICauldronRecipe { private final ResourceLocation resLoc; private CauldronRecipeMethod method; private final ItemStack output; public CauldronRecipe(ResourceLocation resLoc, CauldronRecipeMethod method, ItemStack output) { this.resLoc = resLoc; this.method = method; this.output = output; } @Override public boolean matches(RecipeWrapper inv, World world) { if(method.getSteps()[0].getInput().test(inv.getStackInSlot(0))) return true; else return false; } @Override public ItemStack getCraftingResult(RecipeWrapper inv) { return output; } @Override public ItemStack getRecipeOutput() { return output; } @Override public IRecipeSerializer<?> getSerializer() { return ModRegistry.ModRecipeSerializers.CAULDRON_RECIPE.get(); } @Override public Ingredient[] getInput() { Ingredient[] ingredients = {}; for(int i = 0; i < method.getSteps().length; i++) { ingredients[i] = method.getSteps()[i].getInput(); } return ingredients; } @Override public NonNullList<Ingredient> getIngredients() { return NonNullList.from(null, getInput()); } @Override public ResourceLocation getId() { return resLoc; } } package ... import ... public interface ICauldronRecipe extends IRecipe<RecipeWrapper> { ResourceLocation RECIPE_TYPE_RESOURCE_LOCATION = new ResourceLocation(LabKit.MODID, "cauldron"); @Nonnull @Override default IRecipeType<?> getType() { return Registry.RECIPE_TYPE.getValue(RECIPE_TYPE_RESOURCE_LOCATION).get(); } @Override default boolean canFit(int width, int height) { return false; } Ingredient[] getInput(); } package ... import ... public class CauldronRecipeSerializer extends ForgeRegistryEntry<IRecipeSerializer<?>> implements IRecipeSerializer<CauldronRecipe> { @Override public CauldronRecipe read(ResourceLocation recipeResLoc, JsonObject json) { JsonObject jsonMethod = JSONUtils.getJsonObject(json, "method"); JsonArray jsonSteps = jsonMethod.getAsJsonArray("steps"); ItemStack output = ShapedRecipe.deserializeItem(JSONUtils.getJsonObject(jsonMethod, "output")); CauldronRecipeStep[] steps = getCauldronRecipeStepsFromJson(jsonSteps); CauldronRecipeMethod method = new CauldronRecipeMethod(output, steps); return new CauldronRecipe(recipeResLoc, method, output); } // IDK how to get a CauldronRecipeMethod from the buffer, these two methods are still from the tutorial that I followed's code @Override public CauldronRecipe read(ResourceLocation recipeResLoc, PacketBuffer buffer) { ItemStack output = buffer.readItemStack(); Ingredient input = Ingredient.read(buffer); return new CauldronRecipe(recipeResLoc, input, output); } @Override public void write(PacketBuffer buffer, CauldronRecipe recipe) { Ingredient input = recipe.getIngredients().get(0); input.write(buffer); buffer.writeItemStack(recipe.getRecipeOutput(), false); } private CauldronRecipeStep[] getCauldronRecipeStepsFromJson(JsonArray stepsInput) { ArrayList<CauldronRecipeStep> stepsOutArrayList = new ArrayList<CauldronRecipeStep>(); CauldronRecipeStep[] stepsOutArray = {}; for(JsonElement step : stepsInput) { JsonObject jsonInput = step.getAsJsonObject().getAsJsonObject("input"); Ingredient input = Ingredient.deserialize(jsonInput); int waitTime = step.getAsJsonObject().get("wait").getAsInt(); int waterUsed = step.getAsJsonObject().get("waterused").getAsInt(); stepsOutArrayList.add(new CauldronRecipeStep(input, waitTime, waterUsed)); } return stepsOutArrayList.toArray(stepsOutArray); } } package ... import ... public class CauldronRecipeMethod { private CauldronRecipeStep[] steps; private ItemStack output; public CauldronRecipeMethod(ItemStack output, CauldronRecipeStep... steps) { this.steps = steps; this.output = output; } public CauldronRecipeStep[] getSteps() { return steps; } public ItemStack getOutput() { return output; } } package ... import ... public class CauldronRecipeStep { private Ingredient input; private int waitTime; private int waterUsed; public CauldronRecipeStep(Ingredient input, int waitTime, int waterUsed) { this.input = input; this.waitTime = waitTime; if(waterUsed > 3) throw new JsonSyntaxException("\"waterused\" is greater than 3. Please set it to a value between 0 and 3."); else if(waterUsed < 0) throw new JsonSyntaxException("\"waterused\" is less than 0. Please set it to a value between 0 and 3."); else this.waterUsed = waterUsed; } public Ingredient getInput() { return input; } public int getWaitTime() { return waitTime; } public int getWaterUsed() { return waterUsed; } } EDIT 2: I know that "You're going about this completely the wrong way" is probably a massive understatement, but I would like to know...
  7. Oh ok... how exactly does it defeat the point of DeferredRegister though? I thought the point was it stops you from doing things at the wrong time.
  8. So, I'm switching my mod to use deferred registries. I've got this method: public static RegistryObject<Block> registerBlock(Block block) { return BLOCKS.register(block.getRegistryName().getPath(), () -> block); } (BLOCKS is my deferred registry for blocks) This allows me to do this in another class: public static final RegistryObject<Block> CAUTION_BLOCK = ModRegistry.registerBlock(new CautionBlock()); Which I personally think is a nice, clean way to do it. However, when it comes to things with "type"s, e.g. entities or tile entities, I have this: public static RegistryObject<TileEntityType<?>> registerTile(TileEntity te, ResourceLocation regName, Block... validBlocks) { TileEntityType<?> tetype = TileEntityType.Builder.create(() -> te.getTileEntity(), validBlocks).build(null).setRegistryName(regName); return TILE_TYPES.register(tetype.getRegistryName().getPath(), () -> tetype); } And that works! However for entities I have this: public static RegistryObject<EntityType<?>> registerEntity(Entity entity, ResourceLocation regName, EntityClassification classification, float width, float height) { EntityType<?> entityType = EntityType.Builder.create((a, b) -> entity, classification).size(width, height).build(regName.toString()).setRegistryName(regName); return ENTITY_TYPES.register(entityType.getRegistryName().getPath(), () -> entityType); } And I call it like this: public static final RegistryObject<EntityType<?>> ROBOT = ModRegistry.registerEntity(new RobotEntity(null, null), new ResourceLocation(LabKit.MODID, "robot"), EntityClassification.CREATURE, 0.6f, 0.9f); But that produces an odd error in the console: java.lang.IllegalStateException: Attempted to set registry name with existing registry name! New: labkit:robot Old: labkit:robot What am I doing wrong?
  9. Ah, thanks! Yeah, sorry, my bad. Typed the wrong thing. Ok, cool! Will do! Thanks!
  10. So I have an array of blocks in my registry class: private static Block[] modBlocks = { ModBlocks.CAUTION_BLOCK, ModBlocks.WHITE_FLOOR_TILES, ModBlocks.GREY_FLOOR_TILES, ModBlocks.DARK_GREY_FLOOR_TILES, ... }; I then loop through it to register them all: @SubscribeEvent public static void blockRegistry(final RegistryEvent.Register<Block> e) { for(Block block : modBlocks) { e.getRegistry().register(block); } } I was wondering if there's a way to do this for tile entities, as they're a little more awkward. I'm using the @ObjectHolder to register things. Thanks!
  11. I was trying to avoid lag, but okay
  12. Oh ok, yeah, I didn't think about that, but it makes sense. I was hoping to keep the power stored in a blockstate to avoid using a tile entity. Do you know if it's still possible do that?
  13. Thanks, I'll do that!
  14. So I created a new block, adapting it from RedstoneWireBlock in the vanilla code. Here's my code: public class EnergyConduit extends Block { public static final BooleanProperty NORTH = BlockStateProperties.NORTH; public static final BooleanProperty EAST = BlockStateProperties.EAST; public static final BooleanProperty SOUTH = BlockStateProperties.SOUTH; public static final BooleanProperty WEST = BlockStateProperties.WEST; public static final BooleanProperty UP = BlockStateProperties.UP; public static final BooleanProperty DOWN = BlockStateProperties.DOWN; public static final IntegerProperty ENERGY = ModBlockStateProperties.CONDUIT_POWER_1000; public static final Map<Direction, BooleanProperty> CONNECTED_PROPERTY_MAP = Maps.newEnumMap(ImmutableMap.of( Direction.NORTH, NORTH, Direction.EAST, EAST, Direction.SOUTH, SOUTH, Direction.WEST, WEST )); private final Set<BlockPos> blocksNeedingUpdate = Sets.newHashSet(); public EnergyConduit() { super(Block.Properties.create(Material.IRON, MaterialColor.RED_TERRACOTTA) .hardnessAndResistance(3.0f) .sound(SoundType.LANTERN) ); this.setDefaultState(this.stateContainer.getBaseState().with(NORTH, Boolean.valueOf(false)).with(EAST, Boolean.valueOf(false)).with(SOUTH, Boolean.valueOf(false)).with(WEST, Boolean.valueOf(false)).with(ENERGY, Integer.valueOf(0))); this.setRegistryName(new ResourceLocation(LabKit.MODID, "energy_conduit")); } public BlockState getStateForPlacement(BlockItemUseContext context) { return getConnectedState(context.getWorld(), context.getPos()); } public BlockState updatePostPlacement(BlockState state, Direction facing, BlockState facingState, IWorld world, BlockPos currentPos, BlockPos facingPos) { return getConnectedState(world.getWorld(), currentPos); } private BlockState updateSurroundingConduits(World worldIn, BlockPos pos, BlockState state) { List<BlockPos> list = Lists.newArrayList(this.blocksNeedingUpdate); this.blocksNeedingUpdate.clear(); for(BlockPos blockpos : list) { worldIn.notifyNeighborsOfStateChange(blockpos, this); } return state; } private void notifyConduitNeighborsOfStateChange(World worldIn, BlockPos pos) { if (worldIn.getBlockState(pos).getBlock() == this) { worldIn.notifyNeighborsOfStateChange(pos, this); for(Direction direction : Direction.values()) { worldIn.notifyNeighborsOfStateChange(pos.offset(direction), this); } } } public void onBlockAdded(BlockState state, World worldIn, BlockPos pos, BlockState oldState, boolean isMoving) { if (oldState.getBlock() != state.getBlock() && !worldIn.isRemote) { this.updateSurroundingConduits(worldIn, pos, state); for(Direction direction : Direction.Plane.VERTICAL) { worldIn.notifyNeighborsOfStateChange(pos.offset(direction), this); } for(Direction direction1 : Direction.Plane.HORIZONTAL) { this.notifyConduitNeighborsOfStateChange(worldIn, pos.offset(direction1)); } for(Direction direction2 : Direction.Plane.HORIZONTAL) { BlockPos blockpos = pos.offset(direction2); if (worldIn.getBlockState(blockpos).isNormalCube(worldIn, blockpos)) { this.notifyConduitNeighborsOfStateChange(worldIn, blockpos.up()); } else { this.notifyConduitNeighborsOfStateChange(worldIn, blockpos.down()); } } } } @SuppressWarnings("deprecation") public void onReplaced(BlockState state, World worldIn, BlockPos pos, BlockState newState, boolean isMoving) { if (!isMoving && state.getBlock() != newState.getBlock()) { super.onReplaced(state, worldIn, pos, newState, isMoving); if (!worldIn.isRemote) { for(Direction direction : Direction.values()) { worldIn.notifyNeighborsOfStateChange(pos.offset(direction), this); } this.updateSurroundingConduits(worldIn, pos, state); for(Direction direction1 : Direction.Plane.HORIZONTAL) { this.notifyConduitNeighborsOfStateChange(worldIn, pos.offset(direction1)); } for(Direction direction2 : Direction.Plane.HORIZONTAL) { BlockPos blockpos = pos.offset(direction2); if (worldIn.getBlockState(blockpos).isNormalCube(worldIn, blockpos)) { this.notifyConduitNeighborsOfStateChange(worldIn, blockpos.up()); } else { this.notifyConduitNeighborsOfStateChange(worldIn, blockpos.down()); } } } } } public void neighborChanged(BlockState state, World worldIn, BlockPos pos, Block blockIn, BlockPos fromPos, boolean isMoving) { if (!worldIn.isRemote) { if (state.isValidPosition(worldIn, pos)) { this.updateSurroundingConduits(worldIn, pos, state); } else { spawnDrops(state, worldIn, pos); worldIn.removeBlock(pos, false); } } } @SuppressWarnings("deprecation") protected static boolean canConnectTo(BlockState blockState, IBlockReader world, BlockPos pos, @Nullable Direction side) { Block block = blockState.getBlock(); if (block == ModBlocks.ENERGY_CONDUIT) { return true; } else if (block.hasTileEntity() && world.getTileEntity(pos).getCapability(CapabilityEnergy.ENERGY).isPresent()) { return true; } else { return blockState.canConnectRedstone(world, pos, side) && side != null; } } public BlockRenderLayer getRenderLayer() { return BlockRenderLayer.CUTOUT; } protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder) { builder.add(NORTH, EAST, SOUTH, WEST, ENERGY); } private BlockState getConnectedState(World world, BlockPos pos) { BlockState returnState = this.getDefaultState(); if(world.getBlockState(pos.north()).getBlock() == ModBlocks.ENERGY_CONDUIT) { returnState = returnState.with(NORTH, Boolean.valueOf(true)); } if(world.getBlockState(pos.east()).getBlock() == ModBlocks.ENERGY_CONDUIT) { returnState = returnState.with(EAST, Boolean.valueOf(true)); } if(world.getBlockState(pos.south()).getBlock() == ModBlocks.ENERGY_CONDUIT) { returnState = returnState.with(SOUTH, Boolean.valueOf(true)); } if(world.getBlockState(pos.west()).getBlock() == ModBlocks.ENERGY_CONDUIT) { returnState = returnState.with(WEST, Boolean.valueOf(true)); } if(world.getBlockState(pos.up()).getBlock() == ModBlocks.ENERGY_CONDUIT) { returnState = returnState.with(UP, Boolean.valueOf(true)); } if(world.getBlockState(pos.down()).getBlock() == ModBlocks.ENERGY_CONDUIT) { returnState = returnState.with(DOWN, Boolean.valueOf(true)); } return returnState; } } But when I launch Minecraft, forge just hangs after printing this to the console: [modloading-worker-1/DEBUG] [ne.mi.fm.ja.FMLModContainer/LOADING]: Completed Automatic event subscribers for forge What have I done wrong? I've added some breakpoints and it seems that it never actually registers the block.
  15. So I'd like to create a new crafting block. What do I mean by that? I need a block like a crafting table, so I put in some items, and it gives me another in return. I'd also like it to be reversible. Similar to the Machine Workbench from OmegaCraft. I've been searching through the internet and vanilla code, and all I end up with is a headache. Could someone please give some advice? Thanks, BP.
  16. Ah, that's perfect, thank you! ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ Thanks for the advice, but directions are handled by StairsBlock, which my class extends.
  17. So I've now changed it to this, and the game loads, but if I place down a stair block, it crashes... public class CautionBlockStairs extends StairsBlock { public CautionBlockStairs() { super(new CautionBlock().getDefaultState(), Block.Properties.from(new CautionBlock())); this.setDefaultState(new CautionBlock().getDefaultState()); this.setRegistryName("caution_block_stairs"); } protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder) { builder.add(CautionBlock.BORDER); builder.add(CautionBlock.SHADING); super.fillStateContainer(builder); } } Here's the crash report: What I understand from this is that because I'm passing in the CautionBlock's default state, I need to add the direction/shape properties to it. How would I do that?
  18. Ok, I've now added that in: protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder) { builder.add(CautionBlock.BORDER); builder.add(CautionBlock.SHADING); super.fillStateContainer(builder); } But I'm still getting the same error. It's pointing to line 13: super(null, Block.Properties.from(new CautionBlock())); in the constructor. I realise that adding super in the fillStateContainer probably prevents another error from occurring further down the line, so thanks for that!
  19. So I have a block with two boolean blockstates. I'd like to add a stair variant to it, but it seems to throw some errors in the registry. My block's code: My stair block's code: The error: What's going wrong? All help appreciated!
  20. Ah, ok, so all of the loading screen cancelling is handled within Minecraft rather than the mod, as long as the chunk is loaded? Nice! Thanks
  21. Hi. I'm not looking to do this at the moment, but it's more of a curiosity. How would I render another location or dimension in minecraft? Like what some of these portal mods do. If we go one step further, how do they get it so you can step between dimensions without a loading screen or even a lag spike?
×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.