Page Menu
Home
DevCentral
Search
Configure Global Search
Log In
Files
F11632835
D3648.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
19 KB
Referenced Files
None
Subscribers
None
D3648.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D3648: Label chests with a quill
Attached
Detach File
Event Timeline
Log In to Comment