Page MenuHomeDevCentral

D3648.diff
No OneTemporary

D3648.diff

diff --git a/.gitignore b/.gitignore
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
.gradle
build/
plugins/**/gradle/
+plugins/suhayl/src/main/resources/resourcepack.properties
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
diff --git a/plugins/suhayl/build.gradle.kts b/plugins/suhayl/build.gradle.kts
--- a/plugins/suhayl/build.gradle.kts
+++ b/plugins/suhayl/build.gradle.kts
@@ -1,6 +1,9 @@
import org.gradle.api.tasks.bundling.Jar
import org.gradle.api.tasks.Copy
+import java.security.MessageDigest
+import java.util.Properties
+
plugins {
id("java")
}
@@ -48,6 +51,74 @@
)
}
+tasks.named("build") {
+ dependsOn(
+ promoteEnglishMessagesAsDefault,
+ "updateResourcePackProperties",
+ )
+}
+
+val resourcePackSourceDir = layout.projectDirectory.dir("src/main/resources/resourcepack")
+val resourcePackProperties = layout.projectDirectory.file("src/main/resources/resourcepack.properties")
+
+val resourcePackBuildDir = layout.buildDirectory.dir("resourcepack")
+val resourcePackZip = resourcePackBuildDir.get().file("suhayl.zip")
+val resourcePackSha1 = resourcePackBuildDir.get().file("suhayl.sha1")
+
+tasks.register<Zip>("zipResourcePack") {
+ from(resourcePackSourceDir)
+ archiveFileName.set("suhayl.zip")
+ destinationDirectory.set(resourcePackBuildDir)
+}
+
+tasks.register("computeResourcePackSha1") {
+ dependsOn("zipResourcePack")
+ inputs.file(resourcePackZip)
+ outputs.file(resourcePackSha1)
+
+ doLast {
+ val zipFile = resourcePackZip.asFile
+ val sha1 = MessageDigest.getInstance("SHA-1")
+ .digest(zipFile.readBytes())
+ .joinToString("") { "%02x".format(it) }
+
+ resourcePackSha1.asFile.writeText(sha1)
+ println("Resource pack SHA1: $sha1")
+ }
+}
+
+tasks.register("updateResourcePackProperties") {
+ dependsOn("computeResourcePackSha1")
+
+ doLast {
+ val sha1 = resourcePackSha1.asFile.readText().trim()
+ val propsFile = resourcePackProperties.asFile
+
+ val props = Properties().apply {
+ if (propsFile.exists()) {
+ propsFile.inputStream().use { load(it) }
+ }
+ }
+
+ props["resourcepack.url"] = "https://windriver.nasqueron.org/~minecraft/packs/suhayl.zip"
+ props["resourcepack.sha1"] = sha1
+
+ propsFile.outputStream().use { props.store(it, "Updated by Gradle") }
+
+ println("Updated ${propsFile.name} with SHA1=$sha1")
+ }
+}
+
+tasks.register<Exec>("deployResourcePack") {
+ val remoteHost = (findProperty("remoteHost") as String?) ?: "windriver.nasqueron.org"
+ val remoteUser = (findProperty("remoteUser") as String?) ?: System.getenv("USER") ?: "deploy"
+ val remoteDir = (findProperty("remoteResourcePackDir") as String?) ?: "/var/home-wwwroot/minecraft/packs/"
+
+ dependsOn("updateResourcePackProperties")
+ val zipFile = resourcePackZip.asFile
+ commandLine("scp", zipFile.absolutePath, "$remoteUser@$remoteHost:$remoteDir")
+}
+
tasks.register<Exec>("deploy") {
group = "deployment"
description = "SCP the built JAR to windriver.nasqueron.org (configurable via -PremoteUser and -PremoteDir)."
@@ -55,6 +126,7 @@
dependsOn(
tasks.named("build"),
tasks.named("jar"),
+ tasks.named("deployResourcePack"),
)
val remoteHost = (findProperty("remoteHost") as String?) ?: "windriver.nasqueron.org"
diff --git a/plugins/suhayl/src/main/java/org/eu/loupsgris/quilvaryn/SuhaylPlugin.java b/plugins/suhayl/src/main/java/org/eu/loupsgris/quilvaryn/SuhaylPlugin.java
--- a/plugins/suhayl/src/main/java/org/eu/loupsgris/quilvaryn/SuhaylPlugin.java
+++ b/plugins/suhayl/src/main/java/org/eu/loupsgris/quilvaryn/SuhaylPlugin.java
@@ -6,9 +6,8 @@
import org.eu.loupsgris.quilvaryn.config.SuhaylConfig;
import org.eu.loupsgris.quilvaryn.config.SuhaylTranslationRegistry;
import org.eu.loupsgris.quilvaryn.items.CommandmentStick;
-import org.eu.loupsgris.quilvaryn.listeners.HappyGhastBehavior;
-import org.eu.loupsgris.quilvaryn.listeners.OptimizedWeather;
-import org.eu.loupsgris.quilvaryn.listeners.WanderingTraderBehavior;
+import org.eu.loupsgris.quilvaryn.items.Quill;
+import org.eu.loupsgris.quilvaryn.listeners.*;
import org.eu.loupsgris.quilvaryn.services.WanderingTraderService;
import org.eu.loupsgris.quilvaryn.utils.ResourceLoader;
@@ -33,6 +32,7 @@
Bukkit.getPluginManager().registerEvents(new HappyGhastBehavior(this), this);
registerCommandmentStick();
+ registerQuill();
}
private SuhaylConfig loadConfig() {
@@ -75,6 +75,11 @@
Bukkit.addRecipe(CommandmentStick.getRecipe());
}
+ private void registerQuill() {
+ Bukkit.getPluginManager().registerEvents(new QuillBehavior(), this);
+ Bukkit.addRecipe(Quill.getRecipe());
+ }
+
@Override
public void onDisable() {
getLogger().info("Suhayl quality of life plugin disabled.");
diff --git a/plugins/suhayl/src/main/java/org/eu/loupsgris/quilvaryn/config/SuhaylMessages.java b/plugins/suhayl/src/main/java/org/eu/loupsgris/quilvaryn/config/SuhaylMessages.java
--- a/plugins/suhayl/src/main/java/org/eu/loupsgris/quilvaryn/config/SuhaylMessages.java
+++ b/plugins/suhayl/src/main/java/org/eu/loupsgris/quilvaryn/config/SuhaylMessages.java
@@ -1,5 +1,7 @@
package org.eu.loupsgris.quilvaryn.config;
+import org.bukkit.entity.Player;
+
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
@@ -18,4 +20,8 @@
}
}
+ public static String get(String key, Player player) {
+ return get(key, player.locale());
+ }
+
}
diff --git a/plugins/suhayl/src/main/java/org/eu/loupsgris/quilvaryn/items/Quill.java b/plugins/suhayl/src/main/java/org/eu/loupsgris/quilvaryn/items/Quill.java
new file mode 100644
--- /dev/null
+++ b/plugins/suhayl/src/main/java/org/eu/loupsgris/quilvaryn/items/Quill.java
@@ -0,0 +1,150 @@
+package org.eu.loupsgris.quilvaryn.items;
+
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.format.NamedTextColor;
+
+import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
+import org.bukkit.Sound;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.ShapedRecipe;
+import org.bukkit.inventory.meta.ItemMeta;
+import org.bukkit.persistence.PersistentDataContainer;
+import org.bukkit.persistence.PersistentDataType;
+
+import org.eu.loupsgris.quilvaryn.SuhaylPlugin;
+import org.eu.loupsgris.quilvaryn.config.SuhaylMessages;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.Random;
+
+public class Quill {
+
+ public static final String ITEM_TAG = "is_quill";
+
+ public static final String DURABILITY_TAG = "durability";
+ public static final int MAX_DURABILITY = 16;
+
+ /**
+ * Create the Quill item.
+ */
+ public static ItemStack getItem() {
+ ItemStack item = new ItemStack(Material.FEATHER, 1);
+
+ item.editMeta(meta -> {
+ meta.displayName(Component.translatable("item.quill.name"));
+
+ meta.lore(List.of(
+ Component.translatable("item.quill.lore1").color(NamedTextColor.GRAY),
+ Component.translatable("item.quill.lore2").color(NamedTextColor.GRAY)
+ ));
+
+ PersistentDataContainer dataContainer = meta.getPersistentDataContainer();
+ NamespacedKey key = new NamespacedKey(SuhaylPlugin.NAMESPACE, ITEM_TAG);
+ dataContainer.set(key, PersistentDataType.BOOLEAN, true);
+
+ meta.setMaxStackSize(1);
+ });
+
+ setDurability(item, MAX_DURABILITY);
+
+ return item;
+ }
+
+ public static boolean isItem(ItemStack item) {
+ if (item.getType() != Material.FEATHER || !item.hasItemMeta()) {
+ return false;
+ }
+
+ ItemMeta meta = item.getItemMeta();
+ PersistentDataContainer pdc = meta.getPersistentDataContainer();
+
+ NamespacedKey key = new NamespacedKey(SuhaylPlugin.NAMESPACE, ITEM_TAG);
+ return
+ pdc.has(key, PersistentDataType.BOOLEAN)
+ &&
+ pdc.get(key, PersistentDataType.BOOLEAN);
+ }
+
+ public static ShapedRecipe getRecipe() {
+ NamespacedKey key = new NamespacedKey(SuhaylPlugin.NAMESPACE, "quill");
+ ItemStack quill = getItem();
+
+ ShapedRecipe recipe = new ShapedRecipe(key, quill);
+ recipe.shape(
+ " F ",
+ " I ",
+ " "
+ );
+ recipe.setIngredient('F', Material.FEATHER);
+ recipe.setIngredient('I', Material.INK_SAC);
+
+ return recipe;
+ }
+
+ ///
+ /// Durability
+ ///
+
+ private static Optional<Integer> getDurability(ItemStack item) {
+ try {
+ PersistentDataContainer dataContainer = item.getItemMeta().getPersistentDataContainer();
+
+ NamespacedKey durabilityKey = new NamespacedKey(SuhaylPlugin.NAMESPACE, DURABILITY_TAG);
+ int durability = dataContainer.get(durabilityKey, PersistentDataType.INTEGER);
+
+ return Optional.of(durability);
+ } catch (NullPointerException ex) {
+ return Optional.empty();
+ }
+ }
+
+ private static void setDurability(ItemStack item, int durability) {
+ item.editMeta(meta -> {
+ PersistentDataContainer dataContainer = meta.getPersistentDataContainer();
+
+ NamespacedKey durabilityKey = new NamespacedKey(SuhaylPlugin.NAMESPACE, DURABILITY_TAG);
+ dataContainer.set(durabilityKey, PersistentDataType.INTEGER, durability);
+ });
+ }
+
+ public static void consumeDurability(ItemStack item, Player player) {
+ var optionalDurability = getDurability(item);
+ if (optionalDurability.isEmpty()) {
+ return;
+ }
+
+ int durability = optionalDurability.get() - 1;
+
+ if (durability > 0) {
+ setDurability(item, durability);
+ } else {
+ // Break item
+ breakItem(player, item);
+ }
+ }
+
+ private static void breakItem(Player player, ItemStack item) {
+ item.setAmount(0);
+
+ player.playSound(player.getLocation(), Sound.ENTITY_ITEM_BREAK, 1f, 1f);
+
+ if (canRecycleFeather()) {
+ player.getInventory().addItem(new ItemStack(Material.FEATHER));
+ player.sendMessage(Component.text(SuhaylMessages.get("item.quill.break.recycled", player))
+ .color(NamedTextColor.GRAY));
+ } else {
+ player.sendMessage(Component.text(SuhaylMessages.get("item.quill.break.normal", player))
+ .color(NamedTextColor.GRAY));
+ }
+ }
+
+ private static Boolean canRecycleFeather () {
+ Random random = new Random();
+
+ return random.nextInt(4) == 0;
+ }
+
+}
diff --git a/plugins/suhayl/src/main/java/org/eu/loupsgris/quilvaryn/listeners/QuillBehavior.java b/plugins/suhayl/src/main/java/org/eu/loupsgris/quilvaryn/listeners/QuillBehavior.java
new file mode 100644
--- /dev/null
+++ b/plugins/suhayl/src/main/java/org/eu/loupsgris/quilvaryn/listeners/QuillBehavior.java
@@ -0,0 +1,143 @@
+package org.eu.loupsgris.quilvaryn.listeners;
+
+import io.papermc.paper.dialog.Dialog;
+import io.papermc.paper.registry.data.dialog.ActionButton;
+import io.papermc.paper.registry.data.dialog.DialogBase;
+import io.papermc.paper.registry.data.dialog.action.DialogAction;
+import io.papermc.paper.registry.data.dialog.action.DialogActionCallback;
+import io.papermc.paper.registry.data.dialog.input.DialogInput;
+import io.papermc.paper.registry.data.dialog.type.DialogType;
+
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.event.ClickCallback;
+import net.kyori.adventure.text.format.NamedTextColor;
+
+import org.bukkit.block.BlockState;
+import org.bukkit.block.Container;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.event.player.PlayerInteractEvent;
+import org.bukkit.inventory.EquipmentSlot;
+import org.bukkit.inventory.ItemStack;
+
+import org.eu.loupsgris.quilvaryn.config.SuhaylMessages;
+import org.eu.loupsgris.quilvaryn.items.Quill;
+
+import java.util.List;
+import java.util.Locale;
+
+public class QuillBehavior implements Listener {
+
+ ///
+ /// Events
+ ///
+
+ @EventHandler
+ public void onQuillUse(PlayerInteractEvent event) {
+ if (!event.getAction().isRightClick() || event.getClickedBlock() == null) {
+ return;
+ }
+
+ if (event.getHand() != EquipmentSlot.HAND) {
+ return;
+ }
+
+ Player player = event.getPlayer();
+ ItemStack item = event.getItem();
+
+ if (item == null || !Quill.isItem(item)) {
+ return;
+ }
+
+ BlockState state = event.getClickedBlock().getState();
+ if (!(state instanceof Container container)) {
+ return;
+ }
+
+ InventoryType containerType = container.getInventory().getType();
+ if (!isValidInventoryType(containerType)) {
+ return;
+ }
+
+ event.setCancelled(true);
+ startQuillDialog(player, item, container);
+ }
+
+ ///
+ /// Dialog API
+ ///
+
+ private void startQuillDialog(Player player, ItemStack item, Container container) {
+ Locale locale = player.locale();
+
+ Component title = Component.text(SuhaylMessages.get("dialog.rename_container.title", locale))
+ .color(NamedTextColor.GOLD);
+
+ Component inputLabel = Component.text(SuhaylMessages.get("dialog.rename_container.input.label", locale))
+ .color(NamedTextColor.GRAY);
+
+ Component confirmButton = Component.text(SuhaylMessages.get("dialog.generic.button.confirm", locale))
+ .color(NamedTextColor.GREEN);
+ Component cancelButton = Component.text(SuhaylMessages.get("dialog.generic.button.cancel", locale))
+ .color(NamedTextColor.GRAY);
+
+ Dialog dialog = Dialog.create(builder -> builder.empty()
+ .base(DialogBase.builder(title)
+ .inputs(List.of(
+ DialogInput.text("label", inputLabel)
+ .build()
+ ))
+ .build()
+ )
+ .type(DialogType.confirmation(
+ ActionButton.create(
+ confirmButton,
+ null,
+ 100,
+ DialogAction.customClick(
+ updateContainerLabel(player, item, container),
+ ClickCallback.Options.builder()
+ .uses(1)
+ .lifetime(ClickCallback.DEFAULT_LIFETIME)
+ .build()
+ )
+ ),
+ ActionButton.create(
+ cancelButton,
+ null,
+ 100,
+ null // closes the dialog
+ )
+ ))
+ );
+
+ player.showDialog(dialog);
+ }
+
+ private static DialogActionCallback updateContainerLabel(Player player, ItemStack item, Container container) {
+ return (view, audience) -> {
+ String label = view.getText("label");
+
+ if (audience instanceof Player && label != null) {
+ container.customName(Component.text(label));
+ container.update();
+
+ Quill.consumeDurability(item, player);
+ }
+ };
+ }
+
+ ///
+ /// Helper methods
+ ///
+
+ private boolean isValidInventoryType(InventoryType type) {
+ return switch (type) {
+ case CHEST, BARREL, SHULKER_BOX -> true;
+ default -> false;
+ };
+ }
+
+}
diff --git a/plugins/suhayl/src/main/resources/l10n/messages.properties b/plugins/suhayl/src/main/resources/l10n/messages.properties
--- a/plugins/suhayl/src/main/resources/l10n/messages.properties
+++ b/plugins/suhayl/src/main/resources/l10n/messages.properties
@@ -11,3 +11,12 @@
wandering_trader_menu.actions.ban=Wandering traders are discouraged from visiting for a while.
wandering_trader_menu.actions.silenced=The trader goes quiet.
wandering_trader_menu.actions.unsilenced=The trader finds their voice again.
+
+dialog.generic.button.confirm=Confirm
+dialog.generic.button.cancel=Cancel
+
+dialog.rename_container.title=Write a label
+dialog.rename_container.input.label=Label
+
+item.quill.break.recycled=✒ The quill’s ink fades away, leaving only a feather.
+item.quill.break.normal=✒ The quill is torn and can’t be used anymore.
diff --git a/plugins/suhayl/src/main/resources/l10n/messages_en.properties b/plugins/suhayl/src/main/resources/l10n/messages_en.properties
--- a/plugins/suhayl/src/main/resources/l10n/messages_en.properties
+++ b/plugins/suhayl/src/main/resources/l10n/messages_en.properties
@@ -11,3 +11,12 @@
wandering_trader_menu.actions.ban=Wandering traders are discouraged from visiting for a while.
wandering_trader_menu.actions.silenced=The trader goes quiet.
wandering_trader_menu.actions.unsilenced=The trader finds their voice again.
+
+dialog.generic.button.confirm=Confirm
+dialog.generic.button.cancel=Cancel
+
+dialog.rename_container.title=Write a label
+dialog.rename_container.input.label=Label
+
+item.quill.break.recycled=✒ The quill’s ink fades away, leaving only a feather.
+item.quill.break.normal=✒ The quill is torn and can’t be used anymore.
diff --git a/plugins/suhayl/src/main/resources/l10n/messages_fr.properties b/plugins/suhayl/src/main/resources/l10n/messages_fr.properties
--- a/plugins/suhayl/src/main/resources/l10n/messages_fr.properties
+++ b/plugins/suhayl/src/main/resources/l10n/messages_fr.properties
@@ -11,3 +11,12 @@
wandering_trader_menu.actions.ban=Les marchands ambulants sont découragés de nous rendre visite cette semaine.
wandering_trader_menu.actions.silenced=§7Le marchand garde le silence.
wandering_trader_menu.actions.unsilenced=§7Le marchand parle à nouveau.
+
+dialog.generic.button.confirm=Écrire
+dialog.generic.button.cancel=Annuler
+
+dialog.rename_container.title=Inscription
+dialog.rename_container.input.label=Texte
+
+item.quill.break.recycled=✒ L’encre s’estompe doucement, ne subsiste qu’une plume.
+item.quill.break.normal=✒ La plume se brise et ne peut plus être utilisée.
diff --git a/plugins/suhayl/src/main/resources/resourcepack/assets/suhayl/lang/en_us.json b/plugins/suhayl/src/main/resources/resourcepack/assets/suhayl/lang/en_us.json
new file mode 100644
--- /dev/null
+++ b/plugins/suhayl/src/main/resources/resourcepack/assets/suhayl/lang/en_us.json
@@ -0,0 +1,5 @@
+{
+ "item.quill.name": "Quill",
+ "item.quill.lore1": "Right-click a container",
+ "item.quill.lore2": "to label it."
+}
diff --git a/plugins/suhayl/src/main/resources/resourcepack/assets/suhayl/lang/fr_fr.json b/plugins/suhayl/src/main/resources/resourcepack/assets/suhayl/lang/fr_fr.json
new file mode 100644
--- /dev/null
+++ b/plugins/suhayl/src/main/resources/resourcepack/assets/suhayl/lang/fr_fr.json
@@ -0,0 +1,5 @@
+{
+ "item.quill.name": "Plume",
+ "item.quill.lore1": "Permet d'écrire sur",
+ "item.quill.lore2": "un coffre ou un tonneau."
+}
diff --git a/plugins/suhayl/src/main/resources/resourcepack/pack.mcmeta b/plugins/suhayl/src/main/resources/resourcepack/pack.mcmeta
new file mode 100644
--- /dev/null
+++ b/plugins/suhayl/src/main/resources/resourcepack/pack.mcmeta
@@ -0,0 +1,6 @@
+{
+ "pack": {
+ "pack_format": 64,
+ "description": "Localisation for Quilvaryn server quality of life tweaks."
+ }
+}

File Metadata

Mime Type
text/plain
Expires
Thu, Sep 4, 15:40 (17 h, 32 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2960401
Default Alt Text
D3648.diff (19 KB)

Event Timeline