splittet project to craftattack and varo flavors
This commit is contained in:
28
craftattack/build.gradle
Normal file
28
craftattack/build.gradle
Normal file
@ -0,0 +1,28 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id("com.gradleup.shadow") version "8.3.5"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':core')
|
||||
implementation project(':common')
|
||||
|
||||
compileOnly 'io.papermc.paper:paper-api:1.21.1-R0.1-SNAPSHOT'
|
||||
compileOnly 'org.geysermc.floodgate:api:2.2.2-SNAPSHOT'
|
||||
implementation 'org.apache.httpcomponents:httpclient:4.5.14'
|
||||
implementation 'com.sparkjava:spark-core:2.9.4'
|
||||
}
|
||||
|
||||
configurations {
|
||||
shadowImplementation.extendsFrom implementation
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
configurations = [project.configurations.shadowImplementation]
|
||||
archiveClassifier.set('')
|
||||
|
||||
relocate 'org.apache.httpcomponents', 'eu.mhsl.lib.shadow.httpclient'
|
||||
relocate 'com.sparkjava', 'eu.mhsl.lib.shadow.spark-core'
|
||||
|
||||
mergeServiceFiles()
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.antiSignEdit;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.Appliance;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.datatypes.SelectSetting;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||
import org.bukkit.block.sign.SignSide;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class AntiSignEdit extends Appliance {
|
||||
@Override
|
||||
public void onEnable() {
|
||||
Settings.instance().declareSetting(SignEditSetting.class);
|
||||
}
|
||||
|
||||
public boolean preventSignEdit(Player p, SignSide sign) {
|
||||
SelectSetting.Options.Option setting = Settings.instance().getSetting(p, Settings.Key.SignEdit, SelectSetting.Options.Option.class);
|
||||
if(setting.is(SignEditSetting.editable)) return false;
|
||||
if(setting.is(SignEditSetting.readOnly)) {
|
||||
p.sendActionBar(Component.text("Das Bearbeiten von Schildern ist in deinen Einstellungen deaktiviert.", NamedTextColor.RED));
|
||||
return true;
|
||||
}
|
||||
|
||||
if(setting.is(SignEditSetting.editableWhenEmpty)) {
|
||||
boolean hasText = sign.lines().stream()
|
||||
.anyMatch(line -> !PlainTextComponentSerializer.plainText().serialize(line).isBlank());
|
||||
|
||||
if(hasText) {
|
||||
p.sendActionBar(Component.text("Das Bearbeiten von Schildern, welch bereits beschrieben sind, ist bei dir deaktiviert.", NamedTextColor.RED));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull List<Listener> listeners() {
|
||||
return List.of(new OnSignEditListener());
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.antiSignEdit;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceListener;
|
||||
import io.papermc.paper.event.player.PlayerOpenSignEvent;
|
||||
import org.bukkit.block.sign.SignSide;
|
||||
import org.bukkit.event.EventHandler;
|
||||
|
||||
class OnSignEditListener extends ApplianceListener<AntiSignEdit> {
|
||||
@EventHandler
|
||||
public void onEdit(PlayerOpenSignEvent event) {
|
||||
if(event.getCause().equals(PlayerOpenSignEvent.Cause.PLACE)) return;
|
||||
SignSide signSide = event.getSign().getSide(event.getSide());
|
||||
event.setCancelled(this.getAppliance().preventSignEdit(event.getPlayer(), signSide));
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.antiSignEdit;
|
||||
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.CategorizedSetting;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.SettingCategory;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.datatypes.SelectSetting;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.NamespacedKey;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class SignEditSetting extends SelectSetting implements CategorizedSetting {
|
||||
private static final String namespace = SignEditSetting.class.getSimpleName().toLowerCase(Locale.ROOT);
|
||||
public static Options.Option editable = new Options.Option("Bearbeitbar", new NamespacedKey(namespace, "editable"));
|
||||
public static Options.Option editableWhenEmpty = new Options.Option("Bearbeitbar wenn leer", new NamespacedKey(namespace, "emptyeditable"));
|
||||
public static Options.Option readOnly = new Options.Option("Nicht bearbeitbar", new NamespacedKey(namespace, "readonly"));
|
||||
|
||||
public SignEditSetting() {
|
||||
super(
|
||||
Settings.Key.SignEdit,
|
||||
new Options(List.of(editable, editableWhenEmpty, readOnly))
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String title() {
|
||||
return "Bearbeiten von Schildern";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String description() {
|
||||
return "Bei einem Klick auf ein Schild, kann dieses Bearbeitet werden";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Material icon() {
|
||||
return Material.OAK_SIGN;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Options.Option defaultValue() {
|
||||
return editableWhenEmpty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SettingCategory category() {
|
||||
return SettingCategory.Gameplay;
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.autoShulker;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.Appliance;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.ShulkerBox;
|
||||
import org.bukkit.entity.Item;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.BlockStateMeta;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class AutoShulker extends Appliance {
|
||||
@Override
|
||||
public void onEnable() {
|
||||
Settings.instance().declareSetting(AutoShulkerSetting.class);
|
||||
}
|
||||
|
||||
public boolean tryAutoShulker(Player p, Item item) {
|
||||
ItemStack itemStack = item.getItemStack();
|
||||
ItemStack offhandStack = p.getInventory().getItemInOffHand();
|
||||
if(itemStack.getType().equals(Material.SHULKER_BOX)) return false;
|
||||
if(!offhandStack.getType().equals(Material.SHULKER_BOX)) return false;
|
||||
BlockStateMeta blockStateMeta = (BlockStateMeta) offhandStack.getItemMeta();
|
||||
ShulkerBox shulkerBox = (ShulkerBox) blockStateMeta.getBlockState();
|
||||
|
||||
HashMap<Integer, ItemStack> leftOver = shulkerBox.getInventory().addItem(itemStack);
|
||||
if(leftOver.size() > 1)
|
||||
throw new IllegalStateException("Multiple ItemStacks cannot be processed by AutoShulker!");
|
||||
if(itemStack.equals(leftOver.get(0))) {
|
||||
p.sendActionBar(Component.text("Die Shulkerbox ist voll!", NamedTextColor.RED));
|
||||
return false;
|
||||
}
|
||||
if(leftOver.isEmpty()) {
|
||||
item.remove();
|
||||
}
|
||||
|
||||
blockStateMeta.setBlockState(shulkerBox);
|
||||
offhandStack.setItemMeta(blockStateMeta);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull List<Listener> listeners() {
|
||||
return List.of(new ItemPickupListener());
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.autoShulker;
|
||||
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.CategorizedSetting;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.SettingCategory;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.datatypes.SelectSetting;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.NamespacedKey;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class AutoShulkerSetting extends SelectSetting implements CategorizedSetting {
|
||||
private static final String namespace = AutoShulkerSetting.class.getSimpleName().toLowerCase(Locale.ROOT);
|
||||
public static Options.Option disabled = new Options.Option("Deaktiviert", new NamespacedKey(namespace, "disabled"));
|
||||
public static Options.Option notFromPlayers = new Options.Option("Keine manuell gedroppten Items", new NamespacedKey(namespace, "noplayerdrops"));
|
||||
public static Options.Option enabled = new Options.Option("Alle", new NamespacedKey(namespace, "enabled"));
|
||||
|
||||
public AutoShulkerSetting() {
|
||||
super(
|
||||
Settings.Key.AutoShulker,
|
||||
new Options(List.of(disabled, notFromPlayers, enabled))
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String title() {
|
||||
return "Shulker in offhand automatisch befüllen";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String description() {
|
||||
return "Wenn eine Shulker in der zweiten Hand gehalten wird, werden alle aufgesammelten Items in dieser abgelegt";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Material icon() {
|
||||
return Material.SHULKER_BOX;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Options.Option defaultValue() {
|
||||
return disabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SettingCategory category() {
|
||||
return SettingCategory.Gameplay;
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.autoShulker;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceListener;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.datatypes.SelectSetting;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.entity.EntityPickupItemEvent;
|
||||
|
||||
class ItemPickupListener extends ApplianceListener<AutoShulker> {
|
||||
@EventHandler
|
||||
public void onPickup(EntityPickupItemEvent event) {
|
||||
if(event.getEntity() instanceof Player p) {
|
||||
SelectSetting.Options.Option setting = Settings.instance().getSetting(p, Settings.Key.AutoShulker, SelectSetting.Options.Option.class);
|
||||
if(setting.is(AutoShulkerSetting.disabled)) return;
|
||||
if(setting.is(AutoShulkerSetting.notFromPlayers) && event.getItem().getThrower() != null) return;
|
||||
event.setCancelled(this.getAppliance().tryAutoShulker(p, event.getItem()));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.customAdvancements;
|
||||
|
||||
public class Advancements {
|
||||
public static String searchTrouble = "search_trouble";
|
||||
public static String fleischerchest = "fleischerchest";
|
||||
public static String craftPixelblock = "craft_pixelblock";
|
||||
public static String usePixelblock = "use_pixelblock";
|
||||
public static String start = "start";
|
||||
public static String winner = "winner";
|
||||
public static String participateEvent = "participate_event";
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.customAdvancements;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceListener;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
|
||||
class ApplyPendingAdvancementsListener extends ApplianceListener<CustomAdvancements> {
|
||||
@EventHandler
|
||||
public void onJoin(PlayerJoinEvent event) {
|
||||
this.getAppliance().applyPendingAdvancements(event.getPlayer());
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.customAdvancements;
|
||||
|
||||
import eu.mhsl.craftattack.core.Main;
|
||||
import eu.mhsl.craftattack.core.appliance.Appliance;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.advancement.Advancement;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
public class CustomAdvancements extends Appliance {
|
||||
record PendingAdvancement(UUID receiver, String advancement) {
|
||||
}
|
||||
|
||||
private final List<PendingAdvancement> pendingAdvancements = new ArrayList<>();
|
||||
|
||||
public void grantAdvancement(String advancementName, UUID playerUUID) {
|
||||
Player player = Bukkit.getPlayer(playerUUID);
|
||||
if(player == null) {
|
||||
this.addPendingAdvancement(playerUUID, advancementName);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
NamespacedKey namespacedKey = Objects.requireNonNull(
|
||||
NamespacedKey.fromString("craftattack_advancements:craftattack/" + advancementName),
|
||||
String.format("NamespacedKey with '%s' is invalid!", advancementName)
|
||||
);
|
||||
Advancement advancement = Objects.requireNonNull(
|
||||
Bukkit.getAdvancement(namespacedKey),
|
||||
String.format("The advancement '%s' does not exist!", namespacedKey.asString())
|
||||
);
|
||||
player.getAdvancementProgress(advancement).awardCriteria("criteria");
|
||||
} catch(Exception e) {
|
||||
Main.logger().info("Advancement " + advancementName + " not found! (is Custom Advancements data pack loaded?)");
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public void applyPendingAdvancements(Player player) {
|
||||
if(this.pendingAdvancements.isEmpty()) return;
|
||||
List<PendingAdvancement> grantedAdvancements = this.pendingAdvancements.stream()
|
||||
.filter(pendingAdvancement -> pendingAdvancement.receiver.equals(player.getUniqueId())).toList();
|
||||
this.pendingAdvancements.removeAll(grantedAdvancements);
|
||||
grantedAdvancements.forEach(pendingAdvancement -> this.grantAdvancement(pendingAdvancement.advancement(), player.getUniqueId()));
|
||||
}
|
||||
|
||||
private void addPendingAdvancement(UUID receiver, String advancement) {
|
||||
this.pendingAdvancements.add(new PendingAdvancement(receiver, advancement));
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
protected List<Listener> listeners() {
|
||||
return List.of(
|
||||
new CustomAdvancementsListener(),
|
||||
new ApplyPendingAdvancementsListener()
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.customAdvancements;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceListener;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
||||
import org.bukkit.event.inventory.CraftItemEvent;
|
||||
import org.bukkit.event.player.PlayerChangedWorldEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
class CustomAdvancementsListener extends ApplianceListener<CustomAdvancements> {
|
||||
@EventHandler
|
||||
public void onEntityDamageEntity(EntityDamageByEntityEvent event) {
|
||||
if(!(event.getEntity() instanceof Player damaged)) return;
|
||||
if(!(event.getDamager() instanceof Player damager)) return;
|
||||
if(!damager.getInventory().getItemInMainHand().getType().equals(Material.AIR)) return;
|
||||
if(!damaged.hasPermission("admin")) return;
|
||||
|
||||
this.getAppliance().grantAdvancement(Advancements.searchTrouble, damager.getUniqueId());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onCraftItem(CraftItemEvent event) {
|
||||
ItemStack result = event.getInventory().getResult();
|
||||
if(result == null) return;
|
||||
if(!(event.getView().getPlayer() instanceof Player player)) return;
|
||||
|
||||
if(result.getType() == Material.RED_SHULKER_BOX) {
|
||||
this.getAppliance().grantAdvancement(Advancements.fleischerchest, player.getUniqueId());
|
||||
return;
|
||||
}
|
||||
|
||||
if(result.getItemMeta().itemName().equals(Component.text("98fdf0ae-c3ab-4ef7-ae25-efd518d600de"))) {
|
||||
this.getAppliance().grantAdvancement(Advancements.craftPixelblock, player.getUniqueId());
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onChangeWorld(PlayerChangedWorldEvent event) {
|
||||
if(!event.getPlayer().getWorld().getName().startsWith("plugins/PixelBlocks/worlds")) return;
|
||||
|
||||
this.getAppliance().grantAdvancement(Advancements.usePixelblock, event.getPlayer().getUniqueId());
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.doubleDoor;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.Appliance;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.block.data.type.Door;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class DoubleDoor extends Appliance {
|
||||
@Override
|
||||
public void onEnable() {
|
||||
Settings.instance().declareSetting(DoubleDoorSetting.class);
|
||||
}
|
||||
|
||||
public void openNextDoor(Block block) {
|
||||
BlockData clickedData = block.getBlockData();
|
||||
if(!(clickedData instanceof Door clickedDoor)) return;
|
||||
|
||||
BlockFace neighborFace = this.getNeighborFace(clickedDoor.getFacing(), clickedDoor.getHinge());
|
||||
Block neighbourBlock = block.getRelative(neighborFace);
|
||||
BlockData neighbourData = neighbourBlock.getBlockData();
|
||||
|
||||
if(!(neighbourData instanceof Door neighbourDoor)) return;
|
||||
if(!(neighbourDoor.getFacing() == clickedDoor.getFacing())) return;
|
||||
if(neighbourDoor.getHinge() == clickedDoor.getHinge()) return;
|
||||
|
||||
neighbourDoor.setOpen(!clickedDoor.isOpen());
|
||||
neighbourBlock.setBlockData(neighbourDoor);
|
||||
}
|
||||
|
||||
private @NotNull BlockFace getNeighborFace(BlockFace face, Door.Hinge hinge) {
|
||||
return switch(face) {
|
||||
case EAST -> (hinge == Door.Hinge.RIGHT) ? BlockFace.NORTH : BlockFace.SOUTH;
|
||||
case WEST -> (hinge == Door.Hinge.RIGHT) ? BlockFace.SOUTH : BlockFace.NORTH;
|
||||
case SOUTH -> (hinge == Door.Hinge.RIGHT) ? BlockFace.EAST : BlockFace.WEST;
|
||||
case NORTH -> (hinge == Door.Hinge.RIGHT) ? BlockFace.WEST : BlockFace.EAST;
|
||||
default ->
|
||||
throw new IllegalStateException(String.format("BlockFace '%s' of clicked door is not valid!", face));
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull List<Listener> listeners() {
|
||||
return List.of(new OnDoorInteractListener());
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.doubleDoor;
|
||||
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.CategorizedSetting;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.SettingCategory;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.datatypes.BoolSetting;
|
||||
import org.bukkit.Material;
|
||||
|
||||
public class DoubleDoorSetting extends BoolSetting implements CategorizedSetting {
|
||||
public DoubleDoorSetting() {
|
||||
super(Settings.Key.DoubleDoors);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String title() {
|
||||
return "Automatische Doppeltüren";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String description() {
|
||||
return "Öffnet und schließt die zweite Hälfte einer Doppeltür automatisch";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Material icon() {
|
||||
return Material.OAK_DOOR;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean defaultValue() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SettingCategory category() {
|
||||
return SettingCategory.Gameplay;
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.doubleDoor;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceListener;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.inventory.EquipmentSlot;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
class OnDoorInteractListener extends ApplianceListener<DoubleDoor> {
|
||||
@EventHandler
|
||||
public void onPlayerInteract(PlayerInteractEvent event) {
|
||||
if(!event.hasBlock()) return;
|
||||
if(!Objects.equals(event.getHand(), EquipmentSlot.HAND)) return;
|
||||
if(!event.getAction().equals(Action.RIGHT_CLICK_BLOCK)) return;
|
||||
if(event.getPlayer().isSneaking()) return;
|
||||
Block clickedBlock = event.getClickedBlock();
|
||||
if(clickedBlock == null) return;
|
||||
if(clickedBlock.getType().equals(Material.IRON_DOOR)) return;
|
||||
if(!Settings.instance().getSetting(event.getPlayer(), Settings.Key.DoubleDoors, Boolean.class)) return;
|
||||
this.getAppliance().openNextDoor(clickedBlock);
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.fleischerchest;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.Appliance;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class Fleischerchest extends Appliance {
|
||||
public void renameItem(ItemStack item) {
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
meta.displayName(Component.text("Fleischerchest").color(TextColor.color(235, 20, 28)));
|
||||
item.setItemMeta(meta);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
protected List<Listener> listeners() {
|
||||
return List.of(new FleischerchestCraftItemListener());
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.fleischerchest;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceListener;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.inventory.PrepareItemCraftEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
class FleischerchestCraftItemListener extends ApplianceListener<Fleischerchest> {
|
||||
@EventHandler
|
||||
public void onPrepareItemCraft(PrepareItemCraftEvent event) {
|
||||
ItemStack result = event.getInventory().getResult();
|
||||
if(result == null) return;
|
||||
if(result.getType() != Material.RED_SHULKER_BOX) return;
|
||||
|
||||
this.getAppliance().renameItem(event.getInventory().getResult());
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.glowingBerries;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.Appliance;
|
||||
import net.kyori.adventure.sound.Sound;
|
||||
import net.kyori.adventure.util.Ticks;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.potion.PotionEffect;
|
||||
import org.bukkit.potion.PotionEffectType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class GlowingBerries extends Appliance {
|
||||
private static final PotionEffect glowEffect = new PotionEffect(
|
||||
PotionEffectType.GLOWING,
|
||||
Ticks.TICKS_PER_SECOND * 15,
|
||||
1,
|
||||
false,
|
||||
true,
|
||||
true
|
||||
);
|
||||
|
||||
public void letPlayerGlow(Player player) {
|
||||
player.addPotionEffect(glowEffect);
|
||||
Sound sound = Sound.sound(org.bukkit.Sound.BLOCK_AMETHYST_BLOCK_CHIME.key(), Sound.Source.PLAYER, 1f, 1f);
|
||||
player.stopSound(sound);
|
||||
player.playSound(sound, Sound.Emitter.self());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull List<Listener> listeners() {
|
||||
return List.of(new OnBerryEaten());
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.glowingBerries;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceListener;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.player.PlayerItemConsumeEvent;
|
||||
|
||||
class OnBerryEaten extends ApplianceListener<GlowingBerries> {
|
||||
@EventHandler
|
||||
public void onEat(PlayerItemConsumeEvent event) {
|
||||
if(event.getItem().getType().equals(Material.GLOW_BERRIES))
|
||||
this.getAppliance().letPlayerGlow(event.getPlayer());
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.hotbarRefill;
|
||||
|
||||
import eu.mhsl.craftattack.core.Main;
|
||||
import eu.mhsl.craftattack.core.appliance.Appliance;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
public class HotbarRefill extends Appliance {
|
||||
@Override
|
||||
public void onEnable() {
|
||||
Settings.instance().declareSetting(HotbarRefillSetting.class);
|
||||
}
|
||||
|
||||
public void handleHotbarChange(Player player, ItemStack item) {
|
||||
if(player.getGameMode().equals(GameMode.CREATIVE)) return;
|
||||
if(item.getAmount() != 1) return;
|
||||
Inventory inventory = player.getInventory();
|
||||
|
||||
int itemSlot = inventory.first(item);
|
||||
if(itemSlot > 8) return;
|
||||
|
||||
try {
|
||||
int replacementSlot = inventory.all(item.getType()).entrySet().stream()
|
||||
.filter(entry -> entry.getKey() > 8)
|
||||
.findFirst()
|
||||
.orElseThrow()
|
||||
.getKey();
|
||||
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(Main.instance(), () -> {
|
||||
ItemStack firstItem = inventory.getItem(itemSlot);
|
||||
ItemStack secondItem = inventory.getItem(replacementSlot);
|
||||
|
||||
inventory.setItem(itemSlot, secondItem);
|
||||
inventory.setItem(replacementSlot, firstItem);
|
||||
|
||||
player.sendActionBar(Component.text("Die Hotbar wurde aufgefüllt", NamedTextColor.GREEN));
|
||||
}, 1);
|
||||
} catch(NoSuchElementException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull List<Listener> listeners() {
|
||||
return List.of(new HotbarRefillListener());
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.hotbarRefill;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceListener;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.block.BlockPlaceEvent;
|
||||
import org.bukkit.event.player.PlayerItemBreakEvent;
|
||||
import org.bukkit.event.player.PlayerItemConsumeEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
class HotbarRefillListener extends ApplianceListener<HotbarRefill> {
|
||||
private HotbarRefillSetting.HotbarReplaceConfig getPlayerSetting(Player player) {
|
||||
return Settings.instance().getSetting(
|
||||
player,
|
||||
Settings.Key.HotbarReplacer,
|
||||
HotbarRefillSetting.HotbarReplaceConfig.class
|
||||
);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void blockPlace(BlockPlaceEvent event) {
|
||||
ItemStack stackInHand = event.getItemInHand();
|
||||
if(stackInHand.getAmount() != 1) return;
|
||||
if(stackInHand.getType().getMaxDurability() > 0) return;
|
||||
if(stackInHand.getType().getMaxStackSize() > 0) return;
|
||||
|
||||
if(!this.getPlayerSetting(event.getPlayer()).onBlocks()) return;
|
||||
this.getAppliance().handleHotbarChange(event.getPlayer(), stackInHand);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerItemBreak(PlayerItemBreakEvent event) {
|
||||
if(!this.getPlayerSetting(event.getPlayer()).onTools()) return;
|
||||
|
||||
this.getAppliance().handleHotbarChange(event.getPlayer(), event.getBrokenItem());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerItemConsume(PlayerItemConsumeEvent event) {
|
||||
if(List.of(Material.POTION, Material.HONEY_BOTTLE).contains(event.getItem().getType())) return;
|
||||
if(!this.getPlayerSetting(event.getPlayer()).onConsumable()) return;
|
||||
|
||||
this.getAppliance().handleHotbarChange(event.getPlayer(), event.getItem());
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.hotbarRefill;
|
||||
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.CategorizedSetting;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.SettingCategory;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.datatypes.MultiBoolSetting;
|
||||
import org.bukkit.Material;
|
||||
|
||||
public class HotbarRefillSetting extends MultiBoolSetting<HotbarRefillSetting.HotbarReplaceConfig> implements CategorizedSetting {
|
||||
@Override
|
||||
public SettingCategory category() {
|
||||
return SettingCategory.Gameplay;
|
||||
}
|
||||
|
||||
public record HotbarReplaceConfig(
|
||||
@DisplayName("Blöcke") boolean onBlocks,
|
||||
@DisplayName("Werkzeuge") boolean onTools,
|
||||
@DisplayName("Essen") boolean onConsumable
|
||||
) {
|
||||
}
|
||||
|
||||
public HotbarRefillSetting() {
|
||||
super(Settings.Key.HotbarReplacer);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String title() {
|
||||
return "Automatische Hotbar";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String description() {
|
||||
return "Verschiebe Items automatisch von deinem Inventar in die Hotbar, wenn diese verbraucht werden";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Material icon() {
|
||||
return Material.CHEST;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HotbarReplaceConfig defaultValue() {
|
||||
return new HotbarReplaceConfig(false, false, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> dataType() {
|
||||
return HotbarReplaceConfig.class;
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.knockDoor;
|
||||
|
||||
import eu.mhsl.craftattack.core.Main;
|
||||
import eu.mhsl.craftattack.core.appliance.Appliance;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.datatypes.SelectSetting;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.metadata.FixedMetadataValue;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class KnockDoor extends Appliance {
|
||||
@Override
|
||||
public void onEnable() {
|
||||
Settings.instance().declareSetting(KnockDoorSetting.class);
|
||||
}
|
||||
|
||||
public void knockAtDoor(Player knockingPlayer, Block knockedBlock) {
|
||||
SelectSetting.Options.Option setting = Settings.instance().getSetting(knockingPlayer, Settings.Key.KnockDoors, SelectSetting.Options.Option.class);
|
||||
if(setting.is(KnockDoorSetting.disabled)) return;
|
||||
|
||||
if(setting.is(KnockDoorSetting.knockSingleTime)) {
|
||||
this.playSound(knockedBlock);
|
||||
}
|
||||
|
||||
if(setting.is(KnockDoorSetting.knockThreeTimes)) {
|
||||
String metadataKey = new NamespacedKey(Main.instance(), KnockDoor.class.getName()).getNamespace();
|
||||
if(knockingPlayer.hasMetadata(metadataKey)) return;
|
||||
knockingPlayer.setMetadata(metadataKey, new FixedMetadataValue(Main.instance(), 0));
|
||||
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
int timesKnocked = knockingPlayer.getMetadata(metadataKey).getFirst().asInt();
|
||||
if(timesKnocked >= 3) {
|
||||
knockingPlayer.removeMetadata(metadataKey, Main.instance());
|
||||
this.cancel();
|
||||
return;
|
||||
}
|
||||
KnockDoor.this.playSound(knockedBlock);
|
||||
knockingPlayer.setMetadata(metadataKey, new FixedMetadataValue(Main.instance(), timesKnocked + 1));
|
||||
}
|
||||
}.runTaskTimer(Main.instance(), 0, 8);
|
||||
}
|
||||
}
|
||||
|
||||
private void playSound(Block knockedBlock) {
|
||||
Location knockLocation = knockedBlock.getLocation();
|
||||
Sound sound = knockedBlock.getType() == Material.IRON_DOOR
|
||||
? Sound.ENTITY_ZOMBIE_ATTACK_IRON_DOOR
|
||||
: Sound.ITEM_SHIELD_BLOCK;
|
||||
|
||||
knockLocation.getWorld().playSound(knockLocation, sound, SoundCategory.PLAYERS, 1f, 1f);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull List<Listener> listeners() {
|
||||
return List.of(new KnockDoorListener());
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.knockDoor;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceListener;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.data.type.Door;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.block.BlockDamageAbortEvent;
|
||||
|
||||
class KnockDoorListener extends ApplianceListener<KnockDoor> {
|
||||
@EventHandler
|
||||
public void onKnock(BlockDamageAbortEvent event) {
|
||||
if(event.getPlayer().getGameMode() != GameMode.SURVIVAL) return;
|
||||
Block block = event.getBlock();
|
||||
if(!(block.getBlockData() instanceof Door)) return;
|
||||
this.getAppliance().knockAtDoor(event.getPlayer(), block);
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.knockDoor;
|
||||
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.CategorizedSetting;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.SettingCategory;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.datatypes.SelectSetting;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.NamespacedKey;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class KnockDoorSetting extends SelectSetting implements CategorizedSetting {
|
||||
private static final String namespace = KnockDoorSetting.class.getSimpleName().toLowerCase(Locale.ROOT);
|
||||
public static Options.Option disabled = new Options.Option("Deaktiviert", new NamespacedKey(namespace, "disabled"));
|
||||
public static Options.Option knockSingleTime = new Options.Option("Einmal an der Tür anklopfen", new NamespacedKey(namespace, "single"));
|
||||
public static Options.Option knockThreeTimes = new Options.Option("Dreimal an der Tür anklopfen", new NamespacedKey(namespace, "three"));
|
||||
|
||||
public KnockDoorSetting() {
|
||||
super(
|
||||
Settings.Key.KnockDoors,
|
||||
new Options(List.of(disabled, knockSingleTime, knockThreeTimes))
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SettingCategory category() {
|
||||
return SettingCategory.Gameplay;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String title() {
|
||||
return "Klopfen an Türen";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String description() {
|
||||
return "Klopft durch das schlagen an eine Tür an.";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Material icon() {
|
||||
return Material.BELL;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Options.Option defaultValue() {
|
||||
return disabled;
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.outlawed;
|
||||
|
||||
class OutlawChangeNotPermitted extends Exception {
|
||||
public OutlawChangeNotPermitted(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
@ -0,0 +1,134 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.outlawed;
|
||||
|
||||
import eu.mhsl.craftattack.core.Main;
|
||||
import eu.mhsl.craftattack.core.appliance.Appliance;
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceCommand;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.displayName.DisplayName;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.tooling.whitelist.Whitelist;
|
||||
import eu.mhsl.craftattack.core.config.Configuration;
|
||||
import eu.mhsl.craftattack.core.util.text.DisconnectInfo;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.HoverEvent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Outlawed extends Appliance implements DisplayName.Prefixed {
|
||||
public final int timeoutInMs = 1000 * 60 * 60 * 6;
|
||||
private final Map<UUID, Long> timeouts = new HashMap<>();
|
||||
|
||||
public enum Status {
|
||||
DISABLED,
|
||||
VOLUNTARILY,
|
||||
FORCED
|
||||
}
|
||||
|
||||
private final Map<Player, Status> playerStatusMap = new WeakHashMap<>();
|
||||
private final String voluntarilyEntry = "voluntarily";
|
||||
|
||||
public Outlawed() {
|
||||
super("outlawed");
|
||||
Bukkit.getScheduler().runTaskTimerAsynchronously(
|
||||
Main.instance(),
|
||||
() -> this.playerStatusMap.forEach((player, status) -> {
|
||||
if(!player.isOnline()) return;
|
||||
if(status != Status.FORCED) return;
|
||||
try {
|
||||
this.queryAppliance(Whitelist.class).fullIntegrityCheck(player);
|
||||
} catch(DisconnectInfo.Throwable e) {
|
||||
e.getDisconnectScreen().applyKick(player);
|
||||
}
|
||||
}),
|
||||
20 * 60,
|
||||
20 * 60 * 5
|
||||
);
|
||||
}
|
||||
|
||||
public void switchLawStatus(Player player) throws OutlawChangeNotPermitted {
|
||||
if(this.getLawStatus(player).equals(Status.FORCED)) {
|
||||
throw new OutlawChangeNotPermitted("Dein Vogelfreistatus wurde als Strafe auferlegt und kann daher nicht verändert werden.");
|
||||
}
|
||||
|
||||
if(this.isTimeout(player)) {
|
||||
throw new OutlawChangeNotPermitted("Du kannst deinen Vogelfreistatus nicht so schnell wechseln. Bitte warte einige Stunden bevor du umschaltest!");
|
||||
}
|
||||
|
||||
this.setLawStatus(player, this.isOutlawed(player) ? Status.DISABLED : Status.VOLUNTARILY);
|
||||
this.setTimeout(player);
|
||||
}
|
||||
|
||||
public void updateForcedStatus(Player player, boolean forced) {
|
||||
this.setLawStatus(player, forced ? Status.FORCED : this.getLawStatus(player) == Status.FORCED ? Status.DISABLED : this.getLawStatus(player));
|
||||
}
|
||||
|
||||
public Status getLawStatus(Player player) {
|
||||
return this.playerStatusMap.computeIfAbsent(player, p -> {
|
||||
if(this.localConfig().getStringList(this.voluntarilyEntry).contains(p.getUniqueId().toString()))
|
||||
return Status.VOLUNTARILY;
|
||||
return Status.DISABLED;
|
||||
});
|
||||
}
|
||||
|
||||
private void setLawStatus(Player player, Status status) {
|
||||
this.playerStatusMap.put(player, status);
|
||||
this.queryAppliance(DisplayName.class).update(player);
|
||||
|
||||
List<String> newList = this.localConfig().getStringList(this.voluntarilyEntry);
|
||||
if(status.equals(Status.VOLUNTARILY)) {
|
||||
newList.add(player.getUniqueId().toString());
|
||||
} else {
|
||||
newList.remove(player.getUniqueId().toString());
|
||||
}
|
||||
this.localConfig().set(this.voluntarilyEntry, newList.stream().distinct().toList());
|
||||
Configuration.saveChanges();
|
||||
|
||||
}
|
||||
|
||||
public boolean isOutlawed(Player player) {
|
||||
return this.getLawStatus(player) != Status.DISABLED;
|
||||
}
|
||||
|
||||
private boolean isTimeout(Player player) {
|
||||
return this.timeouts.getOrDefault(player.getUniqueId(), 0L) > System.currentTimeMillis() - this.timeoutInMs;
|
||||
}
|
||||
|
||||
private void setTimeout(Player player) {
|
||||
this.timeouts.put(player.getUniqueId(), System.currentTimeMillis());
|
||||
}
|
||||
|
||||
public Component getStatusDescription(Status status) {
|
||||
return switch(status) {
|
||||
case DISABLED -> Component.text("Vogelfreistatus inaktiv: ", NamedTextColor.GREEN)
|
||||
.append(Component.text("Es gelten die Standard Regeln!", NamedTextColor.GOLD));
|
||||
|
||||
case VOLUNTARILY, FORCED -> Component.text("Vogelfreistatus aktiv: ", NamedTextColor.RED)
|
||||
.append(Component.text("Du darfst von jedem angegriffen und getötet werden!", NamedTextColor.GOLD));
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getNamePrefix(Player player) {
|
||||
if(this.isOutlawed(player)) {
|
||||
return Component.text("[☠]", NamedTextColor.RED)
|
||||
.hoverEvent(HoverEvent.showText(Component.text("Vogelfreie Spieler dürfen ohne Grund angegriffen werden!")));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
protected List<ApplianceCommand<?>> commands() {
|
||||
return List.of(new OutlawedCommand());
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
protected List<Listener> listeners() {
|
||||
return List.of(new OutlawedReminderListener());
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.outlawed;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceCommand;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
class OutlawedCommand extends ApplianceCommand.PlayerChecked<Outlawed> {
|
||||
public OutlawedCommand() {
|
||||
super("vogelfrei");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
|
||||
try {
|
||||
this.getAppliance().switchLawStatus(this.getPlayer());
|
||||
sender.sendMessage(
|
||||
this.getAppliance()
|
||||
.getStatusDescription(this.getAppliance().getLawStatus(this.getPlayer()))
|
||||
);
|
||||
} catch(OutlawChangeNotPermitted e) {
|
||||
sender.sendMessage(Component.text(e.getMessage(), NamedTextColor.RED));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.outlawed;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceListener;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
|
||||
class OutlawedReminderListener extends ApplianceListener<Outlawed> {
|
||||
@EventHandler
|
||||
public void onJoin(PlayerJoinEvent e) {
|
||||
if(this.getAppliance().isOutlawed(e.getPlayer())) {
|
||||
e.getPlayer().sendMessage(this.getAppliance().getStatusDescription(this.getAppliance().getLawStatus(e.getPlayer())));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.portableCrafting;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceListener;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
|
||||
class OnCraftingTableUseListener extends ApplianceListener<PortableCrafting> {
|
||||
@EventHandler
|
||||
public void inInteract(PlayerInteractEvent event) {
|
||||
if(!event.getAction().equals(Action.RIGHT_CLICK_AIR)) return;
|
||||
if(!event.getMaterial().equals(Material.CRAFTING_TABLE)) return;
|
||||
this.getAppliance().openFor(event.getPlayer());
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.portableCrafting;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.Appliance;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class PortableCrafting extends Appliance {
|
||||
@Override
|
||||
public void onEnable() {
|
||||
Settings.instance().declareSetting(PortableCraftingSetting.class);
|
||||
}
|
||||
|
||||
public void openFor(Player player) {
|
||||
if(!Settings.instance().getSetting(player, Settings.Key.EnablePortableCrafting, Boolean.class)) return;
|
||||
player.openWorkbench(null, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull List<Listener> listeners() {
|
||||
return List.of(new OnCraftingTableUseListener());
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.portableCrafting;
|
||||
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.CategorizedSetting;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.SettingCategory;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.datatypes.BoolSetting;
|
||||
import org.bukkit.Material;
|
||||
|
||||
public class PortableCraftingSetting extends BoolSetting implements CategorizedSetting {
|
||||
public PortableCraftingSetting() {
|
||||
super(Settings.Key.EnablePortableCrafting);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String title() {
|
||||
return "Portables Crafting";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String description() {
|
||||
return "Erlaubt das öffnen einer Werkbank in der Hand, ohne sie plazieren zu müssen";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Material icon() {
|
||||
return Material.CRAFTING_TABLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean defaultValue() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SettingCategory category() {
|
||||
return SettingCategory.Gameplay;
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.snowballKnockback;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.Appliance;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SnowballKnockback extends Appliance {
|
||||
public void dealSnowballKnockback(LivingEntity entity, Entity snowball) {
|
||||
entity.damage(0.1);
|
||||
entity.knockback(0.4, -snowball.getVelocity().getX(), -snowball.getVelocity().getZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
protected List<Listener> listeners() {
|
||||
return List.of(new SnowballKnockbackListener());
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.snowballKnockback;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceListener;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.entity.ProjectileHitEvent;
|
||||
|
||||
class SnowballKnockbackListener extends ApplianceListener<SnowballKnockback> {
|
||||
@EventHandler
|
||||
public void onSnowballHit(ProjectileHitEvent event) {
|
||||
if(event.getHitEntity() == null) return;
|
||||
if(!event.getEntityType().equals(EntityType.SNOWBALL)) return;
|
||||
if(!(event.getHitEntity() instanceof LivingEntity hitEntity)) return;
|
||||
|
||||
Entity snowball = event.getEntity();
|
||||
this.getAppliance().dealSnowballKnockback(hitEntity, snowball);
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.adminMarker;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.Appliance;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import org.bukkit.Color;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class AdminMarker extends Appliance {
|
||||
public TextColor getPlayerColor(Player player) {
|
||||
if(player.hasPermission("chatcolor"))
|
||||
return TextColor.color(Color.AQUA.asRGB()); // TODO read permission from config
|
||||
return TextColor.color(Color.WHITE.asRGB());
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.afkTag;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceListener;
|
||||
import io.papermc.paper.event.player.AsyncChatEvent;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerMoveEvent;
|
||||
|
||||
class AfkResetListener extends ApplianceListener<AfkTag> {
|
||||
@EventHandler
|
||||
public void onMove(PlayerMoveEvent event) {
|
||||
this.getAppliance().resetTiming(event.getPlayer());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onInteract(PlayerInteractEvent event) {
|
||||
this.getAppliance().resetTiming(event.getPlayer());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onChat(AsyncChatEvent event) {
|
||||
this.getAppliance().resetTiming(event.getPlayer());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onJoin(PlayerJoinEvent event) {
|
||||
this.getAppliance().resetTiming(event.getPlayer());
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.afkTag;
|
||||
|
||||
import eu.mhsl.craftattack.core.Main;
|
||||
import eu.mhsl.craftattack.core.appliance.Appliance;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.displayName.DisplayName;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.HoverEvent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.util.Ticks;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
public class AfkTag extends Appliance implements DisplayName.Prefixed {
|
||||
private final HashMap<UUID, Long> afkTimings = new HashMap<>();
|
||||
private static final int updateIntervalSeconds = 30;
|
||||
private static final int afkWhenMillis = 3 * 60 * 1000;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
Bukkit.getScheduler().runTaskTimerAsynchronously(
|
||||
Main.instance(),
|
||||
this::checkAfkPlayers,
|
||||
Ticks.TICKS_PER_SECOND,
|
||||
Ticks.TICKS_PER_SECOND * updateIntervalSeconds
|
||||
);
|
||||
}
|
||||
|
||||
public void resetTiming(Player player) {
|
||||
boolean wasAfk = this.isAfk(player);
|
||||
this.afkTimings.put(player.getUniqueId(), System.currentTimeMillis());
|
||||
if(wasAfk) this.updateAfkPrefix(player);
|
||||
}
|
||||
|
||||
private void checkAfkPlayers() {
|
||||
this.afkTimings.keySet().stream()
|
||||
.map(Bukkit::getPlayer)
|
||||
.filter(Objects::nonNull)
|
||||
.filter(this::isAfk)
|
||||
.forEach(this::updateAfkPrefix);
|
||||
}
|
||||
|
||||
private boolean isAfk(Player player) {
|
||||
if(player.isSleeping()) return false;
|
||||
|
||||
long lastTimeActive = this.afkTimings.getOrDefault(player.getUniqueId(), 0L);
|
||||
long timeSinceLastActive = System.currentTimeMillis() - lastTimeActive;
|
||||
return timeSinceLastActive >= afkWhenMillis;
|
||||
}
|
||||
|
||||
private void updateAfkPrefix(Player player) {
|
||||
Main.instance().getAppliance(DisplayName.class).update(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Component getNamePrefix(Player player) {
|
||||
if(this.isAfk(player)) return Component.text("[ᵃᶠᵏ]", NamedTextColor.GRAY)
|
||||
.hoverEvent(HoverEvent.showText(Component.text("Der Spieler ist AFK")));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull List<Listener> listeners() {
|
||||
return List.of(new AfkResetListener());
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.displayName;
|
||||
|
||||
import eu.mhsl.craftattack.core.Main;
|
||||
import eu.mhsl.craftattack.core.appliance.Appliance;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.outlawed.Outlawed;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.adminMarker.AdminMarker;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.afkTag.AfkTag;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.sleepTag.SleepTag;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.yearRank.YearRank;
|
||||
import eu.mhsl.craftattack.core.util.server.Floodgate;
|
||||
import eu.mhsl.craftattack.core.util.text.ComponentUtil;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.ComponentBuilder;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import net.kyori.adventure.text.event.HoverEvent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class DisplayName extends Appliance {
|
||||
public interface Prefixed {
|
||||
@Nullable
|
||||
Component getNamePrefix(Player player);
|
||||
}
|
||||
|
||||
public void update(Player player) {
|
||||
TextColor playerColor = this.queryAppliance(AdminMarker.class).getPlayerColor(player);
|
||||
List<Supplier<Prefixed>> prefixes = List.of(
|
||||
() -> this.queryAppliance(Outlawed.class),
|
||||
() -> this.queryAppliance(YearRank.class),
|
||||
() -> this.queryAppliance(AfkTag.class),
|
||||
() -> this.queryAppliance(SleepTag.class)
|
||||
);
|
||||
|
||||
ComponentBuilder<TextComponent, TextComponent.Builder> playerName = Component.text();
|
||||
prefixes.stream()
|
||||
.map(prefixed -> prefixed.get().getNamePrefix(player))
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(prefix -> playerName
|
||||
.append(prefix)
|
||||
.append(ComponentUtil.clearedSpace())
|
||||
);
|
||||
|
||||
if(Floodgate.isBedrock(player)) {
|
||||
playerName
|
||||
.append(
|
||||
Component.text("\uD83C\uDFAE", NamedTextColor.GRAY)
|
||||
.hoverEvent(HoverEvent.showText(Component.text(
|
||||
String.format("%s spielt die Minecraft: Bedrock Edition", player.getName())
|
||||
)))
|
||||
)
|
||||
.append(ComponentUtil.clearedSpace());
|
||||
}
|
||||
|
||||
playerName.append(Component.text(player.getName(), playerColor));
|
||||
|
||||
this.setGlobal(player, playerName.build());
|
||||
}
|
||||
|
||||
private void setGlobal(Player player, Component component) {
|
||||
try {
|
||||
player.customName(component);
|
||||
player.displayName(component);
|
||||
player.playerListName(component);
|
||||
} catch(Exception e) {
|
||||
Main.instance().getLogger().log(Level.SEVERE, e, e::getMessage);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.displayName;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceListener;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
|
||||
class DisplayNameUpdateListener extends ApplianceListener<DisplayName> {
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
public void onJoin(PlayerJoinEvent event) {
|
||||
this.getAppliance().update(event.getPlayer());
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.event;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceListener;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
|
||||
class ApplyPendingRewardsListener extends ApplianceListener<Event> {
|
||||
@EventHandler
|
||||
public void onJoin(PlayerJoinEvent event) {
|
||||
this.getAppliance().applyPendingRewards(event.getPlayer());
|
||||
}
|
||||
}
|
@ -0,0 +1,230 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.event;
|
||||
|
||||
import eu.mhsl.craftattack.core.Main;
|
||||
import eu.mhsl.craftattack.core.api.client.ReqResp;
|
||||
import eu.mhsl.craftattack.core.api.client.repositories.EventRepository;
|
||||
import eu.mhsl.craftattack.core.api.server.HttpServer;
|
||||
import eu.mhsl.craftattack.core.appliance.Appliance;
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceCommand;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.customAdvancements.Advancements;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.customAdvancements.CustomAdvancements;
|
||||
import eu.mhsl.craftattack.core.util.IteratorUtil;
|
||||
import eu.mhsl.craftattack.core.util.api.HttpStatus;
|
||||
import eu.mhsl.craftattack.core.util.entity.DisplayVillager;
|
||||
import eu.mhsl.craftattack.core.util.listener.DismissInventoryOpenFromHolder;
|
||||
import eu.mhsl.craftattack.core.util.listener.PlayerInteractAtEntityEventListener;
|
||||
import eu.mhsl.craftattack.core.util.server.PluginMessage;
|
||||
import eu.mhsl.craftattack.core.util.text.ComponentUtil;
|
||||
import eu.mhsl.craftattack.core.util.text.Countdown;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.event.command.*;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.Villager;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Event extends Appliance {
|
||||
enum AdvertisementStatus {
|
||||
BEFORE,
|
||||
ADVERTISED,
|
||||
DONE
|
||||
}
|
||||
|
||||
Countdown advertiseCountdown = new Countdown(
|
||||
120,
|
||||
announcementData -> Component.text()
|
||||
.append(ComponentUtil.createRainbowText("Event", 30))
|
||||
.append(Component.text(" Start in ", NamedTextColor.GOLD))
|
||||
.append(Component.text(announcementData.count(), NamedTextColor.AQUA))
|
||||
.append(Component.text(" " + announcementData.unit() + "!", NamedTextColor.GOLD))
|
||||
.build(),
|
||||
component -> IteratorUtil.onlinePlayers(player -> player.sendMessage(component)),
|
||||
() -> this.advertiseStatus = AdvertisementStatus.DONE
|
||||
);
|
||||
public DisplayVillager.ConfigBound villager;
|
||||
private boolean isOpen = false;
|
||||
private AdvertisementStatus advertiseStatus = AdvertisementStatus.BEFORE;
|
||||
private UUID roomId;
|
||||
private final List<Reward> pendingRewards = new ArrayList<>();
|
||||
|
||||
record RewardConfiguration(String memorialMaterial, String memorialTitle, String memorialLore, List<UUID> memorials,
|
||||
String material, Map<UUID, Integer> rewards) {
|
||||
}
|
||||
|
||||
record Reward(UUID playerUuid, ItemStack itemStack) {
|
||||
}
|
||||
|
||||
public Event() {
|
||||
super("event");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
this.villager = new DisplayVillager.ConfigBound(
|
||||
this.localConfig(),
|
||||
villager -> {
|
||||
villager.customName(Component.text("Events", NamedTextColor.GOLD));
|
||||
villager.setProfession(Villager.Profession.LIBRARIAN);
|
||||
villager.setVillagerType(Villager.Type.SNOW);
|
||||
}
|
||||
);
|
||||
this.isOpen = this.localConfig().getBoolean("enabled", false);
|
||||
if(this.isOpen) this.roomId = UUID.fromString(this.localConfig().getString("roomId", ""));
|
||||
}
|
||||
|
||||
public void openEvent() {
|
||||
if(this.isOpen) throw new ApplianceCommand.Error("Es läuft derzeit bereits ein Event!");
|
||||
|
||||
Bukkit.getScheduler().runTaskAsynchronously(Main.instance(), () -> {
|
||||
ReqResp<EventRepository.CreatedRoom> sessionResponse = this.queryRepository(EventRepository.class).createSession();
|
||||
|
||||
if(sessionResponse.status() != HttpStatus.OK)
|
||||
throw new ApplianceCommand.Error("Event-Server meldet Fehler: " + sessionResponse.status());
|
||||
|
||||
this.isOpen = true;
|
||||
this.roomId = sessionResponse.data().uuid();
|
||||
});
|
||||
}
|
||||
|
||||
public void joinEvent(Player p) {
|
||||
if(!this.isOpen) {
|
||||
p.sendMessage(Component.text("Zurzeit ist kein Event geöffnet.", NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
|
||||
if(!p.hasPermission("admin") && this.advertiseStatus == AdvertisementStatus.BEFORE) {
|
||||
p.sendMessage(Component.text("Die Event befinden sich noch in der Vorbereitung.", NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
|
||||
if(!p.hasPermission("admin") && this.advertiseStatus == AdvertisementStatus.DONE) {
|
||||
p.sendMessage(Component.text("Die Events laufen bereits. Ein nachträgliches Beitreten ist nicht möglich.", NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
|
||||
Main.instance().getLogger().info("Verbinde mit eventserver: " + p.getName());
|
||||
p.sendMessage(Component.text("Authentifiziere...", NamedTextColor.GREEN));
|
||||
|
||||
Bukkit.getScheduler().runTaskAsynchronously(Main.instance(), () -> {
|
||||
ReqResp<EventRepository.QueueRoom.Response> queueResponse = this.queryRepository(EventRepository.class)
|
||||
.queueRoom(new EventRepository.QueueRoom(p.getUniqueId(), this.roomId));
|
||||
|
||||
if(queueResponse.status() != HttpStatus.OK || queueResponse.data().error() != null) {
|
||||
p.sendMessage(Component.text("Fehler beim Betreten: " + queueResponse.data().error(), NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
|
||||
p.sendMessage(Component.text("Betrete...", NamedTextColor.GREEN));
|
||||
PluginMessage.connect(p, this.localConfig().getString("connect-server-name"));
|
||||
});
|
||||
}
|
||||
|
||||
public void endEvent() {
|
||||
if(!this.isOpen) throw new ApplianceCommand.Error("Es läuft derzeit kein Event!");
|
||||
this.isOpen = false;
|
||||
}
|
||||
|
||||
private void rewardPlayers(RewardConfiguration rewardConfiguration) {
|
||||
rewardConfiguration.rewards.forEach((uuid, amount) -> {
|
||||
Reward reward = new Reward(uuid, new ItemStack(Objects.requireNonNull(Material.matchMaterial(rewardConfiguration.material)), amount));
|
||||
|
||||
if(Bukkit.getPlayer(uuid) == null) {
|
||||
this.pendingRewards.add(reward);
|
||||
return;
|
||||
}
|
||||
this.giveReward(reward);
|
||||
});
|
||||
|
||||
rewardConfiguration.memorials.forEach(uuid -> {
|
||||
ItemStack memorialItem = new ItemStack(Objects.requireNonNull(Material.matchMaterial(rewardConfiguration.memorialMaterial)));
|
||||
ItemMeta meta = memorialItem.getItemMeta();
|
||||
meta.displayName(Component.text(rewardConfiguration.memorialTitle, NamedTextColor.GOLD));
|
||||
meta.lore(List.of(Component.text(rewardConfiguration.memorialLore, NamedTextColor.AQUA)));
|
||||
memorialItem.setItemMeta(meta);
|
||||
Reward memorial = new Reward(uuid, memorialItem);
|
||||
|
||||
Main.instance().getAppliance(CustomAdvancements.class).grantAdvancement(Advancements.participateEvent, uuid);
|
||||
|
||||
if(Bukkit.getPlayer(uuid) == null) {
|
||||
this.pendingRewards.add(memorial);
|
||||
return;
|
||||
}
|
||||
this.giveReward(memorial);
|
||||
});
|
||||
|
||||
rewardConfiguration.rewards.keySet().stream()
|
||||
.max(Comparator.comparing(rewardConfiguration.rewards::get))
|
||||
.ifPresent(uuid -> Main.instance().getAppliance(CustomAdvancements.class)
|
||||
.grantAdvancement(Advancements.winner, uuid));
|
||||
}
|
||||
|
||||
private void giveReward(Reward reward) {
|
||||
Player player = Bukkit.getPlayer(reward.playerUuid);
|
||||
if(player == null) throw new RuntimeException("Cannot reward offline playerUuid!");
|
||||
|
||||
Map<Integer, ItemStack> remaining = player.getInventory().addItem(reward.itemStack);
|
||||
Bukkit.getScheduler().runTask(
|
||||
Main.instance(),
|
||||
() -> remaining.values().forEach(remainingStack -> player.getWorld().dropItem(player.getLocation(), remainingStack))
|
||||
);
|
||||
}
|
||||
|
||||
public void applyPendingRewards(Player player) {
|
||||
if(this.pendingRewards.isEmpty()) return;
|
||||
List<Reward> givenRewards = this.pendingRewards.stream().filter(reward -> reward.playerUuid.equals(player.getUniqueId())).toList();
|
||||
this.pendingRewards.removeAll(givenRewards);
|
||||
givenRewards.forEach(this::giveReward);
|
||||
}
|
||||
|
||||
public void advertise() {
|
||||
this.advertiseCountdown.cancelIfRunning();
|
||||
this.advertiseStatus = AdvertisementStatus.ADVERTISED;
|
||||
IteratorUtil.onlinePlayers(player -> player.sendMessage(
|
||||
Component.text()
|
||||
.append(Component.text("-".repeat(10), NamedTextColor.GRAY)).appendNewline()
|
||||
.append(Component.text("Ein Event wurde gestartet!", NamedTextColor.GOLD)).appendNewline()
|
||||
.append(Component.text("Nutze "))
|
||||
.append(Component.text("/event", NamedTextColor.AQUA))
|
||||
.append(Component.text(", um dem Event beizutreten!")).appendNewline()
|
||||
.append(Component.text("-".repeat(10), NamedTextColor.GRAY)).appendNewline()
|
||||
));
|
||||
this.advertiseCountdown.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void httpApi(HttpServer.ApiBuilder apiBuilder) {
|
||||
apiBuilder.post("reward", RewardConfiguration.class, (rewardConfiguration, request) -> {
|
||||
this.rewardPlayers(rewardConfiguration);
|
||||
return HttpServer.nothing;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
protected List<ApplianceCommand<?>> commands() {
|
||||
return List.of(
|
||||
new EventCommand(),
|
||||
new MoveEventVillagerCommand(),
|
||||
new EventOpenSessionCommand(),
|
||||
new EventEndSessionCommand(),
|
||||
new EventAdvertiseCommand()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
protected List<Listener> listeners() {
|
||||
return List.of(
|
||||
new ApplyPendingRewardsListener(),
|
||||
new PlayerInteractAtEntityEventListener(this.villager.getUniqueId(), playerInteractAtEntityEvent -> this.joinEvent(playerInteractAtEntityEvent.getPlayer())),
|
||||
new DismissInventoryOpenFromHolder(this.villager.getUniqueId())
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.event.command;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceCommand;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.event.Event;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class EventAdvertiseCommand extends ApplianceCommand<Event> {
|
||||
public EventAdvertiseCommand() {
|
||||
super("eventAdvertise");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
|
||||
this.getAppliance().advertise();
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.event.command;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceCommand;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.event.Event;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class EventCommand extends ApplianceCommand.PlayerChecked<Event> {
|
||||
public EventCommand() {
|
||||
super("event");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
|
||||
this.getAppliance().joinEvent(this.getPlayer());
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.event.command;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceCommand;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.event.Event;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class EventEndSessionCommand extends ApplianceCommand<Event> {
|
||||
public EventEndSessionCommand() {
|
||||
super("eventEndSession");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
this.getAppliance().endEvent();
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.event.command;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceCommand;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.event.Event;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class EventOpenSessionCommand extends ApplianceCommand<Event> {
|
||||
public EventOpenSessionCommand() {
|
||||
super("eventOpenSession");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
|
||||
this.getAppliance().openEvent();
|
||||
sender.sendMessage(Component.text("Event-Server gestartet!", NamedTextColor.GREEN));
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.event.command;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceCommand;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.event.Event;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class MoveEventVillagerCommand extends ApplianceCommand.PlayerChecked<Event> {
|
||||
public MoveEventVillagerCommand() {
|
||||
super("moveEventVillager");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
this.getAppliance().villager.updateLocation(this.getPlayer().getLocation());
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.feedback;
|
||||
|
||||
import eu.mhsl.craftattack.core.Main;
|
||||
import eu.mhsl.craftattack.core.api.client.ReqResp;
|
||||
import eu.mhsl.craftattack.core.api.client.repositories.FeedbackRepository;
|
||||
import eu.mhsl.craftattack.core.appliance.Appliance;
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceCommand;
|
||||
import eu.mhsl.craftattack.core.util.api.HttpStatus;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.ComponentBuilder;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.event.HoverEvent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class Feedback extends Appliance {
|
||||
public Feedback() {
|
||||
super("feedback");
|
||||
}
|
||||
|
||||
public void requestFeedback(String eventName, List<Player> receivers, @Nullable String question) {
|
||||
ReqResp<Map<UUID, String>> response = this.queryRepository(FeedbackRepository.class).createFeedbackUrls(
|
||||
new FeedbackRepository.Request(eventName, receivers.stream().map(Entity::getUniqueId).toList())
|
||||
);
|
||||
|
||||
System.out.println(response.toString());
|
||||
System.out.println(response.status());
|
||||
|
||||
if(response.status() != HttpStatus.CREATED) throw new RuntimeException();
|
||||
|
||||
Component border = Component.text("-".repeat(40), NamedTextColor.GRAY);
|
||||
|
||||
receivers.forEach(player -> {
|
||||
String feedbackUrl = response.data().get(player.getUniqueId());
|
||||
if(feedbackUrl == null) {
|
||||
Main.logger().warning(String.format("FeedbackUrl not found for player '%s' from backend!", player.getUniqueId()));
|
||||
return;
|
||||
}
|
||||
|
||||
ComponentBuilder<TextComponent, TextComponent.Builder> message = Component.text()
|
||||
.append(border)
|
||||
.appendNewline();
|
||||
|
||||
if(question != null) {
|
||||
message
|
||||
.append(Component.text(question, NamedTextColor.GREEN))
|
||||
.appendNewline()
|
||||
.appendNewline();
|
||||
}
|
||||
|
||||
message
|
||||
.append(Component.text("Klicke hier und gib uns Feedback, damit wir dein Spielerlebnis verbessern können!", NamedTextColor.DARK_GREEN)
|
||||
.clickEvent(ClickEvent.openUrl(feedbackUrl)))
|
||||
.hoverEvent(HoverEvent.showText(Component.text("Klicke, um Feedback zu geben.")))
|
||||
.appendNewline()
|
||||
.append(border);
|
||||
|
||||
player.sendMessage(message.build());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull List<ApplianceCommand<?>> commands() {
|
||||
return List.of(
|
||||
new FeedbackCommand(),
|
||||
new RequestFeedbackCommand()
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.feedback;
|
||||
|
||||
import eu.mhsl.craftattack.core.Main;
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceCommand;
|
||||
import eu.mhsl.craftattack.core.util.text.ComponentUtil;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
class FeedbackCommand extends ApplianceCommand.PlayerChecked<Feedback> {
|
||||
public FeedbackCommand() {
|
||||
super("feedback");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
|
||||
sender.sendMessage(ComponentUtil.pleaseWait());
|
||||
Bukkit.getScheduler().runTaskAsynchronously(
|
||||
Main.instance(),
|
||||
() -> this.getAppliance().requestFeedback(
|
||||
"self-issued-ingame",
|
||||
List.of(this.getPlayer()),
|
||||
null
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.feedback;
|
||||
|
||||
import eu.mhsl.craftattack.core.Main;
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceCommand;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
class RequestFeedbackCommand extends ApplianceCommand<Feedback> {
|
||||
public RequestFeedbackCommand() {
|
||||
super("requestFeedback");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
|
||||
Bukkit.getScheduler().runTaskAsynchronously(
|
||||
Main.instance(),
|
||||
() -> this.getAppliance().requestFeedback(
|
||||
"admin-issued-ingame",
|
||||
new ArrayList<>(Bukkit.getOnlinePlayers()), String.join(" ", args)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.optionLinks;
|
||||
|
||||
import eu.mhsl.craftattack.core.Main;
|
||||
import eu.mhsl.craftattack.core.appliance.Appliance;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ServerLinks;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.logging.Level;
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
public class OptionLinks extends Appliance {
|
||||
record ComponentSupplier() {
|
||||
}
|
||||
|
||||
record UriSupplier(Player player) {
|
||||
}
|
||||
|
||||
record UriConsumer(String uri) {
|
||||
}
|
||||
|
||||
record SuppliedLink(Function<ComponentSupplier, Component> component, Function<UriSupplier, UriConsumer> uri) {
|
||||
}
|
||||
|
||||
List<SuppliedLink> links = List.of(
|
||||
new SuppliedLink(
|
||||
componentSupplier -> Component.text("CraftAttack Homepage", NamedTextColor.GOLD),
|
||||
uriSupplier -> new UriConsumer("https://mhsl.eu/craftattack")
|
||||
),
|
||||
new SuppliedLink(
|
||||
componentSupplier -> Component.text("Regeln", NamedTextColor.GOLD),
|
||||
uriSupplier -> new UriConsumer("https://mhsl.eu/craftattack/rules")
|
||||
)
|
||||
);
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
Bukkit.getServer().getServerLinks().getLinks()
|
||||
.forEach(serverLink -> Bukkit.getServer().getServerLinks().removeLink(serverLink));
|
||||
}
|
||||
|
||||
public void setServerLinks(Player player) {
|
||||
ServerLinks playerLinks = Bukkit.getServerLinks().copy();
|
||||
Bukkit.getScheduler().runTaskAsynchronously(Main.instance(), () -> {
|
||||
this.links.forEach(suppliedLink -> {
|
||||
Component component = suppliedLink.component.apply(new ComponentSupplier());
|
||||
String uri = suppliedLink.uri.apply(new UriSupplier(player)).uri;
|
||||
|
||||
try {
|
||||
playerLinks.addLink(component, URI.create(uri));
|
||||
} catch(IllegalArgumentException e) {
|
||||
Main.logger().log(Level.INFO, String.format("Failed to create OptionLink '%s' for player '%s'", uri, player.getName()), e);
|
||||
}
|
||||
});
|
||||
player.sendLinks(playerLinks);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull List<Listener> listeners() {
|
||||
return List.of(new UpdateLinksListener());
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.optionLinks;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceListener;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
|
||||
class UpdateLinksListener extends ApplianceListener<OptionLinks> {
|
||||
@EventHandler
|
||||
public void onJoin(PlayerJoinEvent event) {
|
||||
this.getAppliance().setServerLinks(event.getPlayer());
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.packSelect;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceCommand;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
class ChangePackCommand extends ApplianceCommand.PlayerChecked<PackSelect> {
|
||||
public static final String commandName = "texturepack";
|
||||
|
||||
public ChangePackCommand() {
|
||||
super(commandName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
|
||||
this.getAppliance().openPackInventory(this.getPlayer());
|
||||
}
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.packSelect;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import eu.mhsl.craftattack.core.appliance.CachedApplianceSupplier;
|
||||
import eu.mhsl.craftattack.core.util.inventory.HeadBuilder;
|
||||
import eu.mhsl.craftattack.core.util.text.ComponentUtil;
|
||||
import net.kyori.adventure.resource.ResourcePackInfo;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class PackConfiguration extends CachedApplianceSupplier<PackSelect> {
|
||||
public record Pack(UUID id, String name, String description, String author, ResourcePackInfo info, String icon) {
|
||||
public ItemStack buildItem() {
|
||||
ItemStack stack = HeadBuilder.getCustomTextureHead(this.icon);
|
||||
ItemMeta meta = stack.getItemMeta();
|
||||
meta.itemName(Component.text(this.id.toString()));
|
||||
meta.displayName(Component.text(this.name(), NamedTextColor.GOLD));
|
||||
List<Component> lore = new ArrayList<>();
|
||||
lore.add(Component.text("Autor: ", NamedTextColor.DARK_GRAY).append(Component.text(this.author)));
|
||||
lore.add(Component.text(" "));
|
||||
lore.addAll(
|
||||
ComponentUtil.lineBreak(this.description())
|
||||
.map(s -> Component.text(s, NamedTextColor.GRAY))
|
||||
.toList()
|
||||
);
|
||||
lore.add(Component.text(" "));
|
||||
meta.lore(lore);
|
||||
stack.setItemMeta(meta);
|
||||
return stack;
|
||||
}
|
||||
|
||||
public boolean equalsItem(ItemStack other) {
|
||||
String itemName = PlainTextComponentSerializer.plainText().serialize(other.getItemMeta().itemName());
|
||||
try {
|
||||
return UUID.fromString(itemName).equals(this.id);
|
||||
} catch(IllegalArgumentException ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public record PackList(List<Pack> packs) {
|
||||
public Optional<Pack> findFromItem(ItemStack itemStack) {
|
||||
return this.packs.stream()
|
||||
.filter(pack -> pack.equalsItem(itemStack))
|
||||
.findFirst();
|
||||
}
|
||||
}
|
||||
|
||||
public record SerializedPackList(List<UUID> packs) {
|
||||
}
|
||||
|
||||
private final PackList packList;
|
||||
private final Gson gson = new GsonBuilder().create();
|
||||
|
||||
private PackConfiguration() {
|
||||
this.packList = new PackList(new ArrayList<>());
|
||||
}
|
||||
|
||||
private PackConfiguration(String data) {
|
||||
SerializedPackList serializedData = this.gson.fromJson(data, SerializedPackList.class);
|
||||
|
||||
var availablePackMap = this.getAppliance().availablePacks.packs().stream()
|
||||
.collect(Collectors.toMap(PackConfiguration.Pack::id, pack -> pack));
|
||||
|
||||
this.packList = new PackList(
|
||||
serializedData.packs().stream()
|
||||
.map(availablePackMap::get)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
|
||||
if(this.packList.packs().isEmpty())
|
||||
throw new IllegalArgumentException("Serialized data does not contain any valid data!");
|
||||
}
|
||||
|
||||
public static PackConfiguration deserialize(String data) {
|
||||
return new PackConfiguration(data);
|
||||
}
|
||||
|
||||
public static PackConfiguration empty() {
|
||||
return new PackConfiguration();
|
||||
}
|
||||
|
||||
public String serialize() {
|
||||
return this.gson.toJson(new SerializedPackList(this.packList.packs().stream().map(Pack::id).toList()));
|
||||
}
|
||||
|
||||
public List<Pack> getPackList() {
|
||||
return this.packList.packs();
|
||||
}
|
||||
}
|
@ -0,0 +1,171 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.packSelect;
|
||||
|
||||
import eu.mhsl.craftattack.core.Main;
|
||||
import eu.mhsl.craftattack.core.appliance.CachedApplianceSupplier;
|
||||
import eu.mhsl.craftattack.core.util.IteratorUtil;
|
||||
import eu.mhsl.craftattack.core.util.inventory.ItemBuilder;
|
||||
import eu.mhsl.craftattack.core.util.inventory.PlaceholderItems;
|
||||
import eu.mhsl.craftattack.core.util.world.InteractSounds;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class PackConfigurationInventory extends CachedApplianceSupplier<PackSelect> {
|
||||
private final PackConfiguration packConfiguration;
|
||||
private final Player inventoryOwner;
|
||||
private final Inventory inventory;
|
||||
|
||||
private final ItemStack reset = ItemBuilder.of(Material.BARRIER)
|
||||
.displayName(Component.text("Zurücksetzen", NamedTextColor.RED))
|
||||
.lore("Alle gewählten Texturepacks werden entfernt")
|
||||
.build();
|
||||
|
||||
private final ItemStack info = ItemBuilder.of(Material.OAK_HANGING_SIGN)
|
||||
.displayName(Component.text("Texturepacks", NamedTextColor.GOLD))
|
||||
.lore(
|
||||
"Wähle aus der oberen Liste eine oder mehrere Texturepacks aus. " +
|
||||
"Ändere die anzuwendende Reihenfolge durch Links/Rechtsklick in der unteren Liste. " +
|
||||
"Klicke auf Speichern um die Änderungen anzuwenden!"
|
||||
)
|
||||
.build();
|
||||
|
||||
private final ItemStack save = ItemBuilder.of(Material.GREEN_WOOL)
|
||||
.displayName(Component.text("Anwenden", NamedTextColor.GREEN))
|
||||
.lore("Die Ausgewählten Texturepacks werden in der entsprechenden Reihenfolge angewandt. Das Anwenden kann durch den Download je nach Internetgeschwindigkeit eine Weile dauern.")
|
||||
.build();
|
||||
|
||||
private final ItemStack unusedSlot = ItemBuilder.of(Material.LIME_STAINED_GLASS_PANE)
|
||||
.displayName(Component.text("Freier Slot", NamedTextColor.GREEN))
|
||||
.lore("Klicke auf ein Texturepack um es hinzuzufügen.")
|
||||
.build();
|
||||
|
||||
|
||||
public PackConfigurationInventory(PackConfiguration packConfiguration, Player inventoryOwner) {
|
||||
this.packConfiguration = packConfiguration;
|
||||
this.inventoryOwner = inventoryOwner;
|
||||
this.inventory = Bukkit.createInventory(null, 9 * 6, Component.text("Texturepacks"));
|
||||
this.draw();
|
||||
}
|
||||
|
||||
public Inventory getInventory() {
|
||||
return this.inventory;
|
||||
}
|
||||
|
||||
public void handleClick(@Nullable ItemStack clickedItem, int slot, ClickType clickType) {
|
||||
if(clickedItem == null) return;
|
||||
if(clickedItem.equals(this.reset)) this.reset();
|
||||
if(clickedItem.equals(this.save)) this.apply();
|
||||
|
||||
if(slot >= 9 && slot < 9 * 4) {
|
||||
this.getAppliance().availablePacks.findFromItem(clickedItem)
|
||||
.ifPresent(this::toggle);
|
||||
}
|
||||
|
||||
if(slot >= 9 * 5) {
|
||||
this.getAppliance().availablePacks.findFromItem(clickedItem)
|
||||
.ifPresent(pack -> {
|
||||
switch(clickType) {
|
||||
case RIGHT -> this.move(pack, true);
|
||||
case LEFT -> this.move(pack, false);
|
||||
case MIDDLE -> this.toggle(pack);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void move(PackConfiguration.Pack pack, boolean moveToRight) {
|
||||
List<PackConfiguration.Pack> packs = this.packConfiguration.getPackList();
|
||||
int index = packs.indexOf(pack);
|
||||
|
||||
if(index != -1) {
|
||||
int newIndex = moveToRight ? index + 1 : index - 1;
|
||||
if(newIndex >= 0 && newIndex < packs.size()) Collections.swap(packs, index, newIndex);
|
||||
}
|
||||
|
||||
InteractSounds.of(this.inventoryOwner).click();
|
||||
this.draw();
|
||||
}
|
||||
|
||||
private void toggle(PackConfiguration.Pack pack) {
|
||||
if(this.packConfiguration.getPackList().contains(pack)) {
|
||||
this.packConfiguration.getPackList().remove(pack);
|
||||
} else {
|
||||
this.packConfiguration.getPackList().add(pack);
|
||||
}
|
||||
|
||||
InteractSounds.of(this.inventoryOwner).click();
|
||||
this.draw();
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
this.packConfiguration.getPackList().clear();
|
||||
InteractSounds.of(this.inventoryOwner).delete();
|
||||
this.draw();
|
||||
}
|
||||
|
||||
private void apply() {
|
||||
this.inventoryOwner.closeInventory();
|
||||
InteractSounds.of(this.inventoryOwner).success();
|
||||
Bukkit.getScheduler().runTask(Main.instance(), () -> this.getAppliance().setPack(this.inventoryOwner, this.packConfiguration));
|
||||
}
|
||||
|
||||
private void draw() {
|
||||
this.inventory.clear();
|
||||
|
||||
this.inventory.setItem(0, this.packConfiguration.getPackList().isEmpty() ? PlaceholderItems.grayStainedGlassPane : this.reset);
|
||||
IteratorUtil.times(3, () -> this.inventory.addItem(PlaceholderItems.grayStainedGlassPane));
|
||||
this.inventory.setItem(4, this.info);
|
||||
IteratorUtil.times(3, () -> this.inventory.addItem(PlaceholderItems.grayStainedGlassPane));
|
||||
this.inventory.setItem(8, this.save);
|
||||
|
||||
IteratorUtil.iterateListInGlobal(
|
||||
9,
|
||||
this.getAppliance().availablePacks.packs().stream()
|
||||
.filter(pack -> !this.packConfiguration.getPackList().contains(pack))
|
||||
.limit(9 * 3)
|
||||
.map(pack -> {
|
||||
ItemBuilder stack = ItemBuilder.of(pack.buildItem());
|
||||
if(this.packConfiguration.getPackList().contains(pack)) stack.glint();
|
||||
return stack.build();
|
||||
})
|
||||
.toList(),
|
||||
this.inventory::setItem
|
||||
);
|
||||
|
||||
IntStream.range(9 * 4, 9 * 5)
|
||||
.forEach(slot -> this.inventory.setItem(slot, PlaceholderItems.grayStainedGlassPane));
|
||||
|
||||
IteratorUtil.iterateListInGlobal(
|
||||
9 * 5,
|
||||
IteratorUtil.expandList(
|
||||
IntStream.range(0, Math.min(this.packConfiguration.getPackList().size(), 9))
|
||||
.mapToObj(i -> {
|
||||
PackConfiguration.Pack pack = this.packConfiguration.getPackList().get(i);
|
||||
ItemBuilder builder = ItemBuilder.of(pack.buildItem());
|
||||
|
||||
builder
|
||||
.displayName(existing -> Component.text(String.format("#%s ", i + 1), NamedTextColor.LIGHT_PURPLE).append(existing))
|
||||
.appendLore(Component.text("➡ Rechtsklick um nach Rechts zu verschieben", NamedTextColor.AQUA))
|
||||
.appendLore(Component.text("⬅ Linksklick um nach Links zu verschieben", NamedTextColor.AQUA))
|
||||
.appendLore(Component.text("\uD83D\uDDD1 Mausradklick um zu entfernen", NamedTextColor.AQUA));
|
||||
return builder.build();
|
||||
})
|
||||
.toList(),
|
||||
9,
|
||||
this.unusedSlot
|
||||
),
|
||||
this.inventory::setItem
|
||||
);
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,139 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.packSelect;
|
||||
|
||||
import eu.mhsl.craftattack.core.Main;
|
||||
import eu.mhsl.craftattack.core.appliance.Appliance;
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceCommand;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.packSelect.listeners.ClickPackInventoryListener;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.packSelect.listeners.ClosePackInventoryListener;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.packSelect.listeners.SetPacksOnJoinListener;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
|
||||
import net.kyori.adventure.resource.ResourcePackInfo;
|
||||
import net.kyori.adventure.resource.ResourcePackRequest;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.persistence.PersistentDataContainer;
|
||||
import org.bukkit.persistence.PersistentDataType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.*;
|
||||
|
||||
public class PackSelect extends Appliance {
|
||||
private static final NamespacedKey packKey = new NamespacedKey(Main.instance(), PackSelect.class.getName().toLowerCase(Locale.ROOT));
|
||||
|
||||
public final PackConfiguration.PackList availablePacks = new PackConfiguration.PackList(new ArrayList<>());
|
||||
public final Map<Player, PackConfigurationInventory> openInventories = new WeakHashMap<>();
|
||||
|
||||
public PackSelect() {
|
||||
super("packselect");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
Settings.instance().declareSetting(PackSelectSetting.class);
|
||||
|
||||
List<Map<?, ?>> packs = this.localConfig().getMapList("packs");
|
||||
Bukkit.getScheduler().runTaskAsynchronously(
|
||||
Main.instance(),
|
||||
() -> packs.stream()
|
||||
.flatMap(pack -> pack.entrySet().stream())
|
||||
.forEach(pack -> {
|
||||
@SuppressWarnings("unchecked") Map<String, String> packData = (Map<String, String>) pack.getValue();
|
||||
|
||||
try {
|
||||
ResourcePackInfo resourcePackInfo = ResourcePackInfoFactory
|
||||
.createResourcePackInfo(URI.create(packData.get("url")), packData.get("hash"))
|
||||
.join();
|
||||
|
||||
PackConfiguration.Pack packToAdd = new PackConfiguration.Pack(
|
||||
UUID.nameUUIDFromBytes(pack.getKey().toString().getBytes()),
|
||||
packData.get("name"),
|
||||
packData.get("description"),
|
||||
packData.get("author"),
|
||||
resourcePackInfo,
|
||||
packData.get("icon")
|
||||
);
|
||||
this.availablePacks.packs().add(packToAdd);
|
||||
} catch(Exception e) {
|
||||
Main.logger().warning(String.format("Failed to add pack %s: %s", packData.get("name"), e.getMessage()));
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public PackConfiguration getPackConfigurationForPlayer(Player player) {
|
||||
PersistentDataContainer persistentDataContainer = player.getPersistentDataContainer();
|
||||
try {
|
||||
String serialized = persistentDataContainer.get(packKey, PersistentDataType.STRING);
|
||||
Objects.requireNonNull(serialized);
|
||||
return PackConfiguration.deserialize(serialized);
|
||||
} catch(IllegalArgumentException | NullPointerException exception) {
|
||||
return PackConfiguration.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public void openPackInventory(Player player) {
|
||||
PackConfigurationInventory packInventory = new PackConfigurationInventory(this.getPackConfigurationForPlayer(player), player);
|
||||
player.openInventory(packInventory.getInventory());
|
||||
this.openInventories.put(player, packInventory);
|
||||
}
|
||||
|
||||
public void setPack(Player player, PackConfiguration packConfiguration) {
|
||||
player.getPersistentDataContainer().set(packKey, PersistentDataType.STRING, packConfiguration.serialize());
|
||||
|
||||
int packCount = packConfiguration.getPackList().size();
|
||||
if(packCount > 0) {
|
||||
player.sendMessage(
|
||||
Component.text(
|
||||
String.format("%s heruntergeladen und hinzugefügt...", packCount > 1 ? "Texturenpakete werden" : "Texturenpaket wird"),
|
||||
NamedTextColor.DARK_GREEN
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
player.sendResourcePacks(
|
||||
ResourcePackRequest.resourcePackRequest()
|
||||
.packs(
|
||||
packConfiguration.getPackList().stream()
|
||||
.map(PackConfiguration.Pack::info)
|
||||
.toList()
|
||||
.reversed()
|
||||
)
|
||||
.replace(true)
|
||||
.required(true)
|
||||
.prompt(
|
||||
Component.text()
|
||||
.append(Component.text("Bestätige um fortzufahren! Du kannst deine Entscheidung jederzeit mit ", NamedTextColor.GRAY))
|
||||
.append(Component.text(String.format("/%s ", ChangePackCommand.commandName), NamedTextColor.GOLD))
|
||||
.append(Component.text("ändern.", NamedTextColor.GRAY))
|
||||
.build()
|
||||
)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
public boolean isNotPackInventory(Player player, Inventory inventory) {
|
||||
PackConfigurationInventory packConfigurationInventory = this.openInventories.get(player);
|
||||
if(packConfigurationInventory == null) return true;
|
||||
return packConfigurationInventory.getInventory() != inventory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull List<ApplianceCommand<?>> commands() {
|
||||
return List.of(new ChangePackCommand());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull List<Listener> listeners() {
|
||||
return List.of(
|
||||
new ClosePackInventoryListener(),
|
||||
new ClickPackInventoryListener(),
|
||||
new SetPacksOnJoinListener()
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.packSelect;
|
||||
|
||||
import eu.mhsl.craftattack.core.Main;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.CategorizedSetting;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.SettingCategory;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.datatypes.ActionSetting;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
|
||||
public class PackSelectSetting extends ActionSetting implements CategorizedSetting {
|
||||
@Override
|
||||
protected String title() {
|
||||
return "Texturepacks";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String description() {
|
||||
return "Stelle dein persönliches Texturepack aus einer kuratierten Auswahl zusammen und gestalte deine Spielerfahrung individuell.";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Material icon() {
|
||||
return Material.PAINTING;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAction(Player player, ClickType clickType) {
|
||||
Main.instance().getAppliance(PackSelect.class).openPackInventory(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SettingCategory category() {
|
||||
return SettingCategory.Visuals;
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.packSelect;
|
||||
|
||||
import eu.mhsl.craftattack.core.Main;
|
||||
import net.kyori.adventure.resource.ResourcePackInfo;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URI;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
class ResourcePackInfoFactory {
|
||||
|
||||
private static boolean isValidHash(@Nullable String hash) {
|
||||
return hash != null && hash.length() == 40;
|
||||
}
|
||||
|
||||
public static @NotNull CompletableFuture<ResourcePackInfo> createResourcePackInfo(@NotNull URI resourcePackUrl, @Nullable String hash) {
|
||||
if(isValidHash(hash)) {
|
||||
return CompletableFuture.completedFuture(
|
||||
ResourcePackInfo.resourcePackInfo(UUID.nameUUIDFromBytes(hash.getBytes()), resourcePackUrl, hash)
|
||||
);
|
||||
}
|
||||
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
Main.logger().info(String.format("Start calculating SHA1 Hash of %s", resourcePackUrl));
|
||||
HttpURLConnection connection = (HttpURLConnection) resourcePackUrl.toURL().openConnection();
|
||||
connection.setRequestMethod("GET");
|
||||
connection.connect();
|
||||
|
||||
try(InputStream inputStream = connection.getInputStream()) {
|
||||
MessageDigest sha1Digest = MessageDigest.getInstance("SHA-1");
|
||||
byte[] buffer = new byte[1024];
|
||||
int bytesRead;
|
||||
while((bytesRead = inputStream.read(buffer)) != -1) {
|
||||
sha1Digest.update(buffer, 0, bytesRead);
|
||||
}
|
||||
String sha1Hex = bytesToHex(sha1Digest.digest());
|
||||
|
||||
Main.logger().info(String.format("Calculating SHA1 Hash of %s completed: %s", resourcePackUrl, sha1Hex));
|
||||
return ResourcePackInfo.resourcePackInfo(UUID.nameUUIDFromBytes(sha1Hex.getBytes()), resourcePackUrl, sha1Hex);
|
||||
}
|
||||
} catch(Exception e) {
|
||||
String error = String.format("Error whilst SHA1 calculation of %s: %s", resourcePackUrl, e.getMessage());
|
||||
Main.logger().warning(error);
|
||||
throw new RuntimeException(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static String bytesToHex(byte[] bytes) {
|
||||
StringBuilder hexString = new StringBuilder(bytes.length * 2);
|
||||
for(byte b : bytes) {
|
||||
hexString.append(String.format("%02x", b));
|
||||
}
|
||||
return hexString.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.packSelect.listeners;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceListener;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.packSelect.PackSelect;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
|
||||
public class ClickPackInventoryListener extends ApplianceListener<PackSelect> {
|
||||
@EventHandler
|
||||
public void interact(InventoryClickEvent event) {
|
||||
if(!(event.getWhoClicked() instanceof Player player)) return;
|
||||
if(this.getAppliance().isNotPackInventory(player, event.getInventory())) return;
|
||||
event.setCancelled(true);
|
||||
|
||||
this.getAppliance().openInventories.get(player).handleClick(
|
||||
event.getCurrentItem(),
|
||||
event.getSlot(),
|
||||
event.getClick()
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.packSelect.listeners;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceListener;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.packSelect.PackSelect;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.inventory.InventoryCloseEvent;
|
||||
|
||||
public class ClosePackInventoryListener extends ApplianceListener<PackSelect> {
|
||||
@EventHandler
|
||||
public void onClose(InventoryCloseEvent event) {
|
||||
if(!(event.getPlayer() instanceof Player player)) return;
|
||||
if(this.getAppliance().isNotPackInventory(player, event.getInventory())) return;
|
||||
this.getAppliance().openInventories.remove(player);
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.packSelect.listeners;
|
||||
|
||||
import eu.mhsl.craftattack.core.Main;
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceListener;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.packSelect.PackSelect;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
|
||||
public class SetPacksOnJoinListener extends ApplianceListener<PackSelect> {
|
||||
@EventHandler
|
||||
public void onJoin(PlayerJoinEvent event) {
|
||||
Bukkit.getScheduler().runTask(
|
||||
Main.instance(),
|
||||
() -> this.getAppliance().setPack(event.getPlayer(), this.getAppliance().getPackConfigurationForPlayer(event.getPlayer()))
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.playtime;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.Appliance;
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceCommand;
|
||||
import eu.mhsl.craftattack.core.util.text.DataSizeConverter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.util.Ticks;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.Statistic;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class Playtime extends Appliance {
|
||||
public Component getFormattedPlaytime(OfflinePlayer player) {
|
||||
int playtimeInTicks = player.getStatistic(Statistic.PLAY_ONE_MINUTE);
|
||||
String playtime = DataSizeConverter.formatSecondsToHumanReadable(playtimeInTicks / Ticks.TICKS_PER_SECOND);
|
||||
return Component.text()
|
||||
.append(Component.text("Der Spieler ", NamedTextColor.GRAY))
|
||||
.append(Component.text(Objects.requireNonNull(player.getName())))
|
||||
.append(Component.text(" hat eine Spielzeit von ", NamedTextColor.GRAY))
|
||||
.append(Component.text(playtime, NamedTextColor.GOLD))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull List<ApplianceCommand<?>> commands() {
|
||||
return List.of(new PlaytimeCommand());
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.playtime;
|
||||
|
||||
import eu.mhsl.craftattack.core.Main;
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceCommand;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
class PlaytimeCommand extends ApplianceCommand.PlayerChecked<Playtime> {
|
||||
public PlaytimeCommand() {
|
||||
super("playtime");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
|
||||
String playerName = args.length == 1 ? args[0] : this.getPlayer().getName();
|
||||
|
||||
Bukkit.getScheduler().runTaskAsynchronously(Main.instance(), () -> {
|
||||
OfflinePlayer player = Bukkit.getOfflinePlayer(playerName);
|
||||
if(!player.hasPlayedBefore()) {
|
||||
sender.sendMessage(Component.text("Der Spieler existiert nicht!", NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
sender.sendMessage(this.getAppliance().getFormattedPlaytime(player));
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,177 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.report;
|
||||
|
||||
import eu.mhsl.craftattack.core.Main;
|
||||
import eu.mhsl.craftattack.core.api.client.ReqResp;
|
||||
import eu.mhsl.craftattack.core.api.client.repositories.ReportRepository;
|
||||
import eu.mhsl.craftattack.core.appliance.Appliance;
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceCommand;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.ComponentBuilder;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.event.HoverEvent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class Report extends Appliance {
|
||||
public static Component helpText() {
|
||||
return Component.text()
|
||||
.appendNewline()
|
||||
.append(Component.text(" Um einen Spieler zu melden, verwende ", NamedTextColor.GRAY)).appendNewline()
|
||||
.append(Component.text("/report", NamedTextColor.GOLD)).appendNewline()
|
||||
.append(Component.text("oder", NamedTextColor.GRAY)).appendNewline()
|
||||
.append(Component.text("/report <spieler> [grund]", NamedTextColor.GOLD)).appendNewline()
|
||||
.build();
|
||||
}
|
||||
|
||||
public Report() {
|
||||
super("report");
|
||||
}
|
||||
|
||||
public void reportToUnknown(@NotNull Player issuer) {
|
||||
ReportRepository.ReportCreationInfo request = new ReportRepository.ReportCreationInfo(issuer.getUniqueId(), null, "");
|
||||
Bukkit.getScheduler().runTaskAsynchronously(
|
||||
Main.instance(),
|
||||
() -> this.createReport(issuer, request)
|
||||
);
|
||||
}
|
||||
|
||||
public void reportToKnown(@NotNull Player issuer, @NotNull String targetUsername, @Nullable String reason) {
|
||||
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(targetUsername);
|
||||
if(issuer.getUniqueId().equals(offlinePlayer.getUniqueId())) {
|
||||
issuer.sendMessage(Component.text("Du kannst dich nicht selbst reporten.", NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
|
||||
ReportRepository.ReportCreationInfo request = new ReportRepository.ReportCreationInfo(
|
||||
issuer.getUniqueId(),
|
||||
offlinePlayer.getUniqueId(),
|
||||
Optional.ofNullable(reason).orElse("")
|
||||
);
|
||||
Bukkit.getScheduler().runTaskAsynchronously(
|
||||
Main.instance(),
|
||||
() -> this.createReport(issuer, request)
|
||||
);
|
||||
}
|
||||
|
||||
private void createReport(Player issuer, ReportRepository.ReportCreationInfo reportRequest) {
|
||||
ReqResp<ReportRepository.ReportUrl> createdReport = this.queryRepository(ReportRepository.class)
|
||||
.createReport(reportRequest);
|
||||
|
||||
switch(createdReport.status()) {
|
||||
case 201:
|
||||
issuer.sendMessage(
|
||||
Component.text()
|
||||
.append(Component.text("\\/".repeat(20), NamedTextColor.DARK_GRAY))
|
||||
.appendNewline()
|
||||
.append(Component.text("⚠ Der Report muss über den folgenden Link fertiggestellt werden!", NamedTextColor.GOLD))
|
||||
.appendNewline()
|
||||
.appendNewline()
|
||||
.append(
|
||||
Component
|
||||
.text(createdReport.data().url(), NamedTextColor.GRAY) // URL mit Weltkugel-Emoji
|
||||
.clickEvent(ClickEvent.clickEvent(ClickEvent.Action.OPEN_URL, createdReport.data().url()))
|
||||
)
|
||||
.appendNewline()
|
||||
.appendNewline()
|
||||
.append(Component.text("Ohne das Fertigstellen des Reports wird dieser nicht bearbeitet!", NamedTextColor.DARK_RED))
|
||||
.appendNewline()
|
||||
.append(Component.text("/\\".repeat(20), NamedTextColor.DARK_GRAY))
|
||||
);
|
||||
break;
|
||||
|
||||
case 400:
|
||||
issuer.sendMessage(
|
||||
Component.text()
|
||||
.append(Component.text("Der angegebene Nutzer ist in unserem System nicht bekannt.", NamedTextColor.RED))
|
||||
.appendNewline()
|
||||
.append(Component.text("Bist du sicher, dass du den Namen richtig geschrieben hast?", NamedTextColor.RED))
|
||||
.appendNewline()
|
||||
.append(Component.text("Du kannst dich alternativ jederzeit bei einem Admin melden.", NamedTextColor.GRAY))
|
||||
);
|
||||
break;
|
||||
|
||||
case 401:
|
||||
default:
|
||||
Main.logger().warning("Failed to request Report: " + createdReport.status());
|
||||
issuer.sendMessage(
|
||||
Component.text()
|
||||
.append(Component.text("Interner Serverfehler beim anlegen des Reports.", NamedTextColor.RED))
|
||||
.appendNewline()
|
||||
.append(Component.text("Bitte melde dich bei einem Admin!", NamedTextColor.RED))
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void queryReports(Player issuer) {
|
||||
ReqResp<ReportRepository.PlayerReports> userReports = this.queryRepository(ReportRepository.class)
|
||||
.queryReports(issuer.getUniqueId());
|
||||
|
||||
if(userReports.status() != 200) {
|
||||
Main.logger().warning("Failed to request Reports: " + userReports.status());
|
||||
issuer.sendMessage(
|
||||
Component.text()
|
||||
.append(Component.text("Interner Serverfehler beim abfragen der Reports.", NamedTextColor.RED))
|
||||
.appendNewline()
|
||||
.append(Component.text("Bitte melde dich bei einem Admin!", NamedTextColor.RED))
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
List<ReportRepository.PlayerReports.Report> reports = userReports
|
||||
.data()
|
||||
.from_self()
|
||||
.stream()
|
||||
.filter(report -> !report.draft())
|
||||
.toList()
|
||||
.reversed();
|
||||
|
||||
if(reports.isEmpty()) {
|
||||
issuer.sendMessage(
|
||||
Component.text()
|
||||
.append(Component.text("Du hast noch niemanden reportet.", NamedTextColor.RED))
|
||||
.appendNewline()
|
||||
.append(Component.text("Um jemanden zu melden, nutze /report", NamedTextColor.GRAY))
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
ComponentBuilder<TextComponent, TextComponent.Builder> component = Component.text()
|
||||
.append(Component.newline())
|
||||
.append(Component.text("Von dir erstellte Reports: ", NamedTextColor.GOLD))
|
||||
.appendNewline();
|
||||
|
||||
reports.forEach(report -> {
|
||||
component
|
||||
.append(Component.text(" - ", NamedTextColor.WHITE))
|
||||
.append(
|
||||
report.reported() != null
|
||||
? Component.text(report.reported().username(), NamedTextColor.WHITE)
|
||||
: Component.text("Unbekannt", NamedTextColor.YELLOW)
|
||||
)
|
||||
.append(Component.text(String.format(": %s", report.subject()), NamedTextColor.GRAY))
|
||||
.clickEvent(ClickEvent.openUrl(report.url()))
|
||||
.hoverEvent(HoverEvent.showText(Component.text("Klicke, um den Report einzusehen.", NamedTextColor.GOLD)));
|
||||
component.appendNewline();
|
||||
});
|
||||
|
||||
issuer.sendMessage(component.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
protected List<ApplianceCommand<?>> commands() {
|
||||
return List.of(
|
||||
new ReportCommand(),
|
||||
new ReportsCommand()
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.report;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceCommand;
|
||||
import eu.mhsl.craftattack.core.util.text.ComponentUtil;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
class ReportCommand extends ApplianceCommand.PlayerChecked<Report> {
|
||||
public ReportCommand() {
|
||||
super("report");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
sender.sendMessage(ComponentUtil.pleaseWait());
|
||||
|
||||
if(args.length == 0) {
|
||||
this.getAppliance().reportToUnknown(this.getPlayer());
|
||||
}
|
||||
|
||||
if(args.length == 1) {
|
||||
this.getAppliance().reportToKnown(this.getPlayer(), args[0], null);
|
||||
}
|
||||
|
||||
if(args.length > 1) {
|
||||
this.getAppliance().reportToKnown(this.getPlayer(), args[0], Arrays.stream(args).skip(1).collect(Collectors.joining(" ")));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
List<String> response = new ArrayList<>();
|
||||
|
||||
if(args.length == 1) {
|
||||
response = Stream.concat(
|
||||
Bukkit.getOnlinePlayers().stream().map(Player::getName),
|
||||
Arrays.stream(Bukkit.getOfflinePlayers()).map(OfflinePlayer::getName)
|
||||
).toList();
|
||||
}
|
||||
|
||||
if(args.length == 2) {
|
||||
response = List.of(
|
||||
"Griefing",
|
||||
"Diebstahl",
|
||||
"Beleidigung",
|
||||
"Hacking",
|
||||
"Andere Regelverstöße",
|
||||
" "
|
||||
);
|
||||
}
|
||||
|
||||
return super.tabCompleteReducer(response, args);
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.report;
|
||||
|
||||
import eu.mhsl.craftattack.core.Main;
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceCommand;
|
||||
import eu.mhsl.craftattack.core.util.text.ComponentUtil;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
class ReportsCommand extends ApplianceCommand.PlayerChecked<Report> {
|
||||
public ReportsCommand() {
|
||||
super("reports");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
sender.sendMessage(ComponentUtil.pleaseWait());
|
||||
Bukkit.getScheduler().runTaskAsynchronously(
|
||||
Main.instance(),
|
||||
() -> this.getAppliance().queryReports(this.getPlayer())
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.sleepTag;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceListener;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.player.PlayerBedEnterEvent;
|
||||
import org.bukkit.event.player.PlayerBedLeaveEvent;
|
||||
|
||||
class SleepStateChangeListener extends ApplianceListener<SleepTag> {
|
||||
@EventHandler
|
||||
public void onBedEnter(PlayerBedEnterEvent event) {
|
||||
if(!event.getBedEnterResult().equals(PlayerBedEnterEvent.BedEnterResult.OK)) return;
|
||||
this.getAppliance().updateSleeping(event.getPlayer(), true);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onBedLeave(PlayerBedLeaveEvent event) {
|
||||
this.getAppliance().updateSleeping(event.getPlayer(), false);
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.sleepTag;
|
||||
|
||||
import eu.mhsl.craftattack.core.Main;
|
||||
import eu.mhsl.craftattack.core.appliance.Appliance;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.displayName.DisplayName;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.HoverEvent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.util.Ticks;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class SleepTag extends Appliance implements DisplayName.Prefixed {
|
||||
private final Set<Player> sleepingPlayers = new HashSet<>();
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
Bukkit.getScheduler().runTaskTimerAsynchronously(
|
||||
Main.instance(),
|
||||
this::cleanup,
|
||||
Ticks.TICKS_PER_SECOND * 60,
|
||||
Ticks.TICKS_PER_SECOND * 60
|
||||
);
|
||||
}
|
||||
|
||||
public void updateSleeping(Player player, boolean isSleeping) {
|
||||
if(isSleeping) {
|
||||
this.sleepingPlayers.add(player);
|
||||
} else {
|
||||
this.sleepingPlayers.remove(player);
|
||||
}
|
||||
this.updateDisplayName(player);
|
||||
}
|
||||
|
||||
private void updateDisplayName(Player player) {
|
||||
Main.instance().getAppliance(DisplayName.class).update(player);
|
||||
}
|
||||
|
||||
private void cleanup() {
|
||||
List<Player> invalidEntries = this.sleepingPlayers.stream()
|
||||
.filter(player -> !player.isConnected())
|
||||
.filter(player -> !player.isSleeping())
|
||||
.toList();
|
||||
|
||||
invalidEntries.forEach(this.sleepingPlayers::remove);
|
||||
invalidEntries.forEach(this::updateDisplayName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Component getNamePrefix(Player player) {
|
||||
if(this.sleepingPlayers.contains(player))
|
||||
return Component.text("[\uD83D\uDCA4]", NamedTextColor.GRAY)
|
||||
.hoverEvent(HoverEvent.showText(Component.text("Der Spieler liegt in einem Bett")));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull List<Listener> listeners() {
|
||||
return List.of(new SleepStateChangeListener());
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.tablist;
|
||||
|
||||
import eu.mhsl.craftattack.core.Main;
|
||||
import eu.mhsl.craftattack.core.appliance.Appliance;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.report.Report;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
|
||||
import eu.mhsl.craftattack.core.util.IteratorUtil;
|
||||
import eu.mhsl.craftattack.core.util.statistics.NetworkMonitor;
|
||||
import eu.mhsl.craftattack.core.util.text.ComponentUtil;
|
||||
import eu.mhsl.craftattack.core.util.text.RainbowComponent;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.util.Ticks;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.OperatingSystemMXBean;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class Tablist extends Appliance {
|
||||
private final RainbowComponent serverName = new RainbowComponent(" CraftAttack 7 ", 7, 3);
|
||||
private NetworkMonitor networkMonitor;
|
||||
private OperatingSystemMXBean systemMonitor;
|
||||
|
||||
public Tablist() {
|
||||
super("tablist");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
Settings.instance().declareSetting(TechnicalTablistSetting.class);
|
||||
|
||||
int tabRefreshRate = 3;
|
||||
this.networkMonitor = new NetworkMonitor(this.localConfig().getString("interface"), Duration.ofSeconds(1));
|
||||
this.systemMonitor = ManagementFactory.getOperatingSystemMXBean();
|
||||
|
||||
Bukkit.getScheduler().runTaskTimerAsynchronously(
|
||||
Main.instance(),
|
||||
() -> IteratorUtil.onlinePlayers(this::updateHeader),
|
||||
tabRefreshRate * Ticks.TICKS_PER_SECOND,
|
||||
tabRefreshRate * Ticks.TICKS_PER_SECOND
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
this.networkMonitor.stop();
|
||||
}
|
||||
|
||||
public void fullUpdate(Player player) {
|
||||
this.updateHeader(player);
|
||||
this.updateFooter(player);
|
||||
}
|
||||
|
||||
private void updateHeader(Player player) {
|
||||
boolean detailedInfo = this.queryAppliance(Settings.class).getSetting(player, Settings.Key.TechnicalTab, Boolean.class);
|
||||
Component header = Component.newline()
|
||||
.append(this.serverName.getRainbowState()).appendNewline()
|
||||
.append(Component.text("mhsl.eu", NamedTextColor.GOLD)).appendNewline().appendNewline()
|
||||
.append(ComponentUtil.getFormattedTickTimes(detailedInfo)).appendNewline();
|
||||
|
||||
if(detailedInfo) {
|
||||
header = header
|
||||
.appendNewline()
|
||||
.append(ComponentUtil.getFormattedPing(player)).appendNewline()
|
||||
.append(ComponentUtil.getFormattedNetworkStats(
|
||||
this.networkMonitor.getTraffic(),
|
||||
this.networkMonitor.getPackets()
|
||||
)).appendNewline()
|
||||
.append(ComponentUtil.getFormattedSystemStats(this.systemMonitor)).appendNewline();
|
||||
}
|
||||
|
||||
player.sendPlayerListHeader(header);
|
||||
}
|
||||
|
||||
private void updateFooter(Player player) {
|
||||
player.sendPlayerListFooter(Report.helpText());
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
protected List<Listener> listeners() {
|
||||
return List.of(new TablistListener());
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.tablist;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceListener;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
|
||||
class TablistListener extends ApplianceListener<Tablist> {
|
||||
@EventHandler
|
||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||
this.getAppliance().fullUpdate(event.getPlayer());
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.tablist;
|
||||
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.CategorizedSetting;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.SettingCategory;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.Settings;
|
||||
import eu.mhsl.craftattack.spawn.common.appliances.metaGameplay.settings.datatypes.BoolSetting;
|
||||
import org.bukkit.Material;
|
||||
|
||||
public class TechnicalTablistSetting extends BoolSetting implements CategorizedSetting {
|
||||
public TechnicalTablistSetting() {
|
||||
super(Settings.Key.TechnicalTab);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String title() {
|
||||
return "Technische Informationen";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String description() {
|
||||
return "Zeige erweiterte Informationen und Statistiken in der Tabliste an";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Material icon() {
|
||||
return Material.COMMAND_BLOCK_MINECART;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean defaultValue() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SettingCategory category() {
|
||||
return SettingCategory.Misc;
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.worldmuseum;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceCommand;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
class MoveWorldMuseumVillagerCommand extends ApplianceCommand.PlayerChecked<WorldMuseum> {
|
||||
public MoveWorldMuseumVillagerCommand() {
|
||||
super("moveWorldMuseumVillager");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
this.getAppliance().updateVillagerPosition(this.getPlayer().getLocation());
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.worldmuseum;
|
||||
|
||||
import eu.mhsl.craftattack.core.Main;
|
||||
import eu.mhsl.craftattack.core.appliance.Appliance;
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceCommand;
|
||||
import eu.mhsl.craftattack.core.util.entity.DisplayVillager;
|
||||
import eu.mhsl.craftattack.core.util.listener.DismissInventoryOpenFromHolder;
|
||||
import eu.mhsl.craftattack.core.util.listener.PlayerInteractAtEntityEventListener;
|
||||
import eu.mhsl.craftattack.core.util.server.PluginMessage;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.Villager;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class WorldMuseum extends Appliance {
|
||||
public DisplayVillager.ConfigBound villager;
|
||||
|
||||
public WorldMuseum() {
|
||||
super("worldMuseum");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
this.villager = new DisplayVillager.ConfigBound(
|
||||
this.localConfig(),
|
||||
villager -> {
|
||||
villager.customName(Component.text("Museum der Welten").color(NamedTextColor.GOLD));
|
||||
villager.setProfession(Villager.Profession.CARTOGRAPHER);
|
||||
villager.setVillagerType(Villager.Type.SNOW);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public void updateVillagerPosition(Location location) {
|
||||
this.villager.updateLocation(location);
|
||||
}
|
||||
|
||||
public void handleVillagerInteraction(Player player) {
|
||||
Main.logger().info("Sending" + player.getName() + " to WorldMuseum");
|
||||
PluginMessage.connect(player, this.localConfig().getString("connect-server-name"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
protected List<ApplianceCommand<?>> commands() {
|
||||
return List.of(new MoveWorldMuseumVillagerCommand());
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
protected List<Listener> listeners() {
|
||||
return List.of(
|
||||
new PlayerInteractAtEntityEventListener(this.villager.getUniqueId(), playerInteractAtEntityEvent -> this.handleVillagerInteraction(playerInteractAtEntityEvent.getPlayer())),
|
||||
new DismissInventoryOpenFromHolder(this.villager.getUniqueId())
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.yearRank;
|
||||
|
||||
import eu.mhsl.craftattack.core.Main;
|
||||
import eu.mhsl.craftattack.core.appliance.Appliance;
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceCommand;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.displayName.DisplayName;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.event.HoverEvent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
|
||||
public class YearRank extends Appliance implements DisplayName.Prefixed {
|
||||
record CraftAttackYear(String name) {
|
||||
}
|
||||
|
||||
private final Map<UUID, List<CraftAttackYear>> rankMap = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
File folder = new File(Main.instance().getDataFolder(), "yearRank");
|
||||
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
folder.mkdirs();
|
||||
|
||||
Optional<File[]> dataFolders = Optional.ofNullable(folder.listFiles());
|
||||
if(dataFolders.isEmpty()) return;
|
||||
|
||||
List.of(dataFolders.get()).forEach(playerDataFolder -> {
|
||||
Optional<File[]> datFiles = Optional.ofNullable(playerDataFolder.listFiles());
|
||||
if(datFiles.isEmpty()) return;
|
||||
|
||||
CraftAttackYear craftAttackYear = new CraftAttackYear(playerDataFolder.getName());
|
||||
|
||||
Arrays.stream(datFiles.get())
|
||||
.map(file -> file.getName().split("\\.")[0])
|
||||
.distinct()
|
||||
.map(UUID::fromString)
|
||||
.peek(uuid -> this.rankMap.computeIfAbsent(uuid, p -> new ArrayList<>()))
|
||||
.forEach(uuid -> this.rankMap.get(uuid).add(craftAttackYear));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Component getNamePrefix(Player player) {
|
||||
if(!this.rankMap.containsKey(player.getUniqueId())) return null;
|
||||
int yearCount = this.rankMap.get(player.getUniqueId()).size();
|
||||
if(yearCount <= 3) return null;
|
||||
|
||||
return Component.text()
|
||||
.append(Component.text("[\uD83C\uDF1F]", NamedTextColor.GOLD))
|
||||
.hoverEvent(HoverEvent.showText(
|
||||
Component.text(String.format("Langzeitspieler: %s ist bereits seit %d Jahren dabei!", player.getName(), yearCount))
|
||||
))
|
||||
.build();
|
||||
}
|
||||
|
||||
public Component listYearRanks() {
|
||||
TextComponent.Builder builder = Component.text();
|
||||
builder.append(Component.text("Top 30 Spieler: ", NamedTextColor.GOLD));
|
||||
this.rankMap.keySet().stream()
|
||||
.map(uuid -> Map.entry(uuid, this.rankMap.get(uuid).size()))
|
||||
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
|
||||
.limit(30)
|
||||
.forEach(entry -> builder
|
||||
.appendNewline()
|
||||
.append(Component.text(entry.getKey().toString(), NamedTextColor.GRAY)
|
||||
.hoverEvent(HoverEvent.showText(Component.text(entry.getKey().toString())))
|
||||
.clickEvent(ClickEvent.copyToClipboard(entry.getKey().toString())))
|
||||
.append(Component.text(": "))
|
||||
.append(Component.text(entry.getValue(), NamedTextColor.GOLD)));
|
||||
|
||||
builder
|
||||
.appendNewline()
|
||||
.appendNewline()
|
||||
.append(Component.text("Übersischt:", NamedTextColor.GOLD));
|
||||
|
||||
this.rankMap.values().stream()
|
||||
.mapMulti(Iterable::forEach)
|
||||
.filter(o -> o instanceof CraftAttackYear)
|
||||
.map(o -> (CraftAttackYear) o)
|
||||
.distinct()
|
||||
.forEach(craftAttackYear -> builder
|
||||
.appendNewline()
|
||||
.append(Component.text(craftAttackYear.name, NamedTextColor.GRAY))
|
||||
.append(Component.text(": "))
|
||||
.append(Component.text(this.rankMap.keySet().stream()
|
||||
.filter(uuid -> this.rankMap.get(uuid).contains(craftAttackYear)).count())));
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull List<ApplianceCommand<?>> commands() {
|
||||
return List.of(new YearRankCommand());
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.yearRank;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceCommand;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
class YearRankCommand extends ApplianceCommand<YearRank> {
|
||||
public YearRankCommand() {
|
||||
super("yearRank");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
|
||||
sender.sendMessage(this.getAppliance().listYearRanks());
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.tooling.lightningFireControl;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.Appliance;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class LightningFireControl extends Appliance {
|
||||
Set<Block> frozenFireBlocks = new HashSet<>();
|
||||
|
||||
@Override
|
||||
protected @NotNull List<Listener> listeners() {
|
||||
return List.of(new LightningFireListener());
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.tooling.lightningFireControl;
|
||||
|
||||
import eu.mhsl.craftattack.core.Main;
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceListener;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.block.BlockBurnEvent;
|
||||
import org.bukkit.event.block.BlockIgniteEvent;
|
||||
import org.bukkit.event.block.BlockSpreadEvent;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
class LightningFireListener extends ApplianceListener<LightningFireControl> {
|
||||
private final Random rnd = new Random();
|
||||
|
||||
@EventHandler
|
||||
public void onBlockIgnite(BlockIgniteEvent event) {
|
||||
if(!event.getCause().equals(BlockIgniteEvent.IgniteCause.LIGHTNING)) return;
|
||||
Block block = event.getBlock();
|
||||
|
||||
this.getAppliance().frozenFireBlocks.add(block);
|
||||
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Runnable cancel = () -> {
|
||||
LightningFireListener.this.getAppliance().frozenFireBlocks.remove(block);
|
||||
this.cancel();
|
||||
};
|
||||
|
||||
if (block.getType() != Material.FIRE) {
|
||||
cancel.run();
|
||||
return;
|
||||
}
|
||||
|
||||
if (LightningFireListener.this.rnd.nextInt(100) < 30) {
|
||||
block.setType(Material.AIR);
|
||||
cancel.run();
|
||||
}
|
||||
}
|
||||
}.runTaskTimer(Main.instance(), 20L, 20L);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onFireSpread(BlockSpreadEvent event) {
|
||||
Block source = event.getSource();
|
||||
if(!source.getType().equals(Material.FIRE)) return;
|
||||
if(source.equals(event.getBlock())) return;
|
||||
if(!this.getAppliance().frozenFireBlocks.contains(source)) return;
|
||||
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onBlockBurned(BlockBurnEvent event) {
|
||||
if(!this.getAppliance().frozenFireBlocks.contains(event.getIgnitingBlock())) return;
|
||||
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
@ -0,0 +1,202 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.tooling.projectStart;
|
||||
|
||||
import eu.mhsl.craftattack.core.Main;
|
||||
import eu.mhsl.craftattack.core.appliance.Appliance;
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceCommand;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.customAdvancements.Advancements;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.customAdvancements.CustomAdvancements;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.tooling.projectStart.command.ProjectStartCancelCommand;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.tooling.projectStart.command.ProjectStartCommand;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.tooling.projectStart.command.ProjectStartResetCommand;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.tooling.projectStart.listener.NoAdvancementsListener;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.tooling.projectStart.listener.PlayerInvincibleListener;
|
||||
import eu.mhsl.craftattack.core.config.Configuration;
|
||||
import eu.mhsl.craftattack.core.util.IteratorUtil;
|
||||
import eu.mhsl.craftattack.core.util.entity.PlayerUtils;
|
||||
import eu.mhsl.craftattack.core.util.text.ComponentUtil;
|
||||
import eu.mhsl.craftattack.core.util.text.Countdown;
|
||||
import eu.mhsl.craftattack.core.util.world.BlockCycle;
|
||||
import net.kyori.adventure.sound.Sound;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import static java.util.Map.entry;
|
||||
import static org.bukkit.Sound.MUSIC_DISC_PRECIPICE;
|
||||
|
||||
public class ProjectStart extends Appliance {
|
||||
private final int startMusicAt = 293;
|
||||
private final World startWorld = Bukkit.getWorld("world");
|
||||
private final Location glassLocation = new Location(this.startWorld, 0, 64, -300);
|
||||
private final List<Location> netherFireLocations = List.of(
|
||||
new Location(this.startWorld, 14, 71, -310)
|
||||
);
|
||||
|
||||
private final Countdown countdown = new Countdown(
|
||||
this.localConfig().getInt("countdown"),
|
||||
this::format,
|
||||
this::announce,
|
||||
this::startProject
|
||||
);
|
||||
private final BlockCycle blockCycle = new BlockCycle(
|
||||
this.glassLocation,
|
||||
Material.RED_STAINED_GLASS,
|
||||
List.of(
|
||||
Material.RED_STAINED_GLASS,
|
||||
Material.YELLOW_STAINED_GLASS,
|
||||
Material.GREEN_STAINED_GLASS,
|
||||
Material.BLUE_STAINED_GLASS
|
||||
)
|
||||
);
|
||||
|
||||
private final Map<GameRule<Boolean>, Boolean> gameRulesAfterStart = Map.ofEntries(
|
||||
entry(GameRule.DO_DAYLIGHT_CYCLE, true),
|
||||
entry(GameRule.DO_INSOMNIA, true),
|
||||
entry(GameRule.ANNOUNCE_ADVANCEMENTS, true),
|
||||
entry(GameRule.DISABLE_RAIDS, false),
|
||||
entry(GameRule.DO_FIRE_TICK, true),
|
||||
entry(GameRule.DO_ENTITY_DROPS, true),
|
||||
entry(GameRule.DO_PATROL_SPAWNING, true),
|
||||
entry(GameRule.DO_TRADER_SPAWNING, true),
|
||||
entry(GameRule.DO_WEATHER_CYCLE, true),
|
||||
entry(GameRule.FALL_DAMAGE, true),
|
||||
entry(GameRule.FIRE_DAMAGE, true)
|
||||
);
|
||||
|
||||
public ProjectStart() {
|
||||
super("countdown");
|
||||
|
||||
this.countdown.addCustomAnnouncement(
|
||||
new Countdown.CustomAnnouncements(
|
||||
counter -> counter == this.startMusicAt,
|
||||
counter -> this.glassLocation
|
||||
.getWorld()
|
||||
.playSound(this.glassLocation, MUSIC_DISC_PRECIPICE, SoundCategory.RECORDS, 500f, 1f)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private Component format(Countdown.AnnouncementData data) {
|
||||
return Component.text()
|
||||
.append(ComponentUtil.createRainbowText("CraftAttack", 10))
|
||||
.append(Component.text(" startet in ", NamedTextColor.GOLD))
|
||||
.append(Component.text(data.count(), NamedTextColor.AQUA))
|
||||
.append(Component.text(" " + data.unit() + "!", NamedTextColor.GOLD))
|
||||
.build();
|
||||
}
|
||||
|
||||
private void announce(Component message) {
|
||||
this.blockCycle.next();
|
||||
IteratorUtil.onlinePlayers(player -> player.sendMessage(message));
|
||||
}
|
||||
|
||||
private void resetAdvancements() {
|
||||
Bukkit.getServer().advancementIterator().forEachRemaining(
|
||||
advancement -> Bukkit.getOnlinePlayers().forEach(
|
||||
player -> player.getAdvancementProgress(advancement).getAwardedCriteria().forEach(
|
||||
criteria -> player.getAdvancementProgress(advancement).revokeCriteria(criteria)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public void startCountdown() {
|
||||
if(!this.isEnabled()) return;
|
||||
this.countdown.start();
|
||||
}
|
||||
|
||||
public void cancelCountdown() {
|
||||
this.countdown.cancel();
|
||||
this.restoreBeforeStart();
|
||||
}
|
||||
|
||||
public void startProject() {
|
||||
this.setEnabled(false);
|
||||
|
||||
IteratorUtil.worlds(World::getWorldBorder, worldBorder -> worldBorder.setSize(worldBorder.getMaxSize()));
|
||||
IteratorUtil.worlds(world -> IteratorUtil.setGameRules(this.gameRulesAfterStart, false));
|
||||
IteratorUtil.worlds(world -> world.setFullTime(0));
|
||||
|
||||
this.netherFireLocations.forEach(location -> Objects.requireNonNull(this.startWorld).getBlockAt(location).setType(Material.FIRE));
|
||||
|
||||
Bukkit.getOnlinePlayers().forEach(player -> {
|
||||
player.setFoodLevel(20);
|
||||
player.setHealth(20);
|
||||
player.getInventory().clear();
|
||||
player.setGameMode(GameMode.SURVIVAL);
|
||||
player.setExp(0);
|
||||
player.setLevel(0);
|
||||
|
||||
player.playSound(Sound.sound(org.bukkit.Sound.ITEM_GOAT_HORN_SOUND_5, Sound.Source.MASTER, 500f, 1f));
|
||||
|
||||
player.sendMessage(Component.text("Viel Spaß bei CraftAttack!", NamedTextColor.GREEN));
|
||||
|
||||
player.setStatistic(Statistic.TIME_SINCE_REST, 0);
|
||||
PlayerUtils.resetStatistics(player);
|
||||
});
|
||||
|
||||
this.resetAdvancements();
|
||||
|
||||
Bukkit.getOnlinePlayers().forEach(
|
||||
player -> Main.instance().getAppliance(CustomAdvancements.class).grantAdvancement(Advancements.start, player.getUniqueId())
|
||||
);
|
||||
|
||||
this.blockCycle.reset();
|
||||
}
|
||||
|
||||
public void restoreBeforeStart() {
|
||||
this.setEnabled(true);
|
||||
|
||||
IteratorUtil.onlinePlayers(Player::stopAllSounds);
|
||||
|
||||
IteratorUtil.worlds(World::getWorldBorder, worldBorder -> {
|
||||
worldBorder.setSize(this.localConfig().getLong("worldborder-before"));
|
||||
worldBorder.setWarningDistance(0);
|
||||
worldBorder.setDamageAmount(0);
|
||||
});
|
||||
|
||||
IteratorUtil.worlds(world -> world, world -> IteratorUtil.setGameRules(this.gameRulesAfterStart, true));
|
||||
this.resetAdvancements();
|
||||
|
||||
this.blockCycle.reset();
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return this.localConfig().getBoolean("enabled");
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.localConfig().set("enabled", enabled);
|
||||
Configuration.saveChanges();
|
||||
}
|
||||
|
||||
public Countdown getCountdown() {
|
||||
return this.countdown;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
protected List<Listener> listeners() {
|
||||
return List.of(
|
||||
new PlayerInvincibleListener(),
|
||||
new NoAdvancementsListener()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
protected List<ApplianceCommand<?>> commands() {
|
||||
return List.of(
|
||||
new ProjectStartCommand(),
|
||||
new ProjectStartCancelCommand(),
|
||||
new ProjectStartResetCommand()
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.tooling.projectStart.command;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceCommand;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.tooling.projectStart.ProjectStart;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ProjectStartCancelCommand extends ApplianceCommand<ProjectStart> {
|
||||
public ProjectStartCancelCommand() {
|
||||
super("projectStartCancel");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if(this.getAppliance().getCountdown().isRunning()) {
|
||||
this.getAppliance().cancelCountdown();
|
||||
sender.sendMessage(Component.text("Countdown cancelled successfully!").color(NamedTextColor.GREEN));
|
||||
} else {
|
||||
sender.sendMessage(Component.text("Countdown is not running!").color(NamedTextColor.RED));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.tooling.projectStart.command;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceCommand;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.tooling.projectStart.ProjectStart;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ProjectStartCommand extends ApplianceCommand<ProjectStart> {
|
||||
public ProjectStartCommand() {
|
||||
super("projectStart");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if(!this.getAppliance().isEnabled()) {
|
||||
sender.sendMessage(Component.text("Countdown not enabled or executed once before!").color(NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
|
||||
if(this.getAppliance().getCountdown().isRunning()) {
|
||||
sender.sendMessage(Component.text("Countdown already running!").color(NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
|
||||
this.getAppliance().startCountdown();
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.tooling.projectStart.command;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceCommand;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.tooling.projectStart.ProjectStart;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ProjectStartResetCommand extends ApplianceCommand<ProjectStart> {
|
||||
public ProjectStartResetCommand() {
|
||||
super("projectStartReset");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
this.getAppliance().restoreBeforeStart();
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.tooling.projectStart.listener;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceListener;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.tooling.projectStart.ProjectStart;
|
||||
import org.bukkit.advancement.Advancement;
|
||||
import org.bukkit.advancement.AdvancementProgress;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.player.PlayerAdvancementDoneEvent;
|
||||
|
||||
public class NoAdvancementsListener extends ApplianceListener<ProjectStart> {
|
||||
@EventHandler
|
||||
public void onAdvancement(PlayerAdvancementDoneEvent event) {
|
||||
if(!this.getAppliance().isEnabled()) return;
|
||||
event.message(null);
|
||||
|
||||
Advancement advancement = event.getAdvancement();
|
||||
AdvancementProgress progress = event.getPlayer().getAdvancementProgress(advancement);
|
||||
for(String criteria : progress.getAwardedCriteria()) {
|
||||
progress.revokeCriteria(criteria);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.tooling.projectStart.listener;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceListener;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.tooling.projectStart.ProjectStart;
|
||||
import io.papermc.paper.event.player.PrePlayerAttackEntityEvent;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.entity.EntityDamageEvent;
|
||||
import org.bukkit.event.entity.FoodLevelChangeEvent;
|
||||
|
||||
public class PlayerInvincibleListener extends ApplianceListener<ProjectStart> {
|
||||
@EventHandler
|
||||
public void onDamage(EntityDamageEvent event) {
|
||||
if(!(event.getEntity() instanceof Player)) return;
|
||||
if(this.getAppliance().isEnabled()) event.setCancelled(true);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onHunger(FoodLevelChangeEvent event) {
|
||||
if(this.getAppliance().isEnabled()) event.setCancelled(true);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onHit(PrePlayerAttackEntityEvent event) {
|
||||
if(this.getAppliance().isEnabled()) event.setCancelled(true);
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.tooling.whitelist;
|
||||
|
||||
import eu.mhsl.craftattack.core.appliance.ApplianceListener;
|
||||
import eu.mhsl.craftattack.core.util.text.DisconnectInfo;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
|
||||
import org.bukkit.event.player.PlayerLoginEvent;
|
||||
|
||||
class PlayerJoinListener extends ApplianceListener<Whitelist> {
|
||||
@EventHandler
|
||||
public void preLoginEvent(AsyncPlayerPreLoginEvent event) {
|
||||
try {
|
||||
this.getAppliance().integrityCheck(event.getUniqueId(), event.getName());
|
||||
} catch(DisconnectInfo.Throwable e) {
|
||||
event.disallow(
|
||||
AsyncPlayerPreLoginEvent.Result.KICK_WHITELIST,
|
||||
e.getDisconnectScreen().getComponent()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void joinEvent(PlayerLoginEvent event) {
|
||||
try {
|
||||
this.getAppliance().lateIntegrityCheck(event.getPlayer());
|
||||
} catch(DisconnectInfo.Throwable e) {
|
||||
event.disallow(
|
||||
PlayerLoginEvent.Result.KICK_WHITELIST,
|
||||
e.getDisconnectScreen().getComponent()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,151 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.tooling.whitelist;
|
||||
|
||||
import eu.mhsl.craftattack.core.Main;
|
||||
import eu.mhsl.craftattack.core.api.client.ReqResp;
|
||||
import eu.mhsl.craftattack.core.api.client.repositories.WhitelistRepository;
|
||||
import eu.mhsl.craftattack.core.api.server.HttpServer;
|
||||
import eu.mhsl.craftattack.core.appliance.Appliance;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.outlawed.Outlawed;
|
||||
import eu.mhsl.craftattack.core.util.api.HttpStatus;
|
||||
import eu.mhsl.craftattack.core.util.server.Floodgate;
|
||||
import eu.mhsl.craftattack.core.util.text.DisconnectInfo;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class Whitelist extends Appliance {
|
||||
private final HashMap<UUID, WhitelistRepository.UserData> userData = new HashMap<>();
|
||||
|
||||
public Whitelist() {
|
||||
super("whitelist");
|
||||
}
|
||||
|
||||
public void fullIntegrityCheck(Player player) throws DisconnectInfo.Throwable {
|
||||
this.integrityCheck(player.getUniqueId(), player.getName());
|
||||
this.lateIntegrityCheck(player);
|
||||
}
|
||||
|
||||
public void lateIntegrityCheck(Player player) throws DisconnectInfo.Throwable {
|
||||
@Nullable WhitelistRepository.UserData user = this.userData.get(player.getUniqueId());
|
||||
if(user == null) {
|
||||
throw new DisconnectInfo.Throwable(
|
||||
"Nutzerdaten nicht geladen",
|
||||
"Deine Nutzerdaten sind noch nicht bereit!",
|
||||
"warte einige Sekunden und versuche es erneut. Falls es weiterhin nicht funktioniert kontaktiere einen Admin!",
|
||||
player.getUniqueId()
|
||||
);
|
||||
}
|
||||
this.queryAppliance(Outlawed.class).updateForcedStatus(player, this.timestampRelevant(user.outlawed_until()));
|
||||
|
||||
String purePlayerName = Floodgate.isBedrock(player)
|
||||
? Floodgate.getBedrockPlayer(player).getUsername()
|
||||
: player.getName();
|
||||
|
||||
if(!user.username().trim().equalsIgnoreCase(purePlayerName))
|
||||
throw new DisconnectInfo.Throwable(
|
||||
"Nutzername geändert",
|
||||
String.format("Der Name '%s' stimmt nicht mit '%s' überein.", user.username(), player.getName()),
|
||||
"Bitte kontaktiere einen Admin, um Deine Anmeldedaten zu aktualisieren!",
|
||||
player.getUniqueId()
|
||||
);
|
||||
}
|
||||
|
||||
public void integrityCheck(UUID uuid, String name) throws DisconnectInfo.Throwable {
|
||||
try {
|
||||
Main.instance().getLogger().info(String.format("Running integrityCheck for %s", name));
|
||||
boolean overrideCheck = this.localConfig().getBoolean("overrideIntegrityCheck", false);
|
||||
WhitelistRepository.UserData user = overrideCheck
|
||||
? new WhitelistRepository.UserData(uuid, name, "", "", 0L, 0L)
|
||||
: this.fetchUserData(uuid);
|
||||
|
||||
this.userData.put(uuid, user);
|
||||
Main.logger().info(String.format("got userdata %s", user.toString()));
|
||||
|
||||
if(this.timestampRelevant(user.banned_until())) {
|
||||
Instant bannedDate = new Date(user.banned_until() * 1000L)
|
||||
.toInstant()
|
||||
.plus(1, ChronoUnit.HOURS);
|
||||
|
||||
DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("dd.MM.yyyy").withZone(ZoneOffset.UTC);
|
||||
DateTimeFormatter timeFormat = DateTimeFormatter.ofPattern("HH:mm").withZone(ZoneOffset.UTC);
|
||||
|
||||
throw new DisconnectInfo.Throwable(
|
||||
"Du wurdest vom Server gebannt.",
|
||||
String.format("Dein Bann läuft am %s um %s ab!", dateFormat.format(bannedDate), timeFormat.format(bannedDate)),
|
||||
"Wende dich an einen Admin für weitere Informationen.",
|
||||
uuid
|
||||
);
|
||||
}
|
||||
} catch(DisconnectInfo.Throwable e) {
|
||||
throw e;
|
||||
} catch(Exception e) {
|
||||
Main.instance().getLogger().log(Level.SEVERE, e, e::getMessage);
|
||||
throw new DisconnectInfo.Throwable(
|
||||
"Interner Serverfehler",
|
||||
"Deine Anmeldedaten konnten nicht abgerufen/ überprüft werden.",
|
||||
"Versuche es später erneut oder kontaktiere einen Admin!",
|
||||
uuid
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean timestampRelevant(Long timestamp) {
|
||||
if(timestamp == null) return false;
|
||||
return timestamp > System.currentTimeMillis() / 1000L;
|
||||
}
|
||||
|
||||
private WhitelistRepository.UserData fetchUserData(UUID uuid) throws DisconnectInfo.Throwable {
|
||||
ReqResp<WhitelistRepository.UserData> response = this.queryRepository(WhitelistRepository.class).getUserData(uuid);
|
||||
|
||||
if(response.status() == HttpStatus.NOT_FOUND)
|
||||
throw new DisconnectInfo.Throwable(
|
||||
"Nicht angemeldet",
|
||||
"Du bist derzeit nicht als Teilnehmer des CraftAttack-Projektes registriert!",
|
||||
"Melde Dich bei einem Admin für eine nachträgliche Anmeldung.",
|
||||
uuid
|
||||
);
|
||||
|
||||
if(response.status() != HttpStatus.OK)
|
||||
throw new IllegalStateException(String.format("Http Reponse %d", response.status()));
|
||||
|
||||
return response.data();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void httpApi(HttpServer.ApiBuilder apiBuilder) {
|
||||
record User(UUID user) {
|
||||
}
|
||||
apiBuilder.post("update", User.class, (user, request) -> {
|
||||
Main.instance().getLogger().info(String.format("API Triggered Profile update for %s", user.user));
|
||||
Player player = Bukkit.getPlayer(user.user);
|
||||
if(player != null) {
|
||||
try {
|
||||
this.fullIntegrityCheck(player);
|
||||
} catch(DisconnectInfo.Throwable e) {
|
||||
e.getDisconnectScreen().applyKick(player);
|
||||
}
|
||||
}
|
||||
return HttpServer.nothing;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
protected List<Listener> listeners() {
|
||||
return List.of(
|
||||
new PlayerJoinListener()
|
||||
);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user