mirror of
https://github.com/YouHaveTrouble/Stand-in.git
synced 2026-05-11 22:16:55 +00:00
clean up prototype code
This commit is contained in:
@@ -1,30 +1,40 @@
|
|||||||
package me.youhavetrouble.standin;
|
package me.youhavetrouble.standin;
|
||||||
|
|
||||||
|
import me.youhavetrouble.standin.entity.ArmorStandHandler;
|
||||||
|
import me.youhavetrouble.standin.entity.EntityHandler;
|
||||||
|
import me.youhavetrouble.standin.entity.MannequinHandler;
|
||||||
import me.youhavetrouble.standin.stand.StandinInteractionListener;
|
import me.youhavetrouble.standin.stand.StandinInteractionListener;
|
||||||
import org.bukkit.NamespacedKey;
|
import org.bukkit.NamespacedKey;
|
||||||
import org.bukkit.permissions.Permission;
|
import org.bukkit.entity.Entity;
|
||||||
import org.bukkit.permissions.PermissionDefault;
|
import org.bukkit.entity.EntityType;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
import java.util.List;
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
@SuppressWarnings("UnstableApiUsage")
|
@SuppressWarnings("UnstableApiUsage")
|
||||||
public final class StandIn extends JavaPlugin {
|
public final class StandIn extends JavaPlugin {
|
||||||
|
|
||||||
public static final NamespacedKey KEY = new NamespacedKey("stand-in", "stand-in");
|
public static final NamespacedKey KEY = new NamespacedKey("stand-in", "stand-in");
|
||||||
|
|
||||||
|
private final Map<EntityType, EntityHandler<? extends Entity>> entityHandlers = new HashMap<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
|
entityHandlers.put(EntityType.ARMOR_STAND, new ArmorStandHandler());
|
||||||
getServer().getPluginManager().addPermissions(
|
entityHandlers.put(EntityType.MANNEQUIN, new MannequinHandler());
|
||||||
List.of(
|
|
||||||
new Permission("standin.edit.armor_stand", PermissionDefault.OP),
|
|
||||||
new Permission("standin.edit.mannequin", PermissionDefault.OP)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
getServer().getPluginManager().registerEvents(new StandinInteractionListener(), this);
|
getServer().getPluginManager().registerEvents(new StandinInteractionListener(), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets entity handler for given entity class
|
||||||
|
* @param entityType entity type to get handler for
|
||||||
|
* @return Entity handler or null
|
||||||
|
*/
|
||||||
|
public @Nullable EntityHandler<? extends Entity> getEntityHandler(EntityType entityType) {
|
||||||
|
return entityHandlers.get(entityType);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,279 +0,0 @@
|
|||||||
package me.youhavetrouble.standin;
|
|
||||||
|
|
||||||
import io.papermc.paper.dialog.Dialog;
|
|
||||||
import io.papermc.paper.registry.data.dialog.ActionButton;
|
|
||||||
import io.papermc.paper.registry.data.dialog.DialogBase;
|
|
||||||
import io.papermc.paper.registry.data.dialog.action.DialogAction;
|
|
||||||
import io.papermc.paper.registry.data.dialog.body.DialogBody;
|
|
||||||
import io.papermc.paper.registry.data.dialog.input.DialogInput;
|
|
||||||
import io.papermc.paper.registry.data.dialog.input.SingleOptionDialogInput;
|
|
||||||
import io.papermc.paper.registry.data.dialog.type.DialogType;
|
|
||||||
import me.youhavetrouble.standin.converter.ArmorStandToMannequinConverter;
|
|
||||||
import me.youhavetrouble.standin.converter.EntityConverter;
|
|
||||||
import me.youhavetrouble.standin.converter.MannequinToArmorStandConverter;
|
|
||||||
import net.kyori.adventure.text.Component;
|
|
||||||
import net.kyori.adventure.text.event.ClickCallback;
|
|
||||||
import net.kyori.adventure.text.format.NamedTextColor;
|
|
||||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
|
||||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
|
||||||
import org.bukkit.entity.ArmorStand;
|
|
||||||
import org.bukkit.entity.Entity;
|
|
||||||
import org.bukkit.entity.Mannequin;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
// TODO generify this mess
|
|
||||||
|
|
||||||
@SuppressWarnings("UnstableApiUsage")
|
|
||||||
public class StandinDialog {
|
|
||||||
|
|
||||||
public static void openConversionDialog(@NotNull Player player, @NotNull ArmorStand armorStand) {
|
|
||||||
if (armorStand.isDead()) return;
|
|
||||||
|
|
||||||
UUID armorStandId = armorStand.getUniqueId();
|
|
||||||
UUID playerId = player.getUniqueId();
|
|
||||||
|
|
||||||
List<DialogInput> inputs = List.of(
|
|
||||||
DialogInput.singleOption("entity_type", Component.text("New entity type"), List.of(
|
|
||||||
SingleOptionDialogInput.OptionEntry.create("armor_stand", Component.text("Armor Stand"), true),
|
|
||||||
SingleOptionDialogInput.OptionEntry.create("mannequin", Component.text("Mannequin"), false)
|
|
||||||
)).build()
|
|
||||||
);
|
|
||||||
|
|
||||||
ActionButton saveButton = ActionButton.builder(Component.text("Change type")).action(
|
|
||||||
DialogAction.customClick((view, audience) -> {
|
|
||||||
if (!(audience instanceof Player callbackPlayer)) return;
|
|
||||||
Entity entity = callbackPlayer.getWorld().getEntity(armorStandId);
|
|
||||||
if (playerId != callbackPlayer.getUniqueId()) return;
|
|
||||||
if (!(entity instanceof ArmorStand stand)) return;
|
|
||||||
if (stand.isDead()) return;
|
|
||||||
String newEntityType = view.getText("entity_type");
|
|
||||||
switch (newEntityType) {
|
|
||||||
case "mannequin" -> {
|
|
||||||
EntityConverter<ArmorStand, Mannequin> mannequinConverter = new ArmorStandToMannequinConverter();
|
|
||||||
Mannequin mannequin = mannequinConverter.spawn(stand);
|
|
||||||
if (mannequin == null) {
|
|
||||||
audience.sendMessage(Component.text("Error spawning new entity. Ensure new entity can spawn in this spot.").color(NamedTextColor.RED));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
stand.remove();
|
|
||||||
}
|
|
||||||
case null, default -> {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, ClickCallback.Options.builder().lifetime(Duration.ofHours(1)).uses(1).build())
|
|
||||||
).build();
|
|
||||||
|
|
||||||
Dialog dialog = Dialog.create(builder -> builder.empty()
|
|
||||||
.base(
|
|
||||||
DialogBase.builder(Component.text("Armor Stand Conversion"))
|
|
||||||
.body(List.of(
|
|
||||||
DialogBody.plainMessage(
|
|
||||||
Component.text("Some settings might not persist between changing entity type").color(NamedTextColor.RED)
|
|
||||||
)
|
|
||||||
)).inputs(inputs)
|
|
||||||
.build())
|
|
||||||
.type(DialogType.confirmation(saveButton, ActionButton.builder(Component.text("Cancel")).build()))
|
|
||||||
);
|
|
||||||
|
|
||||||
player.showDialog(dialog);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void openConversionDialog(@NotNull Player player, @NotNull Mannequin mannequin) {
|
|
||||||
if (mannequin.isDead()) return;
|
|
||||||
|
|
||||||
UUID MannequinId = mannequin.getUniqueId();
|
|
||||||
UUID playerId = player.getUniqueId();
|
|
||||||
|
|
||||||
List<DialogInput> inputs = List.of(
|
|
||||||
DialogInput.singleOption("entity_type", Component.text("New entity type"), List.of(
|
|
||||||
SingleOptionDialogInput.OptionEntry.create("mannequin", Component.text("Mannequin"), true),
|
|
||||||
SingleOptionDialogInput.OptionEntry.create("armor_stand", Component.text("Armor Stand"), false)
|
|
||||||
)).build()
|
|
||||||
);
|
|
||||||
|
|
||||||
ActionButton saveButton = ActionButton.builder(Component.text("Change type")).action(
|
|
||||||
DialogAction.customClick((view, audience) -> {
|
|
||||||
if (!(audience instanceof Player callbackPlayer)) return;
|
|
||||||
if (playerId != callbackPlayer.getUniqueId()) return;
|
|
||||||
Entity entity = callbackPlayer.getWorld().getEntity(MannequinId);
|
|
||||||
if (!(entity instanceof Mannequin cMannequin)) return;
|
|
||||||
if (cMannequin.isDead()) return;
|
|
||||||
String newEntityType = view.getText("entity_type");
|
|
||||||
switch (newEntityType) {
|
|
||||||
case "armor_stand" -> {
|
|
||||||
EntityConverter<Mannequin, ArmorStand> armorStandConverter = new MannequinToArmorStandConverter();
|
|
||||||
ArmorStand armorStand = armorStandConverter.spawn(cMannequin);
|
|
||||||
if (armorStand == null) {
|
|
||||||
audience.sendMessage(Component.text("Error spawning new entity. Ensure new entity can spawn in this spot.").color(NamedTextColor.RED));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
cMannequin.remove();
|
|
||||||
}
|
|
||||||
case null, default -> {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, ClickCallback.Options.builder().lifetime(Duration.ofMinutes(5)).uses(1).build())
|
|
||||||
).build();
|
|
||||||
|
|
||||||
Dialog dialog = Dialog.create(builder -> builder.empty()
|
|
||||||
.base(
|
|
||||||
DialogBase.builder(Component.text("Mannequin Conversion"))
|
|
||||||
.body(List.of(
|
|
||||||
DialogBody.plainMessage(
|
|
||||||
Component.text("Some settings might not persist between changing entity type").color(NamedTextColor.RED)
|
|
||||||
)
|
|
||||||
)).inputs(inputs)
|
|
||||||
.build())
|
|
||||||
.type(DialogType.confirmation(saveButton, ActionButton.builder(Component.text("Cancel")).build()))
|
|
||||||
);
|
|
||||||
|
|
||||||
player.showDialog(dialog);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void openArmorStandDialog(@NotNull Player player, @NotNull ArmorStand armorStand) {
|
|
||||||
if (armorStand.isDead()) return;
|
|
||||||
|
|
||||||
UUID armorStandId = armorStand.getUniqueId();
|
|
||||||
UUID playerId = player.getUniqueId();
|
|
||||||
|
|
||||||
List<DialogInput> inputs = new ArrayList<>();
|
|
||||||
|
|
||||||
inputs.add(
|
|
||||||
DialogInput.bool("invisible", Component.text("Invisible"))
|
|
||||||
.initial(armorStand.isInvisible())
|
|
||||||
.build()
|
|
||||||
);
|
|
||||||
|
|
||||||
inputs.add(
|
|
||||||
DialogInput.bool("basePlate", Component.text("Base plate"))
|
|
||||||
.initial(armorStand.hasBasePlate())
|
|
||||||
.build()
|
|
||||||
);
|
|
||||||
|
|
||||||
inputs.add(
|
|
||||||
DialogInput.bool("arms", Component.text("Arms"))
|
|
||||||
.initial(armorStand.hasArms())
|
|
||||||
.build()
|
|
||||||
);
|
|
||||||
|
|
||||||
inputs.add(
|
|
||||||
DialogInput.bool("small", Component.text("Small"))
|
|
||||||
.initial(armorStand.isSmall())
|
|
||||||
.build()
|
|
||||||
);
|
|
||||||
|
|
||||||
ActionButton saveButton = ActionButton.builder(Component.text("Save")).action(
|
|
||||||
DialogAction.customClick((view, audience) -> {
|
|
||||||
if (!(audience instanceof Player callbackPlayer)) return;
|
|
||||||
if (playerId != callbackPlayer.getUniqueId()) return;
|
|
||||||
Entity entity = callbackPlayer.getWorld().getEntity(armorStandId);
|
|
||||||
if (!(entity instanceof ArmorStand stand)) return;
|
|
||||||
if (stand.isDead()) return;
|
|
||||||
stand.setInvisible(Boolean.TRUE.equals(view.getBoolean("invisible")));
|
|
||||||
stand.setBasePlate(Boolean.TRUE.equals(view.getBoolean("basePlate")));
|
|
||||||
stand.setArms(Boolean.TRUE.equals(view.getBoolean("arms")));
|
|
||||||
stand.setSmall(Boolean.TRUE.equals(view.getBoolean("small")));
|
|
||||||
}, ClickCallback.Options.builder().lifetime(Duration.ofMinutes(5)).uses(1).build())
|
|
||||||
).build();
|
|
||||||
|
|
||||||
ActionButton changeTypeButton = ActionButton.builder(Component.text("Change type"))
|
|
||||||
.action(
|
|
||||||
DialogAction.customClick((view, audience) -> {
|
|
||||||
if (!(audience instanceof Player callbackPlayer)) return;
|
|
||||||
if (playerId != callbackPlayer.getUniqueId()) return;
|
|
||||||
Entity entity = callbackPlayer.getWorld().getEntity(armorStandId);
|
|
||||||
if (!(entity instanceof ArmorStand stand)) return;
|
|
||||||
if (stand.isDead()) return;
|
|
||||||
StandinDialog.openConversionDialog(callbackPlayer, armorStand);
|
|
||||||
}, ClickCallback.Options.builder().lifetime(Duration.ofMinutes(5)).uses(1).build()
|
|
||||||
)
|
|
||||||
).build();
|
|
||||||
|
|
||||||
Dialog dialog = Dialog.create(builder -> builder.empty()
|
|
||||||
.base(
|
|
||||||
DialogBase.builder(Component.text("Armor Stand Editor"))
|
|
||||||
.inputs(inputs)
|
|
||||||
.build()
|
|
||||||
).type(
|
|
||||||
DialogType.multiAction(
|
|
||||||
List.of(saveButton, changeTypeButton),
|
|
||||||
ActionButton.builder(Component.text("Cancel")).build(),
|
|
||||||
1)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
player.showDialog(dialog);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void openMannequinDialog(@NotNull Player player, @NotNull Mannequin mannequin) {
|
|
||||||
if (mannequin.isDead()) return;
|
|
||||||
|
|
||||||
UUID mannequinId = mannequin.getUniqueId();
|
|
||||||
UUID playerId = player.getUniqueId();
|
|
||||||
List<DialogInput> inputs = new ArrayList<>();
|
|
||||||
|
|
||||||
String name = "";
|
|
||||||
Component customName = mannequin.customName();
|
|
||||||
if (customName != null) {
|
|
||||||
name = PlainTextComponentSerializer.plainText().serialize(customName);
|
|
||||||
}
|
|
||||||
|
|
||||||
inputs.add(
|
|
||||||
DialogInput.text("name", Component.text("Name"))
|
|
||||||
.initial(name)
|
|
||||||
.build()
|
|
||||||
);
|
|
||||||
|
|
||||||
ActionButton saveButton = ActionButton.builder(Component.text("Save")).action(
|
|
||||||
DialogAction.customClick((view, audience) -> {
|
|
||||||
if (!(audience instanceof Player callbackPlayer)) return;
|
|
||||||
if (playerId != callbackPlayer.getUniqueId()) return;
|
|
||||||
Entity entity = callbackPlayer.getWorld().getEntity(mannequinId);
|
|
||||||
if (!(entity instanceof Mannequin cMannequin)) return;
|
|
||||||
if (cMannequin.isDead()) return;
|
|
||||||
String newName = view.getText("name");
|
|
||||||
Component displayName = null;
|
|
||||||
if (newName != null) {
|
|
||||||
displayName = MiniMessage.miniMessage().deserialize(newName);
|
|
||||||
}
|
|
||||||
cMannequin.customName(displayName);
|
|
||||||
|
|
||||||
}, ClickCallback.Options.builder().lifetime(Duration.ofMinutes(5)).uses(1).build())
|
|
||||||
).build();
|
|
||||||
|
|
||||||
ActionButton changeTypeButton = ActionButton.builder(Component.text("Change type"))
|
|
||||||
.action(
|
|
||||||
DialogAction.customClick((view, audience) -> {
|
|
||||||
if (!(audience instanceof Player callbackPlayer)) return;
|
|
||||||
if (playerId != callbackPlayer.getUniqueId()) return;
|
|
||||||
Entity entity = callbackPlayer.getWorld().getEntity(mannequinId);
|
|
||||||
if (!(entity instanceof Mannequin cMannequin)) return;
|
|
||||||
if (cMannequin.isDead()) return;
|
|
||||||
StandinDialog.openConversionDialog(callbackPlayer, cMannequin);
|
|
||||||
}, ClickCallback.Options.builder().lifetime(Duration.ofMinutes(5)).uses(1).build()
|
|
||||||
)
|
|
||||||
).build();
|
|
||||||
|
|
||||||
Dialog dialog = Dialog.create(builder -> builder.empty()
|
|
||||||
.base(
|
|
||||||
DialogBase.builder(Component.text("Armor Stand Editor"))
|
|
||||||
.inputs(inputs)
|
|
||||||
.build()
|
|
||||||
).type(
|
|
||||||
DialogType.multiAction(
|
|
||||||
List.of(saveButton, changeTypeButton),
|
|
||||||
ActionButton.builder(Component.text("Cancel")).build(),
|
|
||||||
1)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
player.showDialog(dialog);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -2,6 +2,7 @@ package me.youhavetrouble.standin.converter;
|
|||||||
|
|
||||||
import me.youhavetrouble.standin.StandIn;
|
import me.youhavetrouble.standin.StandIn;
|
||||||
import org.bukkit.entity.ArmorStand;
|
import org.bukkit.entity.ArmorStand;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
import org.bukkit.entity.Mannequin;
|
import org.bukkit.entity.Mannequin;
|
||||||
import org.bukkit.inventory.EquipmentSlot;
|
import org.bukkit.inventory.EquipmentSlot;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@@ -18,6 +19,16 @@ public class ArmorStandToMannequinConverter implements EntityConverter<ArmorStan
|
|||||||
return Mannequin.class;
|
return Mannequin.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull EntityType entityFromType() {
|
||||||
|
return EntityType.ARMOR_STAND;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull EntityType entityToType() {
|
||||||
|
return EntityType.MANNEQUIN;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mannequin spawn(@NotNull ArmorStand from) {
|
public Mannequin spawn(@NotNull ArmorStand from) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
package me.youhavetrouble.standin.converter;
|
package me.youhavetrouble.standin.converter;
|
||||||
|
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public interface EntityConverter<F extends Entity, T extends Entity> {
|
public interface EntityConverter<F extends Entity, T extends Entity> {
|
||||||
|
|
||||||
Class<F> entityFrom();
|
Class<F> entityFrom();
|
||||||
|
|
||||||
|
@NotNull EntityType entityFromType();
|
||||||
|
|
||||||
|
@NotNull EntityType entityToType();
|
||||||
|
|
||||||
Class<T> entityTo();
|
Class<T> entityTo();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package me.youhavetrouble.standin.converter;
|
|||||||
|
|
||||||
import me.youhavetrouble.standin.StandIn;
|
import me.youhavetrouble.standin.StandIn;
|
||||||
import org.bukkit.entity.ArmorStand;
|
import org.bukkit.entity.ArmorStand;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
import org.bukkit.entity.Mannequin;
|
import org.bukkit.entity.Mannequin;
|
||||||
import org.bukkit.inventory.EquipmentSlot;
|
import org.bukkit.inventory.EquipmentSlot;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@@ -18,6 +19,16 @@ public class MannequinToArmorStandConverter implements EntityConverter<Mannequin
|
|||||||
return ArmorStand.class;
|
return ArmorStand.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull EntityType entityFromType() {
|
||||||
|
return EntityType.MANNEQUIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull EntityType entityToType() {
|
||||||
|
return EntityType.ARMOR_STAND;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ArmorStand spawn(@NotNull Mannequin from) {
|
public ArmorStand spawn(@NotNull Mannequin from) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
package me.youhavetrouble.standin.entity;
|
||||||
|
|
||||||
|
import io.papermc.paper.dialog.Dialog;
|
||||||
|
import io.papermc.paper.registry.data.dialog.ActionButton;
|
||||||
|
import io.papermc.paper.registry.data.dialog.DialogBase;
|
||||||
|
import io.papermc.paper.registry.data.dialog.action.DialogAction;
|
||||||
|
import io.papermc.paper.registry.data.dialog.input.DialogInput;
|
||||||
|
import io.papermc.paper.registry.data.dialog.type.DialogType;
|
||||||
|
import me.youhavetrouble.standin.converter.ArmorStandToMannequinConverter;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.event.ClickCallback;
|
||||||
|
import org.bukkit.entity.ArmorStand;
|
||||||
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@SuppressWarnings("UnstableApiUsage")
|
||||||
|
public class ArmorStandHandler extends EntityHandler<ArmorStand> {
|
||||||
|
|
||||||
|
public ArmorStandHandler() {
|
||||||
|
super(ArmorStand.class);
|
||||||
|
addConverter(new ArmorStandToMannequinConverter());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dialog editDialog(@NotNull Player player, ArmorStand armorStand) {
|
||||||
|
if (armorStand == null || armorStand.isDead()) return null;
|
||||||
|
if (!canUseAction(player, armorStand, EntityAction.EDIT)) return null;
|
||||||
|
|
||||||
|
UUID entityId = armorStand.getUniqueId();
|
||||||
|
UUID playerId = player.getUniqueId();
|
||||||
|
|
||||||
|
List<DialogInput> inputs = new ArrayList<>();
|
||||||
|
|
||||||
|
inputs.add(
|
||||||
|
DialogInput.bool("invisible", Component.text("Invisible"))
|
||||||
|
.initial(armorStand.isInvisible())
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
inputs.add(
|
||||||
|
DialogInput.bool("canMove", Component.text("Can move"))
|
||||||
|
.initial(armorStand.canMove())
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
inputs.add(
|
||||||
|
DialogInput.bool("gravity", Component.text("Gravity"))
|
||||||
|
.initial(armorStand.hasGravity())
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
inputs.add(
|
||||||
|
DialogInput.bool("basePlate", Component.text("Base plate"))
|
||||||
|
.initial(armorStand.hasBasePlate())
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
inputs.add(
|
||||||
|
DialogInput.bool("arms", Component.text("Arms"))
|
||||||
|
.initial(armorStand.hasArms())
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
inputs.add(
|
||||||
|
DialogInput.bool("small", Component.text("Small"))
|
||||||
|
.initial(armorStand.isSmall())
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
|
||||||
|
ActionButton saveButton = ActionButton.builder(Component.text("Save")).action(
|
||||||
|
DialogAction.customClick((view, audience) -> {
|
||||||
|
if (!(audience instanceof Player callbackPlayer)) return;
|
||||||
|
if (playerId != callbackPlayer.getUniqueId()) return;
|
||||||
|
Entity entity = callbackPlayer.getWorld().getEntity(entityId);
|
||||||
|
if (!(entity instanceof ArmorStand stand)) return;
|
||||||
|
if (!canUseAction(callbackPlayer, stand, EntityAction.EDIT)) return;
|
||||||
|
if (stand.isDead()) return;
|
||||||
|
stand.setInvisible(Boolean.TRUE.equals(view.getBoolean("invisible")));
|
||||||
|
stand.setBasePlate(Boolean.TRUE.equals(view.getBoolean("basePlate")));
|
||||||
|
stand.setArms(Boolean.TRUE.equals(view.getBoolean("arms")));
|
||||||
|
stand.setSmall(Boolean.TRUE.equals(view.getBoolean("small")));
|
||||||
|
stand.setCanMove(Boolean.TRUE.equals(view.getBoolean("canMove")));
|
||||||
|
stand.setGravity(Boolean.TRUE.equals(view.getBoolean("gravity")));
|
||||||
|
}, ClickCallback.Options.builder().lifetime(Duration.ofMinutes(5)).uses(1).build())
|
||||||
|
).build();
|
||||||
|
|
||||||
|
List<ActionButton> actions = new ArrayList<>();
|
||||||
|
if (player.hasPermission("standin.change_type.armor_stand")) {
|
||||||
|
ActionButton changeTypeButton = ActionButton.builder(Component.text("Change type"))
|
||||||
|
.action(
|
||||||
|
DialogAction.customClick((view, audience) -> {
|
||||||
|
if (!(audience instanceof Player callbackPlayer)) return;
|
||||||
|
if (playerId != callbackPlayer.getUniqueId()) return;
|
||||||
|
Entity entity = callbackPlayer.getWorld().getEntity(entityId);
|
||||||
|
if (!(entity instanceof ArmorStand stand)) return;
|
||||||
|
Dialog dialog = conversionDialog(callbackPlayer, stand);
|
||||||
|
if (dialog == null) return;
|
||||||
|
callbackPlayer.showDialog(dialog);
|
||||||
|
}, ClickCallback.Options.builder().lifetime(Duration.ofMinutes(5)).uses(1).build()
|
||||||
|
)
|
||||||
|
).build();
|
||||||
|
actions.add(changeTypeButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
actions.add(saveButton);
|
||||||
|
|
||||||
|
return Dialog.create(builder -> builder.empty()
|
||||||
|
.base(
|
||||||
|
DialogBase.builder(Component.text("Armor Stand Editor"))
|
||||||
|
.inputs(inputs)
|
||||||
|
.build()
|
||||||
|
).type(
|
||||||
|
DialogType.multiAction(
|
||||||
|
actions,
|
||||||
|
ActionButton.builder(Component.text("Cancel")).build(),
|
||||||
|
1)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -23,8 +23,13 @@ import java.util.*;
|
|||||||
@SuppressWarnings("UnstableApiUsage")
|
@SuppressWarnings("UnstableApiUsage")
|
||||||
public abstract class EntityHandler<E extends Entity> {
|
public abstract class EntityHandler<E extends Entity> {
|
||||||
|
|
||||||
|
public final Class<E> clazz;
|
||||||
private final Set<EntityConverter<E, ?>> possibleConverters = new HashSet<>();
|
private final Set<EntityConverter<E, ?>> possibleConverters = new HashSet<>();
|
||||||
|
|
||||||
|
public EntityHandler(Class<E> clazz) {
|
||||||
|
this.clazz = clazz;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the classes of entities this entity can convert into. Conversion can be lossy.
|
* Get the classes of entities this entity can convert into. Conversion can be lossy.
|
||||||
*
|
*
|
||||||
@@ -56,16 +61,28 @@ public abstract class EntityHandler<E extends Entity> {
|
|||||||
public final @Nullable Dialog conversionDialog(@NotNull Player player, E entity) {
|
public final @Nullable Dialog conversionDialog(@NotNull Player player, E entity) {
|
||||||
if (entity.isDead()) return null;
|
if (entity.isDead()) return null;
|
||||||
if (possibleConverters.isEmpty()) return null;
|
if (possibleConverters.isEmpty()) return null;
|
||||||
|
if (!canUseAction(player, entity, EntityAction.CHANGE_TYPE)) return null;
|
||||||
UUID entityId = entity.getUniqueId();
|
UUID entityId = entity.getUniqueId();
|
||||||
UUID playerId = player.getUniqueId();
|
UUID playerId = player.getUniqueId();
|
||||||
Class<? extends Entity> entityClass = entity.getClass();
|
Class<? extends Entity> entityClass = entity.getClass();
|
||||||
|
|
||||||
List<SingleOptionDialogInput.OptionEntry> entityEntries = new ArrayList<>();
|
List<SingleOptionDialogInput.OptionEntry> entityEntries = new ArrayList<>();
|
||||||
|
|
||||||
entityEntries.add(SingleOptionDialogInput.OptionEntry.create(entityClass.getName(), Component.text(entityClass.getName()), true));
|
entityEntries.add(
|
||||||
|
SingleOptionDialogInput.OptionEntry.create(
|
||||||
|
entity.getType().toString(),
|
||||||
|
Component.translatable(entity.getType().translationKey(), entity.getType().toString()),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
);
|
||||||
for (EntityConverter<?, ?> converter : possibleConverters) {
|
for (EntityConverter<?, ?> converter : possibleConverters) {
|
||||||
entityEntries.add(SingleOptionDialogInput.OptionEntry.create(converter.entityTo().getName(), Component.text(converter.entityTo().getName()), false));
|
entityEntries.add(
|
||||||
|
SingleOptionDialogInput.OptionEntry.create(
|
||||||
|
converter.entityTo().getName(),
|
||||||
|
Component.translatable(converter.entityToType().translationKey()),
|
||||||
|
false
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<DialogInput> inputs = List.of(
|
List<DialogInput> inputs = List.of(
|
||||||
@@ -84,6 +101,7 @@ public abstract class EntityHandler<E extends Entity> {
|
|||||||
if (callbackEntity == null || callbackEntity.isDead()) return;
|
if (callbackEntity == null || callbackEntity.isDead()) return;
|
||||||
if (!callbackEntity.getClass().equals(entityClass)) return;
|
if (!callbackEntity.getClass().equals(entityClass)) return;
|
||||||
E existing = (E) callbackEntity;
|
E existing = (E) callbackEntity;
|
||||||
|
if (!canUseAction(callbackPlayer, existing, EntityAction.CHANGE_TYPE)) return;
|
||||||
String newEntityType = view.getText("entity_type");
|
String newEntityType = view.getText("entity_type");
|
||||||
if (newEntityType == null) return;
|
if (newEntityType == null) return;
|
||||||
if (newEntityType.equals(existing.getClass().getName())) return; // skip if the class is the same
|
if (newEntityType.equals(existing.getClass().getName())) return; // skip if the class is the same
|
||||||
@@ -113,6 +131,23 @@ public abstract class EntityHandler<E extends Entity> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if player can execute given action for the entity. This is checked on opening dialogs and on executing
|
||||||
|
* callbacks.
|
||||||
|
* @param player player executing action
|
||||||
|
* @param entity entity action is supposed to be used on
|
||||||
|
* @param action action to b taken
|
||||||
|
* @return Boolean representing the allowance state of the action
|
||||||
|
*/
|
||||||
|
public boolean canUseAction(@NotNull Player player, E entity, EntityAction action) {
|
||||||
|
String entityTypeName = entity.getType().toString().toLowerCase(Locale.ENGLISH);
|
||||||
|
return switch (action) {
|
||||||
|
case EDIT -> player.hasPermission("standin.edit." + entityTypeName);
|
||||||
|
case CHANGE_TYPE -> player.hasPermission("standin.change_type." + entityTypeName);
|
||||||
|
default -> false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open a dialog allowing to edit properties of the entity
|
* Open a dialog allowing to edit properties of the entity
|
||||||
*
|
*
|
||||||
@@ -124,4 +159,9 @@ public abstract class EntityHandler<E extends Entity> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum EntityAction {
|
||||||
|
EDIT,
|
||||||
|
CHANGE_TYPE,
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
package me.youhavetrouble.standin.entity;
|
||||||
|
|
||||||
|
import io.papermc.paper.dialog.Dialog;
|
||||||
|
import io.papermc.paper.registry.data.dialog.ActionButton;
|
||||||
|
import io.papermc.paper.registry.data.dialog.DialogBase;
|
||||||
|
import io.papermc.paper.registry.data.dialog.action.DialogAction;
|
||||||
|
import io.papermc.paper.registry.data.dialog.input.DialogInput;
|
||||||
|
import io.papermc.paper.registry.data.dialog.type.DialogType;
|
||||||
|
import me.youhavetrouble.standin.converter.MannequinToArmorStandConverter;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.event.ClickCallback;
|
||||||
|
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||||
|
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||||
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.bukkit.entity.Mannequin;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@SuppressWarnings("UnstableApiUsage")
|
||||||
|
public class MannequinHandler extends EntityHandler<Mannequin> {
|
||||||
|
|
||||||
|
public MannequinHandler() {
|
||||||
|
super(Mannequin.class);
|
||||||
|
addConverter(new MannequinToArmorStandConverter());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dialog editDialog(@NotNull Player player, Mannequin mannequin) {
|
||||||
|
if (mannequin == null || mannequin.isDead()) return null;
|
||||||
|
if (!canUseAction(player, mannequin, EntityAction.EDIT)) return null;
|
||||||
|
|
||||||
|
UUID entityId = mannequin.getUniqueId();
|
||||||
|
UUID playerId = player.getUniqueId();
|
||||||
|
|
||||||
|
List<DialogInput> inputs = new ArrayList<>();
|
||||||
|
|
||||||
|
String name = "";
|
||||||
|
Component customName = mannequin.customName();
|
||||||
|
if (customName != null) {
|
||||||
|
name = PlainTextComponentSerializer.plainText().serialize(customName);
|
||||||
|
}
|
||||||
|
|
||||||
|
inputs.add(
|
||||||
|
DialogInput.text("name", Component.text("Name"))
|
||||||
|
.initial(name)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
|
||||||
|
List<ActionButton> actions = new ArrayList<>();
|
||||||
|
|
||||||
|
ActionButton saveButton = ActionButton.builder(Component.text("Save")).action(
|
||||||
|
DialogAction.customClick((view, audience) -> {
|
||||||
|
if (!(audience instanceof Player callbackPlayer)) return;
|
||||||
|
if (playerId != callbackPlayer.getUniqueId()) return;
|
||||||
|
Entity entity = callbackPlayer.getWorld().getEntity(entityId);
|
||||||
|
if (!(entity instanceof Mannequin mann)) return;
|
||||||
|
if (!canUseAction(callbackPlayer, mann, EntityAction.EDIT)) return;
|
||||||
|
if (mann.isDead()) return;
|
||||||
|
String newName = view.getText("name");
|
||||||
|
Component displayName = null;
|
||||||
|
if (newName != null) {
|
||||||
|
displayName = MiniMessage.miniMessage().deserialize(newName);
|
||||||
|
}
|
||||||
|
mann.customName(displayName);
|
||||||
|
}, ClickCallback.Options.builder().lifetime(Duration.ofMinutes(5)).uses(1).build())
|
||||||
|
).build();
|
||||||
|
actions.add(saveButton);
|
||||||
|
|
||||||
|
if (player.hasPermission("standin.change_type.armor_stand")) {
|
||||||
|
ActionButton changeTypeButton = ActionButton.builder(Component.text("Change type"))
|
||||||
|
.action(
|
||||||
|
DialogAction.customClick((view, audience) -> {
|
||||||
|
if (!(audience instanceof Player callbackPlayer)) return;
|
||||||
|
if (playerId != callbackPlayer.getUniqueId()) return;
|
||||||
|
Entity entity = callbackPlayer.getWorld().getEntity(entityId);
|
||||||
|
if (!(entity instanceof Mannequin mann)) return;
|
||||||
|
Dialog dialog = conversionDialog(callbackPlayer, mann);
|
||||||
|
if (dialog == null) return;
|
||||||
|
callbackPlayer.showDialog(dialog);
|
||||||
|
}, ClickCallback.Options.builder().lifetime(Duration.ofMinutes(5)).uses(1).build()
|
||||||
|
)
|
||||||
|
).build();
|
||||||
|
actions.add(changeTypeButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Dialog.create(builder -> builder.empty()
|
||||||
|
.base(
|
||||||
|
DialogBase.builder(Component.text("Armor Stand Editor"))
|
||||||
|
.inputs(inputs)
|
||||||
|
.build()
|
||||||
|
).type(
|
||||||
|
DialogType.multiAction(
|
||||||
|
actions,
|
||||||
|
ActionButton.builder(Component.text("Cancel")).build(),
|
||||||
|
1)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,102 +1,50 @@
|
|||||||
package me.youhavetrouble.standin.stand;
|
package me.youhavetrouble.standin.stand;
|
||||||
|
|
||||||
|
import io.papermc.paper.dialog.Dialog;
|
||||||
import io.papermc.paper.event.player.PlayerPickEntityEvent;
|
import io.papermc.paper.event.player.PlayerPickEntityEvent;
|
||||||
import me.youhavetrouble.standin.StandinDialog;
|
import me.youhavetrouble.standin.StandIn;
|
||||||
import org.bukkit.attribute.Attribute;
|
import me.youhavetrouble.standin.entity.EntityHandler;
|
||||||
import org.bukkit.attribute.AttributeInstance;
|
|
||||||
import org.bukkit.entity.ArmorStand;
|
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
import org.bukkit.entity.Mannequin;
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.EventPriority;
|
import org.bukkit.event.EventPriority;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.event.player.PlayerInteractAtEntityEvent;
|
import org.bukkit.event.player.PlayerInteractAtEntityEvent;
|
||||||
import org.bukkit.event.player.PlayerInteractEvent;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.bukkit.inventory.EquipmentSlot;
|
|
||||||
import org.bukkit.util.RayTraceResult;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
public class StandinInteractionListener implements Listener {
|
public class StandinInteractionListener implements Listener {
|
||||||
|
|
||||||
private boolean handleInteraction(@NotNull Player player, Entity entity) {
|
private <E extends Entity> @Nullable Dialog invokeEditDialog(EntityHandler<E> handler, Player player, Entity clicked) {
|
||||||
if (entity == null) return false;
|
return handler.editDialog(player, handler.clazz.cast(clicked));
|
||||||
if (!player.isOnline()) return false;
|
|
||||||
|
|
||||||
if (entity instanceof ArmorStand armorStand && player.hasPermission("standin.edit.armor_stand")) {
|
|
||||||
StandinDialog.openArmorStandDialog(player, armorStand);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entity instanceof Mannequin mannequin && player.hasPermission("standin.edit.mannequin")) {
|
|
||||||
StandinDialog.openMannequinDialog(player, mannequin);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private <E extends Entity> @Nullable Dialog invokeConvertDialog(EntityHandler<E> handler, Player player, Entity clicked) {
|
||||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = false)
|
return handler.conversionDialog(player, handler.clazz.cast(clicked));
|
||||||
public void onInteractWithDisplays(PlayerInteractEvent event) {
|
|
||||||
if (!event.getPlayer().isSneaking()) return;
|
|
||||||
if (event.getHand() == EquipmentSlot.OFF_HAND) return;
|
|
||||||
Player player = event.getPlayer();
|
|
||||||
|
|
||||||
AttributeInstance instance = player.getAttribute(Attribute.ENTITY_INTERACTION_RANGE);
|
|
||||||
if (instance == null) return;
|
|
||||||
|
|
||||||
double interactionRange = instance.getValue();
|
|
||||||
|
|
||||||
RayTraceResult result = player.getWorld().rayTraceEntities(
|
|
||||||
player.getEyeLocation(),
|
|
||||||
player.getEyeLocation().getDirection(),
|
|
||||||
interactionRange,
|
|
||||||
0.5,
|
|
||||||
(entity -> {
|
|
||||||
switch (entity.getType()) {
|
|
||||||
case TEXT_DISPLAY,
|
|
||||||
BLOCK_DISPLAY,
|
|
||||||
ITEM_DISPLAY -> {
|
|
||||||
if (entity.getVehicle() == null) return true;
|
|
||||||
return !player.getUniqueId().equals(entity.getVehicle().getUniqueId());
|
|
||||||
}
|
|
||||||
default -> {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
if (result == null) return;
|
|
||||||
|
|
||||||
Entity entity = result.getHitEntity();
|
|
||||||
if (!handleInteraction(player, entity)) return;
|
|
||||||
event.setCancelled(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||||
public void onInteractWithStands(PlayerInteractAtEntityEvent event) {
|
public void onInteractWithStands(PlayerInteractAtEntityEvent event) {
|
||||||
if (!event.getPlayer().isSneaking()) return;
|
if (!event.getPlayer().isSneaking()) return;
|
||||||
if (!handleInteraction(event.getPlayer(), event.getRightClicked())) return;
|
EntityHandler<?> handler = StandIn.getPlugin(StandIn.class).getEntityHandler(event.getRightClicked().getType());
|
||||||
|
if (handler == null) return;
|
||||||
|
Dialog dialog = invokeEditDialog(handler, event.getPlayer(), event.getRightClicked());
|
||||||
|
if (dialog == null) return;
|
||||||
|
event.getPlayer().showDialog(dialog);
|
||||||
event.setCancelled(true);
|
event.setCancelled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <a href="https://github.com/PaperMC/Paper/issues/13340">This currently does not work for mannequins since pick entity does not fire for them</a>
|
||||||
|
*/
|
||||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||||
public void onInteractWithStands(PlayerPickEntityEvent event) {
|
public void onInteractWithStands(PlayerPickEntityEvent event) {
|
||||||
if (!event.getPlayer().isSneaking()) return;
|
if (!event.getPlayer().isSneaking()) return;
|
||||||
if (event.getEntity() instanceof ArmorStand armorStand) {
|
EntityHandler<?> handler = StandIn.getPlugin(StandIn.class).getEntityHandler(event.getEntity().getType());
|
||||||
StandinDialog.openConversionDialog(event.getPlayer(), armorStand);
|
if (handler == null) return;
|
||||||
event.setCancelled(true);
|
Dialog dialog = invokeConvertDialog(handler, event.getPlayer(), event.getEntity());
|
||||||
return;
|
if (dialog == null) return;
|
||||||
}
|
event.getPlayer().showDialog(dialog);
|
||||||
|
event.setCancelled(true);
|
||||||
// This currently does not work since pick entity does not fire for mannequins
|
|
||||||
// https://github.com/PaperMC/Paper/issues/13340
|
|
||||||
if (event.getEntity() instanceof Mannequin mannequin) {
|
|
||||||
StandinDialog.openConversionDialog(event.getPlayer(), mannequin);
|
|
||||||
event.setCancelled(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user