Jump to content

fweo

Members
  • Posts

    28
  • Joined

  • Last visited

  • Days Won

    1

Everything posted by fweo

  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:
  16. Here's my current attempt, which runs with each BiomeLoadingEvent: List<Supplier<ConfiguredFeature<?,?>>> orefeatures = event.getGeneration().getFeatures(GenerationStep.Decoration.UNDERGROUND_ORES); List<Integer> indicesToRemove = new ArrayList<>(); for(int i = 0; i < orefeatures.size(); i++) { ConfiguredFeature<?,?> feature = orefeatures.get(i).get(); Main.LOGGER.info(feature.toString()); if(feature == Features.ORE_COAL || feature == Features.ORE_IRON) { indicesToRemove.add(i); Main.LOGGER.info("Removing!"); } } orefeatures.removeAll(indicesToRemove); The features all print in the first log, and the two I've picked to remove are included in that list, as expected (they toString into a json format, though, so it's a bit hard to look at). However, it never gets into the body of the if statement, so nothing changes with the generation. Features.ORE_COAL is the exact object put into the list by addDefaultOres in BiomeDefaultFeatures, and that just gets made directly into a Supplier which gets put straight into the list, so those should be the right objects that come out of the Supplier when you call getGeneration.getFeatures(step).get(index).get(). Any ideas about what's wrong? Something up with the ==?
  17. What is the preferred method for preventing a particular vanilla ConfiguredFeature from generating (e.g. preventing natural generation of coal ore)? My best guess is that you would do something when you receive a BiomeLoadingEvent, similarly to how you add your own configured features, but I can't think of a good way to do this. I suppose I could loop through the entire list, retrieve the object from each supplier, compare it to the particular vanilla feature, and then remove it if it's the same, but this seems a little clumsy, especially if I want to stop the generation of several features (which I do). If I want to remove the feature from all biomes, is there a way to prevent it from getting added to the list in the first place or replace it with a no-op so it doesn't need to be removed each time a biome loads? Is this any easier if you only wish to alter an existing feature, e.g. changing an ore vein size or changing what block generates in a vegetation patch?
  18. When you do your data run do you see "Initializing Data Gatherer for mods [examplemod]" in the logs, rather than [yourmodid]? If so, your run configurations are still wrong. Check to see if you have "--mod examplemod" in your IDE's run configs menu, and if you can't get it to update with gradle you can always change the --mod argument manually in your IDE.
  19. If you're asking how to get the EntityType when using the DeferredRegister way, you can call VERRUCT.get() and that will get you the EntityType out of the registry object. You can do the same for any other type of registry object. The only thing to be careful about is to make sure that the entities have actually been registered by the time you call VERRUCT.get(), but if you register your renderer at the right time that won't be a problem. In 1.17.1 you register renderers with the EntityRenderersEvent.RegisterRenderers event, I'm not sure about 1.16.5 but it's probably similar. This event will always fire after entities have been registered, so you can safely call the getter. You should use deferred registration because it ensures that your entities (or blocks, or whatever else you register) get registered at the right time. If everyone did this, it would allow forge to introduce a lot of useful features, such as reloading/enabling/disabling mods while the game is running. This is impossible to do with static initialisers. By the way, you can click the <> button while drafting your post and it will let you format your code like this: RenderingRegistry.registerEntityRenderingHandler(VERRUCT.get(), verructRenderer::new); LifeAndLive.LOGGER.debug("LIFE AND LIVE - Entity rendered");
  20. Call the food method on your item's properties, and give it a FoodProperties. Either use a FoodProperties from vanilla's Foods class or make your own the same way they do there, e.g. new Item.Properties().food((new FoodProperties.Builder()).nutrition(5).saturationMod(0.5f).build()) In the future, if you want to work out how to add more properties to an item, go to vanilla's Items class and look for an item that has the desired properties (e.g. bread in this case) and follow how it is done there.
  21. There's a class called SoupItem that does exactly what you want, simply changing Item to SoupItem will give you a bowl back once you've finished eating it. If you want something other than a vanilla bowl you can just create a modified version of that class to give you whatever you want.
  22. It's not that the order of the properties that matters. Let's look at this line for a second: new Item(newItem.Properties().group(gobber2)).setRegistryName(location("gobber2_goo")).food(FoodList.gooFood) If I space it out a bit so we can read it a little more easily (you can keep spacing as it was in the actual code): new Item( // start constructing item properties new Item.Properties().group(gobber2) // finish constructing item properties ).setRegistryName(location("gobber2_goo")).food(FoodList.gooFood) that middle line is creating the Properties object. You were calling the food method on the Item object. You're right that food is a thing in Item, but it's just a variable not a method. As it happens, all the Properties::food method does is tell the Properties object to put your Food into that field, as you can see in the Item constructor you posted. Comparing to your working one: new Item( // start constructing item properties new Item.Properties().food(FoodList.gooFood).group(gobber2) // finish constructing item properties ).setRegistryName(location("gobber2_goo")) Now you're (correctly) calling the food method on the Item.Properties object. The only actual difference from your working one is the placement of a single bracket, which is determining what object you're calling the methods on. Changing the order works fine, you can apply properties to the Item.Properties object in whatever order you like (each method returns the Item.Properties object allowing you to "chain" the method calls like you have here). For example, this would also work: new Item( // start constructing item properties new Item.Properties().group(gobber2).food(FoodList.gooFood) // finish constructing item properties ).setRegistryName(location("gobber2_goo")) If you had more changes to properties, like rarity or max stack size, you could put them in there too in any order. Hopefully understanding this will help clear up future problems. Also, if you click the button that looks like < > at the top of your editor when you're posting, you can post code in the style that I'm using here, which will make your post look a lot cleaner.
  23. gobber2_goo still needs to be an Item, which Food is not (it doesn't extend Item like ItemFood did). Take your original code using ItemFood and change it to Item. This will give you a generic non-food item, like you've probably created elsewhere. You can now call another method on the Item.Properties object you are giving it to give the Item.Properties food properties. If you look into the Items class, you'll see where all the vanilla items are created. This is where you can find the method you need, because vanilla foods use it (this will also give you some usage examples). The method is in Item.Properties (my mappings are a bit old so it's func_221540_a for me, it might have a name for you updated my mappings, it's called food now). This method takes a single argument, which would be the Food you just constructed. It'd end up looking like this: Food gooFood = (new Food.Builder()).value(4).saturation(0.6F).build(); ItemList.gobber2_goo = new Item(new Item.Properties().group(gobber2).setRegistryName(location("gobber2_goo").food(gooFood));
  24. You don't need the "modid = " part, this is why you're getting the second error on that line (you haven't defined the variable modid). You can simply use @Mod("bravenewworldmodid"), or better yet, do something like @Mod(Main.MOD_ID) public class Main { public static final String MOD_ID = "bravenewworldmodid"; // rest of your code } which lets you use that variable whenever you need the mod ID.
  25. What do you mean by it not accepting any of the values? Posting the code might help. You should give it your mod ID as a String, and should match your mod ID that you've put in META-INF/mods.toml. If you haven't filled out this file yet, or there's a mismatch, this could be the problem.
×
×
  • Create New...

Important Information

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