Jump to content

Recommended Posts

Posted
7 minutes ago, Jay Avery said:

An update for anyone who's interested. I've made a searching algorithm that finds all connected trunk blocks, and connected leaves up to a certain count, starting closest to the trunk. I use it in this method, (and use the search results to make all the tree blocks fall). If anyone has ideas on how to improve the performance I'd be grateful - I know I've used several different collections to keep track of all the checking but I couldn't think of any way to reduce that further.

 

Posted
On 3/20/2017 at 9:45 AM, Jay Avery said:

An update for anyone who's interested. I've made a searching algorithm that finds all connected trunk blocks, and connected leaves up to a certain count, starting closest to the trunk. I use it in this method, (and use the search results to make all the tree blocks fall). If anyone has ideas on how to improve the performance I'd be grateful - I know I've used several different collections to keep track of all the checking but I couldn't think of any way to reduce that further.

 

I'm using a method very similar to yours. I've gone through several versions of this already. In my current iteration I'm only using two collections: one for blocks that need to be checked, and one for blocks I've already checked. The collection of blocks that need to be checked is an ArrayList. I use an iterator to work through it and only add blocks to it if they are logs and aren't in the list of blocks I've already checked. When I'm done, theoretically, it should only contain log blocks that need to fall.

 

However, I'm having trouble getting my collections to compare BlockPos objects correctly. It only seems to detect equivalents some of the time, so I always end up with duplicate BlockPos objects in my final list. Is that happening to you?

 

One idea for improving performance is to check for what type of log is being broken. If it's a birch log, for example, you only need to check logs directly above (no branches in birch trees).

Posted
7 hours ago, Daeruin said:

 

I'm using a method very similar to yours. I've gone through several versions of this already. In my current iteration I'm only using two collections: one for blocks that need to be checked, and one for blocks I've already checked. The collection of blocks that need to be checked is an ArrayList. I use an iterator to work through it and only add blocks to it if they are logs and aren't in the list of blocks I've already checked. When I'm done, theoretically, it should only contain log blocks that need to fall.

 

However, I'm having trouble getting my collections to compare BlockPos objects correctly. It only seems to detect equivalents some of the time, so I always end up with duplicate BlockPos objects in my final list. Is that happening to you?

 

One idea for improving performance is to check for what type of log is being broken. If it's a birch log, for example, you only need to check logs directly above (no branches in birch trees).

Interesting!

 

I started off trying to use an iterator but that caused ConcurrentModificationExceptions when I added to the list during iteration - that's why I switched to a Queue. How do you avoid that problem?

 

I haven't noticed any problem with BlockPos duplication but I haven't checked carefully. I'll try printing out the results list before using it and see if it does contain any duplicates. How are you checking for duplicates before adding to the results list? BlockPos uses Vec3i#equals which just compares the three co-ordinates so I can't think of a reason that wouldn't work reliably.

 

Would you care to share your code?

Posted
8 hours ago, Daeruin said:

 

One idea for improving performance is to check for what type of log is being broken. If it's a birch log, for example, you only need to check logs directly above (no branches in birch trees).

On the surface this seems like a good optimization, but I think it would break down when doing modded trees that change the structure of a birch tree.  Currently working on a modpack that has birch, oak, spruce, etc. that defy the normal vanilla growth patterns.

Posted
15 hours ago, Jay Avery said:

Interesting!

 

I started off trying to use an iterator but that caused ConcurrentModificationExceptions when I added to the list during iteration - that's why I switched to a Queue. How do you avoid that problem?

 

I haven't noticed any problem with BlockPos duplication but I haven't checked carefully. I'll try printing out the results list before using it and see if it does contain any duplicates. How are you checking for duplicates before adding to the results list? BlockPos uses Vec3i#equals which just compares the three co-ordinates so I can't think of a reason that wouldn't work reliably.

 

Would you care to share your code?

An iterator works fine if you use the iterator's add and remove methods (iterator.add). I discovered that after running into the ConcurrentModificationException and doing some more research. There's another problem, however, because the iterator's cursor is always in between elements in the collection. When you add a new object to the collection, it gets placed behind the cursor. I have to keep track of how many things I've added and move the iterator back to look at them. I'm thinking I'll switch back to a queue at this point. The only reason I wanted to try the ArrayList in the first place was to see if it was somehow more accurate with detecting duplicates in the collection.

 

I've tried using a LinkedList, an ArrayList, and now a hash set to hold the blocks I've already looked at. In each case, I just used the collection's #contains method to see if the BlockPos is already in the list. Looks like the same thing you're doing here:

 

if (!checked.contains(toAdd))

 

My code is currently part of a private BitBucket repository. I didn't want it to be public at first, because I was embarrassed. :) I was going to post some of my code here , but it looks like it isn't possible to do spoilers or code blocks in topic replies. Unless I'm missing something?

 

Come to think of it, how do you do that monospace font in replies? All I see are bold, italic, and underline.

 

By the way, I loved your idea of using a list of BlockPos offsets and iterating through that to scan for log blocks. I tried that in my code, and it's so much cleaner than what I was doing before, which was basically a giant block of code with a million BlockPos.up().north().east() references and the like.

  • Like 1
Posted
14 hours ago, OreCruncher said:

On the surface this seems like a good optimization, but I think it would break down when doing modded trees that change the structure of a birch tree.  Currently working on a modpack that has birch, oak, spruce, etc. that defy the normal vanilla growth patterns.

Good point! For that matter, it seems possible that modded trees could defy the pattern Draco pointed out early in this thread. For example, you could have long branches with log blocks below another log block.

Posted

I just realized another tradeoff between LinkedList and ArrayList. With the ArrayList, you can keep it around for later use. With the LinkedList, you can't look at an element in the list with removing it from the list, which requires you to store things in yet another list if you want to do something with them later. But if you do everything you need to at the time you get the element from the list, you wouldn't need to store them elsewhere.

Posted
9 hours ago, Daeruin said:

An iterator works fine if you use the iterator's add and remove methods (iterator.add). I discovered that after running into the ConcurrentModificationException and doing some more research. There's another problem, however, because the iterator's cursor is always in between elements in the collection. When you add a new object to the collection, it gets placed behind the cursor. I have to keep track of how many things I've added and move the iterator back to look at them. I'm thinking I'll switch back to a queue at this point. The only reason I wanted to try the ArrayList in the first place was to see if it was somehow more accurate with detecting duplicates in the collection.

Ohh, I remember now - I found out you could add elements to an iterator but I was unhappy about the cursor positioning problem which is why I went with a queue. That way, the elements which are added later are always searched later, so that guarantees that the leaves closest to the trunk will be selected first if the search gets stopped by the maximum count (rather than by reaching the edge of all leaves).

 

9 hours ago, Daeruin said:

 

I've tried using a LinkedList, an ArrayList, and now a hash set to hold the blocks I've already looked at. In each case, I just used the collection's #contains method to see if the BlockPos is already in the list. Looks like the same thing you're doing here:

 

if (!checked.contains(toAdd))

I did a test with printing out my results list before using it and there don't seem to be any duplicates - I can't think of a reason that would be happening for you!

 

9 hours ago, Daeruin said:

 

My code is currently part of a private BitBucket repository. I didn't want it to be public at first, because I was embarrassed. :) I was going to post some of my code here , but it looks like it isn't possible to do spoilers or code blocks in topic replies. Unless I'm missing something?

 

Come to think of it, how do you do that monospace font in replies? All I see are bold, italic, and underline.

You can post code! :) The eye icon is a spoiler, and the <> icon is for a block of code. And the monospace font is the tt button.

9 hours ago, Daeruin said:

By the way, I loved your idea of using a list of BlockPos offsets and iterating through that to scan for log blocks. I tried that in my code, and it's so much cleaner than what I was doing before, which was basically a giant block of code with a million BlockPos.up().north().east() references and the like.

Thanks! It backfired at first because I made the list of offsets manually and ended up missing some, so then I gave in and wrote code to print a list of all offsets so that I could copy that back into the code. :P 

Posted
13 hours ago, Jay Avery said:

I did a test with printing out my results list before using it and there don't seem to be any duplicates - I can't think of a reason that would be happening for you!

I figured out why. It has to do with the way I'm adding things to the list. I was checking to see if the BlockPos was in the list of things I already checked, but I wasn't checking to see if it was already in the list waiting to be checked. Because I do a round of adding stuff to the queue up front, before starting to iterate on the queue, those things were getting added to the queue twice. Should be pretty easy to fix.

 

13 hours ago, Jay Avery said:

You can post code! :) The eye icon is a spoiler, and the <> icon is for a block of code. And the monospace font is the tt button.

I don't see an eye icon or <> icon. I have a single row of icons with B, I, U, link, quote, emoticon, two lists, and preview.

 

It looks like you may be doing something special with making the blocks fall. Are you just breaking and harvesting the blocks, or do you have some nifty method for making the blocks fall to the ground horizontally like a real tree? I've been messing with that piece and having some weird behavior for big oak, jungle trees, and regular oaks with branches. I think I can deal with it, but I was curious to see if you've already solved those problems.

Posted
22 minutes ago, diesieben07 said:

This sounds like the use-case for a Set.

A set won't give first-in-first-out order though, which is needed in this situation. It looks like there's no existing collection that has these exact properties (e.g. this conversation) - a LinkedHashSet might nearly work except the iterator can't be added to.

Posted

 

5 hours ago, Daeruin said:

It looks like you may be doing something special with making the blocks fall. Are you just breaking and harvesting the blocks, or do you have some nifty method for making the blocks fall to the ground horizontally like a real tree? I've been messing with that piece and having some weird behavior for big oak, jungle trees, and regular oaks with branches. I think I can deal with it, but I was curious to see if you've already solved those problems.

I make the blocks actually fall - I made a custom falling block entity that moves sideways as it falls, so that the blocks end up going roughly diagonally like a real tree. Here is my entity class - I've got slightly different behaviour for logs and leaves, to make sure that logs are prioritised as much as possible and fall through leaves that are in the way.

  • Like 1

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • Version 1.19 - Forge 41.0.63 I want to create a wolf entity that I can ride, so far it seems to be working, but the problem is that when I get on the wolf, I can’t control it. I then discovered that the issue is that the server doesn’t detect that I’m riding the wolf, so I’m struggling with synchronization. However, it seems to not be working properly. As I understand it, the server receives the packet but doesn’t register it correctly. I’m a bit new to Java, and I’ll try to provide all the relevant code and prints *The comments and prints are translated by chatgpt since they were originally in Spanish* Thank you very much in advance No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. MountableWolfEntity package com.vals.valscraft.entity; import com.vals.valscraft.network.MountSyncPacket; import com.vals.valscraft.network.NetworkHandler; import net.minecraft.client.Minecraft; import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.EntityDataSerializers; import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.ai.attributes.AttributeSupplier; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.animal.Wolf; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.Entity; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec3; import net.minecraftforge.event.TickEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.network.PacketDistributor; public class MountableWolfEntity extends Wolf { private boolean hasSaddle; private static final EntityDataAccessor<Byte> DATA_ID_FLAGS = SynchedEntityData.defineId(MountableWolfEntity.class, EntityDataSerializers.BYTE); public MountableWolfEntity(EntityType<? extends Wolf> type, Level level) { super(type, level); this.hasSaddle = false; } @Override protected void defineSynchedData() { super.defineSynchedData(); this.entityData.define(DATA_ID_FLAGS, (byte)0); } public static AttributeSupplier.Builder createAttributes() { return Wolf.createAttributes() .add(Attributes.MAX_HEALTH, 20.0) .add(Attributes.MOVEMENT_SPEED, 0.3); } @Override public InteractionResult mobInteract(Player player, InteractionHand hand) { ItemStack itemstack = player.getItemInHand(hand); if (itemstack.getItem() == Items.SADDLE && !this.hasSaddle()) { if (!player.isCreative()) { itemstack.shrink(1); } this.setSaddle(true); return InteractionResult.SUCCESS; } else if (!level.isClientSide && this.hasSaddle()) { player.startRiding(this); MountSyncPacket packet = new MountSyncPacket(true); // 'true' means the player is mounted NetworkHandler.CHANNEL.sendToServer(packet); // Ensure the server handles the packet return InteractionResult.SUCCESS; } return InteractionResult.PASS; } @Override public void travel(Vec3 travelVector) { if (this.isVehicle() && this.getControllingPassenger() instanceof Player) { System.out.println("The wolf has a passenger."); System.out.println("The passenger is a player."); Player player = (Player) this.getControllingPassenger(); // Ensure the player is the controller this.setYRot(player.getYRot()); this.yRotO = this.getYRot(); this.setXRot(player.getXRot() * 0.5F); this.setRot(this.getYRot(), this.getXRot()); this.yBodyRot = this.getYRot(); this.yHeadRot = this.yBodyRot; float forward = player.zza; float strafe = player.xxa; if (forward <= 0.0F) { forward *= 0.25F; } this.flyingSpeed = this.getSpeed() * 0.1F; this.setSpeed((float) this.getAttributeValue(Attributes.MOVEMENT_SPEED) * 1.5F); this.setDeltaMovement(new Vec3(strafe, travelVector.y, forward).scale(this.getSpeed())); this.calculateEntityAnimation(this, false); } else { // The wolf does not have a passenger or the passenger is not a player System.out.println("No player is mounted, or the passenger is not a player."); super.travel(travelVector); } } public boolean hasSaddle() { return this.hasSaddle; } public void setSaddle(boolean hasSaddle) { this.hasSaddle = hasSaddle; } @Override protected void dropEquipment() { super.dropEquipment(); if (this.hasSaddle()) { this.spawnAtLocation(Items.SADDLE); this.setSaddle(false); } } @SubscribeEvent public static void onServerTick(TickEvent.ServerTickEvent event) { if (event.phase == TickEvent.Phase.START) { MinecraftServer server = net.minecraftforge.server.ServerLifecycleHooks.getCurrentServer(); if (server != null) { for (ServerPlayer player : server.getPlayerList().getPlayers()) { if (player.isPassenger() && player.getVehicle() instanceof MountableWolfEntity) { MountableWolfEntity wolf = (MountableWolfEntity) player.getVehicle(); System.out.println("Tick: " + player.getName().getString() + " is correctly mounted on " + wolf); } } } } } private boolean lastMountedState = false; @Override public void tick() { super.tick(); if (!this.level.isClientSide) { // Only on the server boolean isMounted = this.isVehicle() && this.getControllingPassenger() instanceof Player; // Only print if the state changed if (isMounted != lastMountedState) { if (isMounted) { Player player = (Player) this.getControllingPassenger(); // Verify the passenger is a player System.out.println("Server: Player " + player.getName().getString() + " is now mounted."); } else { System.out.println("Server: The wolf no longer has a passenger."); } lastMountedState = isMounted; } } } @Override public void addPassenger(Entity passenger) { super.addPassenger(passenger); if (passenger instanceof Player) { Player player = (Player) passenger; if (!this.level.isClientSide && player instanceof ServerPlayer) { // Send the packet to the server to indicate the player is mounted NetworkHandler.CHANNEL.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), new MountSyncPacket(true)); } } } @Override public void removePassenger(Entity passenger) { super.removePassenger(passenger); if (passenger instanceof Player) { Player player = (Player) passenger; if (!this.level.isClientSide && player instanceof ServerPlayer) { // Send the packet to the server to indicate the player is no longer mounted NetworkHandler.CHANNEL.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), new MountSyncPacket(false)); } } } @Override public boolean isControlledByLocalInstance() { Entity entity = this.getControllingPassenger(); return entity instanceof Player; } @Override public void positionRider(Entity passenger) { if (this.hasPassenger(passenger)) { double xOffset = Math.cos(Math.toRadians(this.getYRot() + 90)) * 0.4; double zOffset = Math.sin(Math.toRadians(this.getYRot() + 90)) * 0.4; passenger.setPos(this.getX() + xOffset, this.getY() + this.getPassengersRidingOffset() + passenger.getMyRidingOffset(), this.getZ() + zOffset); } } } MountSyncPacket package com.vals.valscraft.network; import com.vals.valscraft.entity.MountableWolfEntity; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player; import net.minecraftforge.network.NetworkEvent; import java.util.function.Supplier; public class MountSyncPacket { private final boolean isMounted; public MountSyncPacket(boolean isMounted) { this.isMounted = isMounted; } public void encode(FriendlyByteBuf buffer) { buffer.writeBoolean(isMounted); } public static MountSyncPacket decode(FriendlyByteBuf buffer) { return new MountSyncPacket(buffer.readBoolean()); } public void handle(NetworkEvent.Context context) { context.enqueueWork(() -> { ServerPlayer player = context.getSender(); // Get the player from the context if (player != null) { // Verifies if the player has dismounted if (!isMounted) { Entity vehicle = player.getVehicle(); if (vehicle instanceof MountableWolfEntity wolf) { // Logic to remove the player as a passenger wolf.removePassenger(player); System.out.println("Server: Player " + player.getName().getString() + " is no longer mounted."); } } } }); context.setPacketHandled(true); // Marks the packet as handled } } networkHandler package com.vals.valscraft.network; import com.vals.valscraft.valscraft; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.network.NetworkRegistry; import net.minecraftforge.network.simple.SimpleChannel; import net.minecraftforge.network.NetworkEvent; import java.util.function.Supplier; public class NetworkHandler { private static final String PROTOCOL_VERSION = "1"; public static final SimpleChannel CHANNEL = NetworkRegistry.newSimpleChannel( new ResourceLocation(valscraft.MODID, "main"), () -> PROTOCOL_VERSION, PROTOCOL_VERSION::equals, PROTOCOL_VERSION::equals ); public static void init() { int packetId = 0; // Register the mount synchronization packet CHANNEL.registerMessage( packetId++, MountSyncPacket.class, MountSyncPacket::encode, MountSyncPacket::decode, (msg, context) -> msg.handle(context.get()) // Get the context with context.get() ); } }  
    • Do you use features of inventory profiles next (ipnext) or is there a change without it?
    • Remove rubidium - you are already using embeddium, which is a fork of rubidium
  • Topics

  • Who's Online (See full list)

×
×
  • Create New...

Important Information

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