Added entity_sitting property to allow player and some other entities to sit

This commit is contained in:
D3v1s0m 2024-12-10 10:56:20 +04:00
parent 8b1fde3652
commit a7bf542eb3
No known key found for this signature in database
GPG key ID: FA1F770C7B1D40C1
6 changed files with 173 additions and 1 deletions

View file

@ -0,0 +1,72 @@
package lol.pyr.znpcsplus.entity;
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
import lol.pyr.znpcsplus.api.entity.EntityProperty;
import lol.pyr.znpcsplus.api.entity.PropertyHolder;
import org.bukkit.inventory.ItemStack;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* Represents an armor stand vehicle entity.
* <p>
* This entity is used to make the NPC sit on an invisible armor stand.
* </p>
*/
public class ArmorStandVehicleEntity implements PropertyHolder {
private final Map<EntityPropertyImpl<?>, Object> propertyMap = new HashMap<>();
public ArmorStandVehicleEntity(EntityPropertyRegistryImpl propertyRegistry) {
setProperty(propertyRegistry.getByName("small", Boolean.class), true);
setProperty(propertyRegistry.getByName("invisible", Boolean.class), true);
setProperty(propertyRegistry.getByName("base_plate", Boolean.class), false);
}
@SuppressWarnings("unchecked")
public <T> T getProperty(EntityProperty<T> key) {
return hasProperty(key) ? (T) propertyMap.get((EntityPropertyImpl<?>) key) : key.getDefaultValue();
}
@Override
public boolean hasProperty(EntityProperty<?> key) {
return propertyMap.containsKey((EntityPropertyImpl<?>) key);
}
@SuppressWarnings("unchecked")
@Override
public <T> void setProperty(EntityProperty<T> key, T value) {
Object val = value;
if (val instanceof ItemStack) val = SpigotConversionUtil.fromBukkitItemStack((ItemStack) val);
setProperty((EntityPropertyImpl<T>) key, (T) val);
}
@Override
public void setItemProperty(EntityProperty<?> key, ItemStack value) {
throw new UnsupportedOperationException("Cannot set item properties on armor stands");
}
@Override
public ItemStack getItemProperty(EntityProperty<?> key) {
throw new UnsupportedOperationException("Cannot get item properties on armor stands");
}
public <T> void setProperty(EntityPropertyImpl<T> key, T value) {
if (key == null) return;
if (value == null || value.equals(key.getDefaultValue())) propertyMap.remove(key);
else propertyMap.put(key, value);
}
public Set<EntityProperty<?>> getAllProperties() {
return Collections.unmodifiableSet(propertyMap.keySet());
}
@Override
public Set<EntityProperty<?>> getAppliedProperties() {
return Collections.unmodifiableSet(propertyMap.keySet());
}
}

View file

@ -182,6 +182,8 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
register(new BooleanProperty("baby", babyIndex, false, legacyBooleans));
}
register(new EntitySittingProperty(packetFactory, this));
// Player
register(new DummyProperty<>("skin", SkinDescriptor.class, false));
final int skinLayersIndex;

View file

@ -4,6 +4,7 @@ import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import lol.pyr.znpcsplus.ZNpcsPlusBootstrap;
import lol.pyr.znpcsplus.api.entity.EntityProperty;
import lol.pyr.znpcsplus.api.entity.PropertyHolder;
import lol.pyr.znpcsplus.packets.PacketFactory;
@ -13,6 +14,7 @@ import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.util.Collection;
import java.util.HashMap;
import java.util.Set;
import java.util.UUID;
@ -26,6 +28,8 @@ public class PacketEntity implements PropertyHolder {
private final EntityType type;
private NpcLocation location;
private final HashMap<String, Object> metadata = new HashMap<>();
public PacketEntity(PacketFactory packetFactory, PropertyHolder properties, EntityType type, NpcLocation location) {
this.packetFactory = packetFactory;
this.properties = properties;
@ -67,6 +71,15 @@ public class PacketEntity implements PropertyHolder {
public void despawn(Player player) {
packetFactory.destroyEntity(player, this, properties);
if (hasMetadata("ridingVehicle")) {
try {
PacketEntity armorStand = (PacketEntity) getMetadata("ridingVehicle");
armorStand.despawn(player);
} catch (Exception e) {
//noinspection CallToPrintStackTrace
e.printStackTrace();
}
}
}
public void refreshMeta(Player player) {
@ -116,4 +129,32 @@ public class PacketEntity implements PropertyHolder {
public Set<EntityProperty<?>> getAppliedProperties() {
return properties.getAppliedProperties();
}
public void setMetadata(String key, Object value) {
metadata.put(key, value);
}
public Object getMetadata(String key) {
return metadata.get(key);
}
public <T> T getMetadata(String key, Class<T> type) {
try {
return type.cast(metadata.get(key));
} catch (ClassCastException e) {
return null;
}
}
public void removeMetadata(String key) {
metadata.remove(key);
}
public boolean hasMetadata(String key) {
return metadata.containsKey(key);
}
public void clearMetadata() {
metadata.clear();
}
}

View file

@ -0,0 +1,53 @@
package lol.pyr.znpcsplus.entity.properties;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSetPassengers;
import lol.pyr.znpcsplus.entity.ArmorStandVehicleEntity;
import lol.pyr.znpcsplus.entity.EntityPropertyImpl;
import lol.pyr.znpcsplus.entity.EntityPropertyRegistryImpl;
import lol.pyr.znpcsplus.entity.PacketEntity;
import lol.pyr.znpcsplus.packets.PacketFactory;
import org.bukkit.entity.Player;
import java.util.Map;
public class EntitySittingProperty extends EntityPropertyImpl<Boolean> {
private final PacketFactory packetFactory;
private final EntityPropertyRegistryImpl propertyRegistry;
public EntitySittingProperty(PacketFactory packetFactory, EntityPropertyRegistryImpl propertyRegistry) {
super("entity_sitting", false, Boolean.class);
this.packetFactory = packetFactory;
this.propertyRegistry = propertyRegistry;
}
@Override
public void apply(Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
boolean sitting = entity.getProperty(this);
if (sitting) {
ArmorStandVehicleEntity vehicleEntity = new ArmorStandVehicleEntity(propertyRegistry);
PacketEntity vehiclePacketEntity = new PacketEntity(packetFactory, vehicleEntity, EntityTypes.ARMOR_STAND, entity.getLocation().withY(entity.getLocation().getY() - 0.9));
vehiclePacketEntity.spawn(player);
entity.setMetadata("ridingVehicle", vehiclePacketEntity);
PacketEvents.getAPI().getPlayerManager().sendPacket(player, new WrapperPlayServerSetPassengers(
vehiclePacketEntity.getEntityId(),
new int[]{entity.getEntityId()}
));
} else {
if (entity.hasMetadata("ridingVehicle")) {
PacketEntity vehicleEntity = (PacketEntity) entity.getMetadata("ridingVehicle");
PacketEvents.getAPI().getPlayerManager().sendPacket(player, new WrapperPlayServerSetPassengers(
vehicleEntity.getEntityId(),
new int[]{}
));
vehicleEntity.despawn(player);
entity.removeMetadata("ridingVehicle");
// Send a packet to reset the npc's position
packetFactory.teleportEntity(player, entity);
}
}
}
}

View file

@ -86,6 +86,10 @@ public class NpcImpl extends Viewable implements Npc {
public void setLocation(NpcLocation location) {
this.location = location;
entity.setLocation(location, getViewers());
if (entity.hasMetadata("ridingVehicle")) {
PacketEntity armorStand = (PacketEntity) entity.getMetadata("ridingVehicle");
armorStand.setLocation(location.withY(location.getY() - 0.9), getViewers());
}
hologram.setLocation(location.withY(location.getY() + type.getHologramOffset()));
}

View file

@ -118,7 +118,7 @@ public class NpcTypeImpl implements NpcType {
"potion_color", "potion_ambient", "display_name", "permission_required",
"player_knockback", "player_knockback_exempt_permission", "player_knockback_distance", "player_knockback_vertical",
"player_knockback_horizontal", "player_knockback_cooldown", "player_knockback_sound", "player_knockback_sound_name",
"player_knockback_sound_volume", "player_knockback_sound_pitch");
"player_knockback_sound_volume", "player_knockback_sound_pitch", "entity_sitting");
if (!type.equals(EntityTypes.PLAYER)) addProperties("dinnerbone");
// TODO: make this look nicer after completing the rest of the properties
if (version.isNewerThanOrEquals(ServerVersion.V_1_9)) addProperties("glow");