Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc7ce154bd |
No files matched your search
@@ -1,3 +1,11 @@
|
||||
# Chatcoloring
|
||||
Der Spieler benötigt das Recht "chatcolor" damit dessen Namen blau im Chat angezeigt wird.
|
||||
|
||||
# Wandering Trader
|
||||
|
||||
Ich würde vorschlagen, das Plugin WanderingTrades zu verwenden, da es uns ermöglicht, eigene Handelsangebote festzulegen und gleichzeitig die Köpfe von Spielern unseres Servers zu verkaufen.
|
||||
|
||||
Da WanderingTrades auch Mini-Blöcke unterstützt, sollten wir überlegen, ob wir das Plugin MiniBlocks nicht vollständig weglassen. Was das Plugin nicht kann ist Player Heads zu droppen, wenn ein Spieler einen anderen tötet, dass müssten wir dann noch selber schreiben.
|
||||
|
||||
Zu tun wäre dann noch:
|
||||
Die Config des Plugins um Sniffing Potion und Heart of the Seas hinzuzufügen und Preis für Player Heads hinzufügen.
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.silenceNametag;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.JoinConfiguration;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
final class ActionBarNametagFeedback implements NametagFeedback {
|
||||
@Override
|
||||
public void send(Player player, NametagFeedbackUpdate update) {
|
||||
player.sendActionBar(this.render(update));
|
||||
}
|
||||
|
||||
Component render(NametagFeedbackUpdate update) {
|
||||
if(update.summary()) {
|
||||
var counts = update.counts();
|
||||
List<Component> parts = new ArrayList<>();
|
||||
addCount(parts, counts.silenced(), "verstummt", NamedTextColor.GREEN);
|
||||
addCount(parts, counts.unsilenced(), "entstummt", NamedTextColor.GREEN);
|
||||
addCount(parts, counts.alreadySilent(), "bereits stumm", NamedTextColor.YELLOW);
|
||||
addCount(parts, counts.alreadyAudible(), "bereits hörbar", NamedTextColor.YELLOW);
|
||||
addCount(parts, counts.unsupported(), "nicht unterstützt", NamedTextColor.RED);
|
||||
return Component.join(JoinConfiguration.separator(Component.text(" · ", NamedTextColor.GRAY)), parts);
|
||||
}
|
||||
|
||||
boolean silence = update.action() == NametagAction.SILENCE;
|
||||
String suffix = switch(update.result()) {
|
||||
case CHANGED -> silence ? " ist jetzt stumm." : " ist jetzt wieder hörbar.";
|
||||
case UNCHANGED -> silence ? " ist bereits stumm." : " ist bereits hörbar.";
|
||||
case UNSUPPORTED -> " unterstützt dieses Namensschild nicht.";
|
||||
};
|
||||
NamedTextColor color = switch(update.result()) {
|
||||
case CHANGED -> NamedTextColor.GREEN;
|
||||
case UNCHANGED -> NamedTextColor.YELLOW;
|
||||
case UNSUPPORTED -> NamedTextColor.RED;
|
||||
};
|
||||
return Component.empty().color(color).append(update.targetName()).append(Component.text(suffix));
|
||||
}
|
||||
|
||||
private static void addCount(List<Component> parts, int count, String label, NamedTextColor color) {
|
||||
if(count > 0) parts.add(Component.text(count + " " + label, color));
|
||||
}
|
||||
}
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.silenceNametag;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
public enum NametagAction {
|
||||
SILENCE(0, Set.of("@verstummen", "@stumm", "@mute"), "Dieses Namensschild muted Mobs.", true),
|
||||
UNSILENCE(1, Set.of("@entstummen", "@laut", "@unmute"), "Dieses Namensschild unmuted Mobs.", false);
|
||||
|
||||
private final int persistentId;
|
||||
private final Set<String> renameTexts;
|
||||
private final Component lore;
|
||||
private final boolean silent;
|
||||
|
||||
NametagAction(int persistentId, Set<String> renameTexts, String lore, boolean silent) {
|
||||
this.persistentId = persistentId;
|
||||
this.renameTexts = Set.copyOf(renameTexts);
|
||||
this.lore = Component.text(lore);
|
||||
this.silent = silent;
|
||||
}
|
||||
|
||||
public int persistentId() {
|
||||
return this.persistentId;
|
||||
}
|
||||
|
||||
public Component lore() {
|
||||
return this.lore;
|
||||
}
|
||||
|
||||
public boolean silent() {
|
||||
return this.silent;
|
||||
}
|
||||
|
||||
public static @Nullable NametagAction fromPersistentId(int id) {
|
||||
for(NametagAction action : values()) {
|
||||
if(action.persistentId == id) return action;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static @Nullable NametagAction fromRenameText(@Nullable String text) {
|
||||
if(text == null) return null;
|
||||
String normalizedText = text.strip().toLowerCase(Locale.ROOT);
|
||||
for(NametagAction action : values()) {
|
||||
if(action.renameTexts.contains(normalizedText)) return action;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.silenceNametag;
|
||||
|
||||
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.inventory.PrepareAnvilEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.view.AnvilView;
|
||||
|
||||
public class NametagAnvilListener extends ApplianceListener<SilenceNametag> {
|
||||
@EventHandler
|
||||
public void onAnvilRename(PrepareAnvilEvent event) {
|
||||
ItemStack resultItem = event.getResult();
|
||||
if(resultItem == null || resultItem.getType() != Material.NAME_TAG) return;
|
||||
|
||||
AnvilView anvilView = event.getView();
|
||||
ItemStack inputItem = anvilView.getItem(0);
|
||||
if(inputItem == null || inputItem.getType() != Material.NAME_TAG) return;
|
||||
|
||||
NametagAction action = NametagAction.fromRenameText(anvilView.getRenameText());
|
||||
if(action == null) return;
|
||||
|
||||
event.setResult(SilenceNametagItem.create(resultItem, action));
|
||||
}
|
||||
}
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.silenceNametag;
|
||||
|
||||
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.player.PlayerInteractEntityEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.inventory.EquipmentSlot;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public class NametagEntityListener extends ApplianceListener<SilenceNametag> {
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||
public void onNametagUse(PlayerInteractEntityEvent event) {
|
||||
ItemStack item;
|
||||
if(event.getHand() == EquipmentSlot.HAND) {
|
||||
item = event.getPlayer().getInventory().getItemInMainHand();
|
||||
} else {
|
||||
item = event.getPlayer().getInventory().getItemInOffHand();
|
||||
}
|
||||
|
||||
if(item.getType() != Material.NAME_TAG) return;
|
||||
|
||||
NametagAction action = SilenceNametagItem.readAction(item);
|
||||
if(action == null) return;
|
||||
|
||||
SilenceNametag.ActionResult result = this.getAppliance().applyNametagAction(event.getRightClicked(), action);
|
||||
this.getAppliance().feedback().send(event.getPlayer(), event.getRightClicked(), action, result);
|
||||
if(result == SilenceNametag.ActionResult.UNSUPPORTED) return;
|
||||
|
||||
// Named tags retain vanilla naming and consumption, even if the mob is already in the desired state.
|
||||
if(item.getItemMeta().customName() != null) return;
|
||||
|
||||
// Unnamed special tags are handled entirely here, including no-op interactions.
|
||||
event.setCancelled(true);
|
||||
if(result == SilenceNametag.ActionResult.CHANGED && event.getPlayer().getGameMode() != GameMode.CREATIVE) {
|
||||
item.setAmount(item.getAmount() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onQuit(PlayerQuitEvent event) {
|
||||
this.getAppliance().feedback().forget(event.getPlayer().getUniqueId());
|
||||
}
|
||||
}
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.silenceNametag;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
interface NametagFeedback {
|
||||
void send(Player player, NametagFeedbackUpdate update);
|
||||
}
|
||||
-82
@@ -1,82 +0,0 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.silenceNametag;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.LongSupplier;
|
||||
|
||||
final class NametagFeedbackService {
|
||||
private static final long SERIES_TIMEOUT = TimeUnit.SECONDS.toNanos(1);
|
||||
private final LongSupplier clock;
|
||||
private final List<NametagFeedback> outputs;
|
||||
private final Map<UUID, Series> seriesByPlayer = new HashMap<>();
|
||||
|
||||
NametagFeedbackService(LongSupplier clock, List<NametagFeedback> outputs) {
|
||||
this.clock = clock;
|
||||
this.outputs = List.copyOf(outputs);
|
||||
}
|
||||
|
||||
void send(Player player, Entity target, NametagAction action, SilenceNametag.ActionResult result) {
|
||||
Component name = target.customName();
|
||||
if(name == null) name = Component.translatable(target.getType().translationKey());
|
||||
NametagFeedbackUpdate update = this.recordInteraction(player.getUniqueId(), target.getUniqueId(), name, action, result);
|
||||
this.outputs.forEach(output -> output.send(player, update));
|
||||
}
|
||||
|
||||
// Called synchronously by the entity listener. Series retain no Bukkit objects or display names.
|
||||
NametagFeedbackUpdate recordInteraction(UUID playerId, UUID targetId, Component name,
|
||||
NametagAction action, SilenceNametag.ActionResult result) {
|
||||
long now = this.clock.getAsLong();
|
||||
this.seriesByPlayer.values().removeIf(series -> now - series.lastInteraction >= SERIES_TIMEOUT);
|
||||
boolean newSeries = !this.seriesByPlayer.containsKey(playerId);
|
||||
Series series = this.seriesByPlayer.computeIfAbsent(playerId, ignored -> new Series());
|
||||
series.lastInteraction = now;
|
||||
if(result == SilenceNametag.ActionResult.UNSUPPORTED) {
|
||||
series.unsupported.add(targetId);
|
||||
} else {
|
||||
series.results.merge(new TargetAction(targetId, action), result, (previous, current) ->
|
||||
previous == SilenceNametag.ActionResult.CHANGED ? previous : current);
|
||||
}
|
||||
return new NametagFeedbackUpdate(targetId, name, action, result, newSeries, !newSeries, series.counts());
|
||||
}
|
||||
|
||||
void forget(UUID playerId) {
|
||||
this.seriesByPlayer.remove(playerId);
|
||||
}
|
||||
|
||||
void clear() {
|
||||
this.seriesByPlayer.clear();
|
||||
}
|
||||
|
||||
private record TargetAction(UUID targetId, NametagAction action) {
|
||||
}
|
||||
|
||||
private static final class Series {
|
||||
private long lastInteraction;
|
||||
private final Map<TargetAction, SilenceNametag.ActionResult> results = new HashMap<>();
|
||||
private final Set<UUID> unsupported = new HashSet<>();
|
||||
|
||||
private NametagFeedbackUpdate.Counts counts() {
|
||||
int silenced = 0, unsilenced = 0, alreadySilent = 0, alreadyAudible = 0;
|
||||
for(var entry : this.results.entrySet()) {
|
||||
boolean changed = entry.getValue() == SilenceNametag.ActionResult.CHANGED;
|
||||
if(entry.getKey().action() == NametagAction.SILENCE) {
|
||||
if(changed) silenced++;
|
||||
else alreadySilent++;
|
||||
} else {
|
||||
if(changed) unsilenced++;
|
||||
else alreadyAudible++;
|
||||
}
|
||||
}
|
||||
return new NametagFeedbackUpdate.Counts(silenced, unsilenced, alreadySilent, alreadyAudible, this.unsupported.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.silenceNametag;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
record NametagFeedbackUpdate(
|
||||
UUID targetId,
|
||||
Component targetName,
|
||||
NametagAction action,
|
||||
SilenceNametag.ActionResult result,
|
||||
boolean newSeries,
|
||||
boolean summary,
|
||||
Counts counts
|
||||
) {
|
||||
record Counts(int silenced, int unsilenced, int alreadySilent, int alreadyAudible, int unsupported) {
|
||||
}
|
||||
}
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.silenceNametag;
|
||||
|
||||
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Mob;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class SilenceNametag extends Appliance {
|
||||
private final NametagFeedbackService feedback = new NametagFeedbackService(
|
||||
System::nanoTime, List.of(new ActionBarNametagFeedback()));
|
||||
|
||||
NametagFeedbackService feedback() {
|
||||
return this.feedback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
this.feedback.clear();
|
||||
}
|
||||
|
||||
public enum ActionResult {
|
||||
CHANGED,
|
||||
UNCHANGED,
|
||||
UNSUPPORTED
|
||||
}
|
||||
|
||||
public ActionResult applyNametagAction(Entity entity, NametagAction action) {
|
||||
Objects.requireNonNull(action, "Action must not be null.");
|
||||
if(!(entity instanceof Mob)) return ActionResult.UNSUPPORTED;
|
||||
if(entity.isSilent() == action.silent()) return ActionResult.UNCHANGED;
|
||||
|
||||
entity.setSilent(action.silent());
|
||||
return ActionResult.CHANGED;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull List<Listener> listeners() {
|
||||
return List.of(new NametagAnvilListener(), new NametagEntityListener());
|
||||
}
|
||||
}
|
||||
-50
@@ -1,50 +0,0 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.silenceNametag;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.persistence.PersistentDataType;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
final class SilenceNametagItem {
|
||||
// Keep the key and IDs compatible with existing nametags.
|
||||
private static final NamespacedKey ACTION_KEY = new NamespacedKey("silence-nametag", "nametag-action");
|
||||
|
||||
private SilenceNametagItem() {
|
||||
}
|
||||
|
||||
static @Nullable NametagAction readAction(ItemStack item) {
|
||||
if(item.getType() != Material.NAME_TAG) return null;
|
||||
var data = item.getPersistentDataContainer();
|
||||
if(!data.has(ACTION_KEY, PersistentDataType.INTEGER)) return null;
|
||||
|
||||
Integer id = data.get(ACTION_KEY, PersistentDataType.INTEGER);
|
||||
return id == null ? null : NametagAction.fromPersistentId(id);
|
||||
}
|
||||
|
||||
static ItemStack create(ItemStack source, NametagAction action) {
|
||||
Objects.requireNonNull(action, "Action must not be null.");
|
||||
if(source.getType() != Material.NAME_TAG) {
|
||||
throw new IllegalArgumentException("Item must be a nametag.");
|
||||
}
|
||||
|
||||
ItemStack result = source.clone();
|
||||
result.editMeta(meta -> {
|
||||
meta.getPersistentDataContainer().set(ACTION_KEY, PersistentDataType.INTEGER, action.persistentId());
|
||||
List<Component> existingLore = meta.lore();
|
||||
List<Component> lore = existingLore == null ? new ArrayList<>() : new ArrayList<>(existingLore);
|
||||
for(NametagAction knownAction : NametagAction.values()) {
|
||||
lore.removeIf(knownAction.lore()::equals);
|
||||
}
|
||||
lore.add(action.lore());
|
||||
meta.lore(lore);
|
||||
meta.customName(null);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user