Jump to content
View in the app

A better way to browse. Learn more.

Forge Forums

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.

Featured Replies

Posted

Long story short: I have an item that, when right clicked, does two things.
1. Spawns in an entity
2. Sets the player who spawned it in as riding that entity.

This "works", but the problem is that it takes multiple ticks for the entity's existence to be recognized by the client. This creates a really jarring effect where the player will hover frozen in the air for a 1/4-1/2 of a second before it resumes as expected. Since the entity moves rather quickly this can also be extremely disorienting to the player, making using the item much less fun and rather confusing.

A hypothetical solution would be, in my mind, to add a stage to the entity spawning process that first spawns it on the client side, then a packet is sent to the server to tell it that the entity exists and to tell the server to tell everyone else that the entity exists.
However, this goes against how minecraft fundamentally does its entity creation, which is the server is told to make an entity, then tell everyone on the server of the entities existence and synchronizes everything. I don't think either minecraft or forge has the ability to accommodate the method above, so that leaves behind the $64,000 question.
 

Q: Is ensuring that a client recognizes an entity the first tick it is created even possible (that is, is this a solution I should be persuing?), and if so, how?

 

    private static EntityType.Builder<HoverBoardEntity> hoverBoard() {
        return EntityType.Builder.<HoverBoardEntity>of(HoverBoardEntity::new, MobCategory.MISC)
                .sized(1.0f, 0.1f)
                .fireImmune()
                .setUpdateInterval(1)
                .clientTrackingRange(10)
                .setCustomClientFactory((spawnEntity, level) -> new HoverBoardEntity(MyEntities.HOVER_BOARD_ENTITY.get(), level));
    }
public class HoverBoardEntity extends Entity {

    protected static final EntityDataAccessor<Optional<UUID>> OWNER_UNIQUE_ID = SynchedEntityData.defineId(HoverBoardEntity.class, EntityDataSerializers.OPTIONAL_UUID);

    public HoverBoardEntity(EntityType<?> entityTypeIn, Level worldIn) {
        super(entityTypeIn, worldIn);
    }

    public HoverBoardEntity(Level worldIn) {
        this(MyEntities.HOVER_BOARD_ENTITY.get(), worldIn);
        this.setNoGravity(true);
        this.noPhysics = true;
    }

    @Override
    protected void defineSynchedData() {
        this.entityData.define(OWNER_UNIQUE_ID, Optional.empty());
    }

    @Override
    protected void readAdditionalSaveData(CompoundTag compound) {
        UUID uuid;
        if (compound.hasUUID("Owner")) {
            uuid = compound.getUUID("Owner");
        } else {
            String s = compound.getString("Owner");
            uuid = OldUsersConverter.convertMobOwnerIfNecessary(this.getServer(), s);
        }

        if (uuid != null) {
            try {
                this.setOwnerId(uuid);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    protected void addAdditionalSaveData(@NotNull CompoundTag compound) {
        if (this.getOwnerId() != null) {
            compound.putUUID("Owner", this.getOwnerId());
        }
    }

    @Override
    public void tick() {
        super.tick();
        System.out.println(this.getPlayer());
        if (this.getPassengers().isEmpty()) {
            this.setCoolDown(this.tickCount * 3 + 60);
            this.givePlayerMomentum();
            this.remove(RemovalReason.DISCARDED);
        } else
            if (this.tickCount >= 100) {
            boolean creativeFlag = false;
            for (Entity e : this.getPassengers()) {
                if (e instanceof Player && ((Player) e).isCreative()) creativeFlag = true;
                e.stopRiding();
            }
            this.setCoolDown(!creativeFlag ? 300 : 10);
            this.givePlayerMomentum();
            this.remove(RemovalReason.DISCARDED);
        } else {
            this.setDeltaMovement(this.getDeltaMovement().x, this.getDeltaMovement().y * 0.9D, this.getDeltaMovement().z);
            this.move(MoverType.PLAYER, this.getDeltaMovement());
            if (this.getPlayer() != null) {
                Player player = this.getPlayer();
                Vec3 collide = GravitationEffect.collide(player, this.getDeltaMovement());
                if(collide.x != this.getDeltaMovement().x || collide.z != this.getDeltaMovement().z) {
                    for (Entity e : this.getPassengers()) {
                        e.stopRiding();
                    }
                    this.setCoolDown(300);
                    this.givePlayerMomentum();
                    this.remove(RemovalReason.DISCARDED);
                }
            }
        }
    }

    @Override
    public double getPassengersRidingOffset() {
        return 0.2D;
    }

    @Nullable
    public UUID getOwnerId() {
        return this.entityData.get(OWNER_UNIQUE_ID).orElse(null);
    }

    @Override
    public boolean shouldRiderSit() {
        return false;
    }

    @Override
    protected void removePassenger(@NotNull Entity passenger) {
        super.removePassenger(passenger);
        passenger.setDeltaMovement(this.getDeltaMovement());
    }

    public void setOwnerId(@Nullable UUID p_184754_1_) {
        this.entityData.set(OWNER_UNIQUE_ID, Optional.ofNullable(p_184754_1_));
    }

    private void setCoolDown(int cooldown) {
        UUID uuid = this.getOwnerId();
        if (uuid != null) {
            Player player = this.level.getPlayerByUUID(uuid);
            if (player != null) {
                player.getCooldowns().addCooldown(RareoresItems.HOVER_BOARD.get(), cooldown);
            }
        }
    }

    private boolean givePlayerMomentum() {
        Player player = this.getPlayer();
        if (player != null) {
            player.setDeltaMovement(this.getDeltaMovement());
            return true;
        }
        return false;
    }

    private Player getPlayer() {
        UUID uuid = this.getOwnerId();
        if (uuid != null) {
            return this.level.getPlayerByUUID(uuid);
        }
        return null;
    }

    @Override
    public @NotNull Vec3 getDismountLocationForPassenger(@NotNull LivingEntity livingEntity) {
        return new Vec3(this.position().x, this.position().y, this.position().z);
    }

    @Override
    public void onPassengerTurned(Entity pEntityToUpdate) {
        super.onPassengerTurned(pEntityToUpdate);
    }

    @Override
    public boolean canCollideWith(Entity p) {
        return false;
    }

    @Override
    public @NotNull Packet<?> getAddEntityPacket() {
        return NetworkHooks.getEntitySpawningPacket(this);
    }
}
public class ItemHoverBoard extends Item {
    public ItemHoverBoard(Properties properties) {
        super(properties);
    }

    @Override
    public InteractionResultHolder<ItemStack> use(Level level, Player playerIn, InteractionHand hand) {
        ItemStack itemstack = playerIn.getItemInHand(hand);
        if(!playerIn.isPassenger()) {
            playerIn.startUsingItem(hand);
            double x = playerIn.getViewVector(0).x;
            double z = playerIn.getViewVector(0).z;
            double ratio = 1.0D / Math.sqrt((x * x + z * z));
            AttributeInstance gravity = playerIn.getAttribute(net.minecraftforge.common.ForgeMod.ENTITY_GRAVITY.get());
            Vec3 motion = new Vec3(x * ratio * 0.667D, playerIn.getDeltaMovement().y + gravity.getValue(), z * ratio * 0.667D);
            HoverBoardEntity entity = new HoverBoardEntity(level);
            if (!level.isClientSide()) {
                level.addFreshEntity(entity);
            }
            entity.setPosRaw(playerIn.position().x, playerIn.position().y + 0.5D, playerIn.position().z);
            playerIn.startRiding(entity);
            entity.setOwnerId(playerIn.getUUID());
            entity.setDeltaMovement(motion);
            return InteractionResultHolder.sidedSuccess(itemstack, level.isClientSide());
        }
        return InteractionResultHolder.pass(itemstack);
    }
}

 

Edited by nbreech

  • Author
12 minutes ago, diesieben07 said:

Show your code. This is not normal behavior.

Added the relevant bits.

  • Author

I was under the impression that I was preventing that by calling level.addFreshEntity(ent) within an if block that checks if the world is not client side e.g. 

if (!level.isClientSide()) {
    level.addFreshEntity(entity);
}

I mimicked the code I found in the vanilla boat item class, which does the same thing.

  • Author

I got it to work by using the spawn egg code instead, thanks for helping me brainstorm. :)

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...

Important Information

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

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.