Jump to content

Question regarding spawning in entities.


nbreech

Recommended Posts

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
Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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



×
×
  • Create New...

Important Information

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