Jump to content

fweo

Members
  • Posts

    28
  • Joined

  • Last visited

  • Days Won

    1

fweo last won the day on July 8 2023

fweo had the most liked content!

Converted

  • Gender
    Male

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

fweo's Achievements

Tree Puncher

Tree Puncher (2/8)

4

Reputation

  1. I want to replace a block in a vanilla structure (ruined portal) with another block. I can see two ways of doing this, neither of which are ideal. 1. Overwrite the NBT files containing the structure variants like a data pack after loading and editing the structure in-world (or edit the NBT directly with an editor, not sure which is simpler). First of all, this is tedious, as the structure has 13 variants, and can't be done in code alone. This method is also limited, as I can only change the blocks themselves, not add a new processor, which I would prefer (e.g. to only replace the block 50% of the time). It also just seems ugly to overwrite an entire set of structures for a simple change, and it is not compatible with the addition of more variants by another mod/datapack/vanilla update. 2. Mixin to add one line of code to put a new ProcessorRule in the StructurePlaceSettings made by makeSettings in RuinedPortalPiece. Downside: mixin. Is there a simple and flexible way of doing this that does not require a mixin? E.g. an event when a structure is placed, a way to track down the TemplaceStructurePieces and append to their StructurePlaceSettings, etc? If there is no easier way, which method above seems more appropriate?
  2. I see you are modifying tick behaviour. If fluids aren't flowing the game is probably not ticking at all. You can confirm this by checking whether other ticked behaviour works. The problem is likely in your mixin. Disable it and it will probably fix it. I haven't looked at it in detail, but perhaps your capability is not present or the upper bound on the for loop is 0. Does this behaviour even need a mixin? It will be much easier to debug if written without a mixin. I'm almost certain there's an event for ticks, surely you can just use that instead. I haven't looked for the strange item behaviour but at a guess mixins are likely the source again.
  3. PlayerTickEvents fire twice per tick, once with a START phase and later with an END phase. As well as checking event.side, also check that event.phase matches the phase you want it to occur in. I have a ticking capability very similar to yours, this is how I do it: @SubscribeEvent public static void tick(TickEvent.PlayerTickEvent event) { if(event.side == LogicalSide.SERVER && event.phase == TickEvent.Phase.END) { event.player.getCapability(CombatTimeCapability.INSTANCE).ifPresent(CombatTimeCapabilityInterface::tickCombat); } }
  4. The issue is that the method you have marked does not override or implement a method from its supertype (PickaxeItem).
  5. I have a block that has several possible drops, chosen randomly. I'd like this block to drop multiple items when mined with fortune, each rolled separately. For example, without fortune the block might drop A, B, or C, but with fortune I it might drop A and B, or two of C, or just one B, just as an ore drops 1-2 of its usual drop with fortune I. I've tried applying fortune to the loot table in the way vanilla does, with the "minecraft:apply_bonus" function. This gives the correct number of drops, but they are always the same. That is, first it determines whether to drop A, B, or C, then it drops 1-2 of that single item, rather than rolling on the table 1-2 times. As an ugly partial solution, I have tried creating a different loot table for each level of fortune, with the appropriate number of min/max rolls, and then the master table redirects to the fortune 3 table if you have at least fortune 3, otherwise to the fortune 2 table if you have at least fortune 2, etc. This is not ideal because it is very cumbersome, and it doesn't work for fortune levels higher than whatever value I decide to support. I also cannot get it working, but I assume it is possible. Before I try to fix my ugly solution, is there a clean way to achieve the desired outcome? If it could be done with data generation that would be preferable, as I have a few blocks in the same "family" with similar behaviour, but hand-written JSONs will be feasible if simpler that way.
  6. I have a feature that I want to spawn only in specific chunk coordinates. The way the coordinates are determined is a little complicated, but I have a function f(chunkX, chunkZ, seed) that determines whether the feature is allowed to spawn, without any reference to world information, just the coordinates. For the sake of example, suppose I want to spawn the feature only in chunks where the X coordinate is equal to the Z coordinate. My current solution is to create a subclass of PlacementFilter, which for the example I gave would look like protected boolean shouldPlace(PlacementContext context, RandomSource rand, BlockPos pos) { //return true if ChunkPos(pos) has the same X and Z } This then goes in the List of PlacementModifiers for the PlacedFeature. This approach works, in that it generates correctly in the intended chunks and not at all in the other chunks. However, I am concerned that this approach may cause issues, as I don't think it will "intelligently" apply the filter, and instead re-check for each instance in the chunk. If I am trying to generate the feature 100 times in the chunks it is supposed to be in, I suspect it will also pick out 100 positions in every other chunk and try to generate them there before getting individually filtered by my PlacementFilter. This could become a significant waste of resources if I have many such features. Is there a better way to have a feature only spawn in a small minority of chunks, based only on chunk coordinates and seed (not contents)?
  7. Good to know, that is what I thought originally, so I was surprised to see it not show up on the server side. Your reply got me looking in the right place, so while I was copying the code over to show you, I spotted the error, and I have now solved the issue. If my ContainerLevelAccess evaluation failed, I was returning false (as I do when it fails in other ways, e.g. the slots are empty). However, I should have been returning true, because "false" is telling the client to short-circuit and not bother sending the packet to the server as it has already determined the button will fail. After returning true here, handleInventoryButtonClick actually gets called, so the packet is sent to the server and it works correctly. I include the code below anyway, in case it helps anyone, or in case someone wants to tell me I'm doing something else horribly wrong. The Screen: @Override public boolean mouseClicked(double x, double y, int p_98760_) { int xstart = (this.width - this.imageWidth) / 2; int ystart = (this.height - this.imageHeight) / 2; double xl = x - (double)(xstart + 126); double yl = y - (double)(ystart + 7); double yl1 = y - (double)(ystart + 61); if (xl >= 0.0D && yl >= 0.0D && xl < 36D && yl < 18.0D) { // This conditional lets the client side decide not to bother sending the packet to the server if it does not need it if (this.menu.clickMenuButton(this.minecraft.player, 1)) { this.minecraft.gameMode.handleInventoryButtonClick((this.menu).containerId, 1); return true; } } // 2nd button ... return super.mouseClicked(x, y, p_98760_); } The Menu: public HarmoniserMenu(int id, Inventory inv) { this(id, inv, ContainerLevelAccess.NULL); Main.LOGGER.info("Creating harmoniser menu with null level access"); // I see this called from the render thread } public HarmoniserMenu(int id, Inventory inv, ContainerLevelAccess access) { super(ModMenus.HARMONISER.get(), id); Main.LOGGER.info("Created harmoniser menu"); // I see this called from both the render and the server thread this.access = access; // ... slots etc ... } private int testEval(Level l, BlockPos p) { Main.LOGGER.info("Evaluation successful at " + p); // Never called return 0; } @Override public boolean clickMenuButton(Player player, int button) { Main.LOGGER.info("Clicked button " + button); // Shows "Clicked button 1" on Render thread only (!!) if(button == 1) { // Debug button access.evaluate(this::testEval); return false; // This line was the problem: evaluation fails on client side, but should return true to do on server side } // ... other button etc ... }
  8. Do you have custom wood for the tree also? If the leaves do not recognise your tree's trunk as wood, they will decay as if there is no wood around. From memory, your wood needs to be tagged as a log using vanilla's tagging system (tag your leaves as leaves while you're at it). If that does not fix it, try placing a vanilla log next to the leaves as they're decaying, that will tell you if the problem is with your leaves or your wood.
  9. Thanks. The other bit I can't work out is that the server needs to know the BlockPos of my block (or some other identifying feature by which it could work that out). I know I could send that in the packet from the client to the server if I had it, but I'm not sure how the Menu can get the position of its block in the first place, again because it doesn't receive a ContainerLevelAccess. How do I find the BlockPos to send across in the packet, or am I taking the wrong approach here?
  10. I have a block with a Menu/Screen and a button. In the Screen I handle button clicks with this call: this.minecraft.gameMode.handleInventoryButtonClick((this.menu).containerId, 1); which will call clickMenuButton in my Menu. I would like the button to perform its task only if a certain condition in the world is met (for example, there is a stone block below it). However, it seems that clickMenuButton is only called on the client side (debug prints only show as coming from the "Render thread" not the "Server thread") where the ContainerLevelAccess given to the Menu constructor is ContainerLevelAccess.NULL. If clickMenuButton is only called on the client side, how can I interact with the Level as part of a button press? Note that I do not necessarily need to read the Level exactly when the button is pressed, so I do not mind storing whether the world condition is met in a variable and changing it when the block is changed, for example, as an alternate solution.
  11. Of course, thanks. Here's the solution for future reference: @Override public void entityInside(BlockState state, Level level, BlockPos pos, Entity entity) { double factor = 0.4; entity.setDeltaMovement(entity.getDeltaMovement().multiply(factor, 1.0D, factor)); }
  12. I'm looking to create a block like cobwebs that you can walk through which slows you, but I would like it to slow in the manner of soul sand, not of cobwebs. The difference is that cobwebs (or anything using Entity#makeStuckInBlock) set the Entity's deltaMovement to zero. This means that you move much slower, you cannot jump a full block, and your falling is slowed, even if you set the vertical speed multiplier to 1 and the horizontal multiplier close to 1. For example, if I take the cobweb approach with tweaked values as follows: @Override public void entityInside(BlockState state, Level level, BlockPos pos, Entity entity) { entity.makeStuckInBlock(state, new Vec3(0.95D, 1.0D, 0.95D)); } This results in a block that slows by much more than the stated 5%, reduces the jump height to less than one block, and reduces your fall speed significantly when you land in it, removing all your momentum. I would like my block to behave like soul sand, where it does not affect jumping and the speed multiplier translates directly to the resulting speed. However, soul sand works by setting the Block's speedFactor, and only the speedFactor of the block the entity is standing on affects the entity's speed, so I cannot simply set the speedFactor. Is there a way I can create a block that you can walk through, slowing you down, yet you can still jump your full jump height and walking through does not kill your momentum like cobwebs do?
  13. I have a generated structure that generates a multi-storey tower consisting of a few levels picked from a pool of interchangeable sections. The levels are connected and generated using jigsaw blocks. Each level is to be connected to the level above by a ladder, but I would like it if the ladders could be in different places each time the structure is generated. On each level there are 8 spots where the ladder could go (the same 8 in all possible levels), and I would like it to pick from these randomly. Ideally I could do this programmatically, rather than having to create and save 8 different versions of each level with jigsaw blocks. However, it is not clear to me when the blocks actually get placed in the world and whether I can hook into this to perform arbitrary post-processing to the structure after the jigsaws have done their work. Is it possible to do this? If not, is there a simple way other than jigsaws that I can build tower sections in-game and then pull from a pool to generate them? I do not need all of the capabilities of jigsaws as all levels are of the same size and fit together in a simple linear fashion, so it would be simple to fit them together in code if I had a method that could place a section from saved NBT output from a structure block. This approach might be preferable as it would also allow me to prevent duplicates of certain levels, which it does not seem to be possible to do with jigsaws. NB: If there will be a simpler solution in 1.19.X, give the solution for that instead and I'll just update my mod to that version.
  14. Is there a simple way to have a smelting/blasting recipe that produces more than one item of output for each input item? For example, one redstone ore -> two redstone dust? The vanilla wiki doesn't list "count" as one of the possible tags for smelting, unlike e.g. crafting, so it can't be done with plain JSON. I can't see any methods to do it easily with data generation either, because SimpleCookingRecipeBuilder only has an Item as a result with nothing for count. Is there a way to get this result without having to replace a lot of vanilla furnace behaviour? If not, can anyone think of any nicer workarounds than outputting a single item that can be crafted into the appropriate number of the desired output (e.g. one redstone ore -> one "redstone dust cake" -> two redstone dust)?
  15. That's helped a bit, but I'm still stuck. If I compare the the configured features in Features.ORE_COAL.getFeatures() to those in event.getGeneration().getFeatures(GenerationStep.Decoration.UNDERGROUND_ORES).get(i).get().getFeatures(); then for the i that corresponds to coal ore (which won't necessarily always be the same) those two streams both contain four configured features, and the final three of them match in terms of what they toString into, but don't compare as equal with .equals. Am I doing the wrong thing with getFeatures here? Surely there's a proper way to compare them without having to resort to comparing strings or comparing some other complicated properties of them. That is, if I do something like this then they match in the log but don't get into the if statement: List<Supplier<ConfiguredFeature<?,?>>> orefeatures = event.getGeneration().getFeatures(GenerationStep.Decoration.UNDERGROUND_ORES); for(int i = 0; i < orefeatures.size(); i++) { List<ConfiguredFeature<?, ?>> oneFeature = orefeatures.get(i).get().getFeatures().toList(); List<ConfiguredFeature<?, ?>> testAgainst = Features.ORE_COAL.getFeatures().toList(); for(int j = 0; j < oneFeature.size() && j < testAgainst.size(); j++) { Main.LOGGER.info(oneFeature.get(j)); Main.LOGGER.info(testAgainst.get(j)); if(oneFeature.get(j).equals(testAgainst.get(j))) { Main.LOGGER.info("Matched at " + i); break; } Main.LOGGER.info(""); } } Giving the following output:
×
×
  • Create New...

Important Information

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