Jump to content

Syric

Members
  • Posts

    108
  • Joined

  • Last visited

Everything posted by Syric

  1. Ah, thank you! That helps a lot. I'm afraid I'm not 100% clear on the enqueue business; threads are a bit of a weak spot in my knowledge. I'll go patch that up and figure things out. Thanks again!
  2. I'm porting a mod someone else wrote to 1.18.2. It increases mob caps. It does this using something called ObfuscationReflectionHelper, which I unfortunately have no experience with, and which seems to have been removed for 1.18. How can I replicate the following code in 1.18.2 and beyond? (I've already updated a few things, like changing EntityClassification to MobCategory.) private void setup(final FMLCommonSetupEvent event) { // for (EntityClassification classification : EntityClassification.values()) { // LOGGER.error(String.format("%s: %d", classification.getName(), classification.getMaxNumberOfCreature())); // } try { Field mobCap = ObfuscationReflectionHelper.findField(MobCategory.class, "field_75606_e"); mobCap.setAccessible(true); Field modifierField = Field.class.getDeclaredField("modifiers"); modifierField.setAccessible(true); modifierField.setInt(mobCap, mobCap.getModifiers() & ~Modifier.FINAL); mobCap.setInt(MobCategory.MONSTER, Config.MONSTER_CAP.get()); mobCap.setInt(MobCategory.CREATURE, Config.CREATURE_CAP.get()); mobCap.setInt(MobCategory.AMBIENT, Config.AMBIENT_CAP.get()); mobCap.setInt(MobCategory.WATER_CREATURE, Config.WATER_CREATURE_CAP.get()); mobCap.setInt(MobCategory.WATER_AMBIENT, Config.WATER_AMBIENT_CAP.get()); } catch (NoSuchFieldException | IllegalAccessException e) { e.printStackTrace(); } // for (EntityClassification classification : EntityClassification.values()) { // LOGGER.error(String.format("%s: %d", classification.getName(), classification.getMaxNumberOfCreature())); // } }
  3. I'm trying to use an event to switch the model of a particular held item under certain conditions - it should usually have one model, but under specific conditions (which I can query, that part's fine) I want to use a different, larger model. Is there some part of RenderPlayerEvent et al that I can just go setHeldItemModel() in, or something like that?
  4. Forge and Fabric are two different mod systems. You need to pick one and only use its mods. This is Forge, so Fabric mods won't work. If you want to use Fabric mods you should use Fabric instead, but then you'd have to lose your Forge ones.
  5. Ah, thank you so much. I suppose the only parts of BaseFireBlock I can't override are methods I can simply choose not to call anyway, so this is doable even if it isn't ideal. Edit: I can confirm that this works.
  6. I've already put the blocks in BlockTags.FIRE, which didn't help. The problem is not playerWillDestroy(); I copied that and it works fine. When the player punches fire, it disappears and plays the fire extinguishing noise without making particles. The problem is also not the animation: my fire produces smoke perfectly fine as well. The problem is that when my custom fire 'breaks' because it destroys the block it's adjacent to, it makes a breaking sound (wool, as it happens) and bursts into a scatter of fire-textured particles, just like any other destroyed block. Vanilla fire doesn't do this. I'd like my fire to also not do this. What should I override?
  7. I've made some custom fire blocks, borrowing all the code from BaseFireBlock and FireBlock. They don't produce any particles when I punch them to extinguish them, but they do make particles and the 'poof' wool sound when they break as a consequence of a block update - usually, because they've burned the block they're on top of. They didn't do this when they extended BaseFireBlock, but for some reason they do now that they're extending my custom replacement, even though the code is the same. How can I prevent this behavior?
  8. Copy-Paste may not always get everything, depending on the size limit of your copy-paste and/or the limit of wherever you're pasting to. You should generally check the end of the thing being copied to make sure it makes it across to wherever you're pasting it to. The log you've posted looks like it cuts off in the middle of something, so we're checking whether that's what actually happened or if it's an error with the copy-pasting. As for putting the mods in afterwards, sieben's right - the game doesn't update dynamically from the mods folder. A better way of thinking about it is that when you start forge, it assembles all the provided mods into a cohesive modded game that you then play - it generally doesn't go back to check the mods folder until you restart the game entirely. It was a good idea to try to circumvent the problem you were having, but unfortunately that method isn't going to work.
  9. So I would do something like this? Just checking that I understand what you mean by calling #immutable. BlockPos.betweenClosedStream(pos1, pos2) //Filter out ones that don't match our criteria .filter(c -> distance(c) <= radius) //Put the remaining ones into a map: .forEach(c -> { //Put the block into the map output.put(c.immutable(), (double) distance(c)); }); Edit: yep, that worked. Thank you!
  10. I'm trying to write a method that produces a Map<BlockPos, Double> containing every block within a certain radius of a given point and their distance to that point. However, it has a very strange problem: if there are 30 blocks within that radius, it will add each of those 30 blocks to the output map... and then return a map contianing 30 copies of a single block, which isn't even within the radius provided. (It's the block with the highest x,y,z coordinates in the original box.) Does anyone know what's going wrong here? My apologies if this turns out to be me misusing streams rather than an actual Forge issue, but I think there's an equal chance that I'm misunderstanding BlockPos.betweenClosedStream() instead. @Override public Map<BlockPos, Double> blockMap() { //Create a box int variation = (int) Math.ceil(radius); BlockPos pos1 = new BlockPos(origin.getX()-variation, origin.getY()-variation,origin.getZ()-variation); BlockPos pos2 = new BlockPos(origin.getX()+variation, origin.getY()+variation, origin.getZ()+variation); //(pos2 is the block that I end up with many copies of in the map) //Put all blocks within that box into a map if they pass a filter //Create a map to collect outputs in HashMap<BlockPos, Double> output = new HashMap<BlockPos, Double>(); //Create a stream of blocks BlockPos.betweenClosedStream(pos1, pos2) //Filter out ones that don't match our criteria .filter(c -> distance(c) <= radius) //Put the remaining ones into a map: .forEach(c -> { //Put the block into the map output.put(c, (double) distance(c)); // Log that you've done this. This indicates that many different elements are being placed into the map. LogUtils.getLogger().info("Placing " + c.toShortString() + " into pattern map"); LogUtils.getLogger().info("Pattern map has " + output.entrySet().size() + " elements"); StringBuilder sb1 = new StringBuilder("Sphere pattern blocks: "); for (BlockPos pos : output.keySet()) { sb1.append("(").append(pos.toShortString()).append("), "); } LogUtils.getLogger().info(sb1.toString()); }); //After the loop, do the *exact same* printing of the output. This suddenly shows that the output //is full of many copies of the *same* block, which shouldn't even have passed the filter. //It's either the first or last block to have been passed into the stream, not sure. LogUtils.getLogger().info("Generated a sphere pattern. It has " + output.entrySet().size() + " elements."); LogUtils.getLogger().info("Sphere pattern blocks: "); StringBuilder sb = new StringBuilder(); for (BlockPos pos : output.keySet()) { sb.append("(").append(pos.toShortString()).append("), "); } LogUtils.getLogger().info(sb.toString()); //Return return output; } The log then looks like this: https://imgur.com/4IPVU7i
  11. Unfortunately, the block is definitely not as slippery as it should be for the player (because the server side is always returning more friction than it should, presumably). The server side is therefore relevant to actual movement, so I do need its calculation to be correct. The client side calculations seem to be relevant as well, and working correctly, but insufficient on their own. I set server side to always be high friction and client side always low, and the friction the player gets is somewhere in between. Edit: Switching server side to low friction and client side to high produces the strange effect of a block which has very high friction whenever you aren't touching the keyboard but which flings you at absurd speeds as soon as you attempt to walk on it.
  12. I'm trying to make a block that is slippery in one direction: its friction depends on the angle of your motion. I've got it to work perfectly for nonplayer entities, but (once again), the player poses a different issue because of siding. My testing indicates that calling getDeltaMovement is giving (0, -1, 0) on serverside when it shouldn't be, and this screws up the calculation. How can I get accurate information here? @Override public float getFriction(BlockState state, LevelReader level, BlockPos pos, @org.jetbrains.annotations.Nullable Entity entity) { if (entity == null) { return 0.7F; } //Get a unit vector in the appropriate direction Vec3 axisUnitVector = Vec3.ZERO; if (state.getValue(AXIS) == Direction.Axis.X) { axisUnitVector = new Vec3(1, 0, 0); } else if (state.getValue(AXIS) == Direction.Axis.Z) { axisUnitVector = new Vec3(0, 0, 1); } //The block can't be placed vertically //Take the dot product of that vector with a unit vector in the direction of the entity's movement //This effectively just returns the cosine of the angle between the entity's vector and the chosen axis //i.e. 0 when perpendicular, 1 when parallel, etc. Vec3 normalizedMovement = entity.getDeltaMovement().normalize(); double dotProduct = Math.abs(normalizedMovement.dot(axisUnitVector)); //Produce a string for reporting String vectorString = "(" + StringUtils.truncate(String.valueOf(normalizedMovement.x), 4) + "," + StringUtils.truncate(String.valueOf(normalizedMovement.y), 4) + "," + StringUtils.truncate(String.valueOf(normalizedMovement.z), 4) + ")"; //Friction ranges from 0.6 to 1 depending on that cosine double finalMultiplier = Mth.lerp(dotProduct, 0.6, 1); chatPrint("Friction: " + StringUtils.truncate(String.valueOf(finalMultiplier), 5) + ", dot:" + StringUtils.truncate(String.valueOf(dotProduct), 5) + ", vector:" + vectorString + (level.isClientSide() ? "clientside" : "serverside"), (Level) level); return (float) finalMultiplier; } Walking on the block in the slippery direction produces this output: [CHAT] Friction: 0.6, dot:0.0, vector:(0.0,-1.0,0.0) serverside [CHAT] Friction: 0.981, dot:0.953, vector:(-0.0,-0.3,-0.9) clientside [CHAT] Friction: 0.6, dot:0.0, vector:(0.0,-1.0,0.0) serverside [CHAT] Friction: 0.981, dot:0.952, vector:(-0.0,-0.3,-0.9) clientside [CHAT] Friction: 0.6, dot:0.0, vector:(0.0,-1.0,0.0) serverside [CHAT] Friction: 0.981, dot:0.952, vector:(-0.0,-0.3,-0.9) clientside [CHAT] Friction: 0.6, dot:0.0, vector:(0.0,-1.0,0.0) serverside [CHAT] Friction: 0.981, dot:0.952, vector:(-0.0,-0.3,-0.9) clientside [CHAT] Friction: 0.6, dot:0.0, vector:(0.0,-1.0,0.0) serverside [CHAT] Friction: 0.980, dot:0.952, vector:(-0.0,-0.3,-0.9) clientside [CHAT] Friction: 0.6, dot:0.0, vector:(0.0,-1.0,0.0) serverside [CHAT] Friction: 0.980, dot:0.952, vector:(-0.0,-0.3,-0.9) clientside [CHAT] Friction: 0.6, dot:0.0, vector:(0.0,-1.0,0.0) serverside [CHAT] Friction: 0.980, dot:0.952, vector:(-0.0,-0.3,-0.9) clientside
  13. Here's my solution, based on entity.isColliding(). You can just pass your block's VoxelShape in as shape; this block is directional so it needs a reference to the map. This method returns 'true' if the hitbox of the entity in question intersects the provided shape, in this case the shape of a non-colliding block. private boolean isColliding(BlockPos pos, BlockState state, Entity entity) { VoxelShape shape = SHAPE_MAP.get(state.getValue(FACING)); VoxelShape positionedShape = shape.move((double)pos.getX(), (double)pos.getY(), (double)pos.getZ()); return Shapes.joinIsNotEmpty(positionedShape, Shapes.create(entity.getBoundingBox()), BooleanOp.AND); }
  14. So I should use setDeltaMovement() on the server side only, and update the client with the code you posted? I'll give that a shot. Edit: seems to be working, thank you!
  15. I'm having a strange issue where I want to move an entity with entity.setDeltaMovement. Since this has nothing to do with rendering, I would assume it should be run on the server side only. However, when I do that, it doesn't appear to work: ingame, when I trigger this code (and I can confirm that it is triggering), I do not appear to move. if (!level.isClientSide()) { entity.setDeltaMovement(vec3.x * mult, vert, vec3.z*mult); } That said, the server certainly thinks it's moved me - I set up some nearby blocks to log when they detect an entity within them, along with the side, and when I use setDeltaMovement on the server side they report detecting an entity on the server side but not the client side. However, when I invert the if statement: if (level.isClientSide()) { entity.setDeltaMovement(vec3.x * mult, vert, vec3.z*mult); } then the player does appear to move when I trigger this code in-game, and the detector blocks register an entity inside them on both logical sides. Setting the code to run on both sides produces problems, however. Namely, I have code so that this movement happens only once: when the entity is moved, they're also given the Slowness effect, and they're not moved if they already have it. If the code runs on both sides, sometimes the server side runs first and applies Slowness, which then prevents the client side from moving the entity. I believe that I could make my code appear to work by using the second variation, only running the code on the client side. However, this answer is counterintuitive enough that I'm suspicious of it, so I'm coming here to ask for advice. Is running this code on the client side only really a valid way to handle this issue? How should I deal with this problem?
  16. The context is that I'm calling onDestroyedByPlayer() to make some things happen - specifically, the block might explode. I want to check if the tool used (if any) is a pickaxe, so that if it is, the explosion chance can be decreased.
  17. How can I check if a block was destroyed with a pickaxe? I found this thread from 1.15, but the method it suggests: Item::getToolTypes // Which returns a Set<ToolType> Set::contains does not seem to work anymore. What has it been replaced with?
  18. I found the error. The explosions were being spawned at the block's small corner, while the block was still present. This frequently meant that the block absorbed the majority of the explosion and shielded the player from damage.
  19. explode() is called from a few other places as well, all with the same !level.isClientSide() wrapping. public void stepOn(Level level, BlockPos pos, BlockState state, Entity entity) { if (!level.isClientSide()) { explode(level, pos, state,0.1F); } super.stepOn(level, pos, state, entity); } private void explode(Level level, BlockPos pos, BlockState state, float chance) { RandomSource rdm = RandomSource.create(); chatPrint("Checking explosion with chance " + chance, level); if (rdm.nextFloat() < chance) { chatPrint("Exploding!", level); if (state.getValue(WEAK_VERSION)) { level.explode(null, pos.getX(), pos.getY(), pos.getZ(), 1.5F, Explosion.BlockInteraction.BREAK); } else { level.explode(null, pos.getX(), pos.getY(), pos.getZ(), 2.5F, Explosion.BlockInteraction.BREAK); } } }
  20. I'm trying to make a block that has a chance of exploding when the player steps on it or mines it. However, I have an issue: if I allow the code to run on either side, then if the block explodes on the client side only it produces block glitches (as it's still there serverside). On the other hand, if I only allow the code to run on the server side, the block glitches vanish but the player takes no damage from the explosion. How can I resolve this? Apologies if this is a basic question, I'm still a little wobbly on how to properly manage sides.
  21. If it helps, I did something similar recently. This is the overlay class, implementing IIngameOverlay: https://github.com/Syrikal/alchemine/blob/main/src/main/java/syric/alchemine/client/VitaSlimeOverlay.java Here's my overlay registry class, which triggers at the same time I register blocks and items and whatnot: https://github.com/Syrikal/alchemine/blob/aae585ab7d23fd4be688a60642f200ab09a4fe38/src/main/java/syric/alchemine/setup/AlchemineOverlays.java And here's where I turn the overlays on and off (it's triggered by RenderFogEvent because it happens concurrently with some fog stuff, but this is a little hacky.) https://github.com/Syrikal/alchemine/blob/47d9d08a092d3e26441bace8fb5014db0c78c332/src/main/java/syric/alchemine/client/FogEffects.java
  22. That seems... excessive. Perhaps override the default lava pool feature to include some seed blocks of dry grass, which then spread if they receive a random tick and are near lava? It does a little of what you want, I guess. A way to override the functions of a vanilla block would instantly solve your problem. This thread suggests outright replacing vanilla blocks with your new block (functionally identical to grass but with a randomtick method that dries it in the right circumstances). I'm leery of messing with vanilla because it seems like a compatibility hazard, but it might be worth investigating.
  23. Yes, I know. That's what I'm asking for help with. I'm not sure which methods to use to check if their hitbox intersects the block's.
×
×
  • Create New...

Important Information

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