Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: lay and spin #33

Open
1 task done
rvbsm opened this issue Mar 31, 2024 · 0 comments
Open
1 task done

feat: lay and spin #33

rvbsm opened this issue Mar 31, 2024 · 0 comments
Assignees
Labels
✨ feature New feature or request

Comments

@rvbsm
Copy link
Owner

rvbsm commented Mar 31, 2024

✍️ Describe the feature you'd like to request

Coming from #10.

GSit has /lay and /spin features, and it will make a fine addition to the mod collection.

💡 Describe the solution you'd like to see

For /lay, GSit tricks the client that a bed exists at y = -63 and spawns a fake player with the EntityPose.SLEEPING pose.

For /spin, as far as I understand it, it sends a rotation packet

ℹ️ Additional information

Creating the fake player
private ServerPlayer createNPC() {

    MinecraftServer minecraftServer = ((CraftServer) Bukkit.getServer()).getServer();

    ServerLevel serverLevel = ((CraftWorld) seat.getLocation().getWorld()).getHandle();

    GameProfile gameProfile = new GameProfile(UUID.randomUUID(), seatPlayer.getName());

    gameProfile.getProperties().putAll(serverPlayer.getGameProfile().getProperties());

    ClientInformation clientInformation = serverPlayer.clientInformation();

    ServerPlayer sPlayer = new ServerPlayer(minecraftServer, serverLevel, gameProfile, clientInformation);

    sPlayer.connection = serverPlayer.connection;

    return sPlayer;
}
Prepare the fake player and register packets
playerNpc = createNPC();
playerNpc.moveTo(seatLocation.getX(), seatLocation.getY() + GPM.getSitManager().BASE_OFFSET + (pose == org.bukkit.entity.Pose.SLEEPING ? 0.1125d : 0d), seatLocation.getZ(), 0f, 0f);

blockLocation = seatLocation.clone();
blockLocation.setY(blockLocation.getWorld().getMinHeight());

bedBlock = blockLocation.getBlock();
bedPos = new BlockPos(blockLocation.getBlockX(), blockLocation.getBlockY(), blockLocation.getBlockZ());

direction = getDirection();

if(pose == org.bukkit.entity.Pose.SLEEPING) setBedPacket = new ClientboundBlockUpdatePacket(bedPos, Blocks.WHITE_BED.defaultBlockState().setValue(BedBlock.FACING, direction.getOpposite()).setValue(BedBlock.PART, BedPart.HEAD));
addNpcInfoPacket = new ClientboundPlayerInfoUpdatePacket(EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER, ClientboundPlayerInfoUpdatePacket.Action.INITIALIZE_CHAT, ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE, ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LATENCY, ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME), Collections.singletonList(playerNpc));
removeNpcInfoPacket = new ClientboundPlayerInfoRemovePacket(Collections.singletonList(playerNpc.getUUID()));
removeNpcPacket = new ClientboundRemoveEntitiesPacket(playerNpc.getId());
createNpcPacket = new ClientboundAddEntityPacket(playerNpc);
if(pose == org.bukkit.entity.Pose.SLEEPING) teleportNpcPacket = new ClientboundTeleportEntityPacket(playerNpc);
if(pose == org.bukkit.entity.Pose.SPIN_ATTACK) rotateNpcPacket = new ClientboundMoveEntityPacket.PosRot(playerNpc.getId(), (short) 0, (short) 0, (short) 0, (byte) 0, getFixedRotation(-90f), true);
Spawning the fake player
public void spawn() {

    nearPlayers = getNearPlayers();

    playerNpc.setGlowingTag(serverPlayer.hasGlowingTag());
    if(serverPlayer.hasGlowingTag()) serverPlayer.setGlowingTag(false);

    playerNpc.getEntityData().set(EntityDataSerializers.POSE.createAccessor(6), net.minecraft.world.entity.Pose.values()[pose.ordinal()]);
    if(pose == Pose.SPIN_ATTACK) playerNpc.getEntityData().set(EntityDataSerializers.BYTE.createAccessor(8), (byte) 4);
    if(pose == Pose.SLEEPING) playerNpc.getEntityData().set(EntityDataSerializers.OPTIONAL_BLOCK_POS.createAccessor(14), Optional.of(bedPos));
    playerNpc.getEntityData().set(EntityDataSerializers.BYTE.createAccessor(17), serverPlayer.getEntityData().get(EntityDataSerializers.BYTE.createAccessor(17)));
    playerNpc.getEntityData().set(EntityDataSerializers.BYTE.createAccessor(18), serverPlayer.getEntityData().get(EntityDataSerializers.BYTE.createAccessor(18)));
    playerNpc.getEntityData().set(EntityDataSerializers.COMPOUND_TAG.createAccessor(19), serverPlayer.getEntityData().get(EntityDataSerializers.COMPOUND_TAG.createAccessor(19)));
    playerNpc.getEntityData().set(EntityDataSerializers.COMPOUND_TAG.createAccessor(20), serverPlayer.getEntityData().get(EntityDataSerializers.COMPOUND_TAG.createAccessor(20)));
    serverPlayer.getEntityData().set(EntityDataSerializers.COMPOUND_TAG.createAccessor(19), new CompoundTag());
    serverPlayer.getEntityData().set(EntityDataSerializers.COMPOUND_TAG.createAccessor(20), new CompoundTag());

    serverPlayer.setInvisible(true);

    setEquipmentVisibility(false);

    if(pose == Pose.SLEEPING) {

        if(GPM.getCManager().P_LAY_NIGHT_SKIP) seatPlayer.setSleepingIgnored(true);

        if(GPM.getCManager().P_LAY_REST) seatPlayer.setStatistic(Statistic.TIME_SINCE_REST, 0);
    }

    metaNpcPacket = new ClientboundSetEntityDataPacket(playerNpc.getId(), playerNpc.getEntityData().isDirty() ? playerNpc.getEntityData().packDirty() : playerNpc.getEntityData().getNonDefaultValues());

    List<Packet<ClientGamePacketListener>> packages = new ArrayList<>();

    packages.add(addNpcInfoPacket);
    packages.add(createNpcPacket);
    if(pose == Pose.SLEEPING) packages.add(setBedPacket);
    packages.add(metaNpcPacket);
    if(pose == Pose.SLEEPING) packages.add(teleportNpcPacket);
    if(pose == Pose.SPIN_ATTACK) packages.add(rotateNpcPacket);

    bundle = new ClientboundBundlePacket(packages);

    for(Player nearPlayer : nearPlayers) spawnToPlayer(nearPlayer);

    Bukkit.getPluginManager().registerEvents(listener, GPM);

    ((SeatEntity) ((CraftEntity) seat.getSeatEntity()).getHandle()).setCallback(() -> {

        List<Player> playerList = getNearPlayers();

        for(Player nearPlayer : playerList) {

            if(nearPlayers.contains(nearPlayer)) continue;

            nearPlayers.add(nearPlayer);

            spawnToPlayer(nearPlayer);
        }

        for(Player nearPlayer : new ArrayList<>(nearPlayers)) {

            if(playerList.contains(nearPlayer)) continue;

            nearPlayers.remove(nearPlayer);

            removeToPlayer(nearPlayer);
        }

        if(pose != Pose.SPIN_ATTACK) updateDirection();

        serverPlayer.setInvisible(true);

        updateEquipment();

        setEquipmentVisibility(false);

        updateSkin();

        if(pose != Pose.SLEEPING || !GPM.getCManager().P_LAY_SNORING_SOUNDS) return;

        long tick = serverPlayer.getPlayerTime();

        if((!GPM.getCManager().P_LAY_SNORING_NIGHT_ONLY || (tick >= 12500 && tick <= 23500)) && tick % 90 == 0) {

            for(Player nearPlayer : nearPlayers) nearPlayer.playSound(seat.getLocation(), Sound.ENTITY_FOX_SLEEP, SoundCategory.PLAYERS, 1.5f, 0);
        }
    });
}

👨‍👧‍👦 Contributing

  • 🙋‍♂️ Yes, I'd be down to file a PR implementing this feature!
@rvbsm rvbsm added the ✨ feature New feature or request label Mar 31, 2024
@rvbsm rvbsm self-assigned this Mar 31, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
✨ feature New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant