Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
11 changes: 10 additions & 1 deletion .gitignore
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,13 @@ $RECYCLE.BIN/
# End of https://www.gitignore.io/api/java,windows,intellij

# Claude Code documentation (local development only)
CLAUDE.md
CLAUDE.md

# Maven build output
target/

# Folia notes (local only)
FOLIA_COMPATIBILITY.md

# Generated by maven-shade-plugin on every package
dependency-reduced-pom.xml
Empty file modified .idea/compiler.xml
100644 → 100755
Empty file.
Empty file modified .idea/inspectionProfiles/Project_Default.xml
100644 → 100755
Empty file.
Empty file modified .idea/misc.xml
100644 → 100755
Empty file.
Empty file modified .idea/vcs.xml
100644 → 100755
Empty file.
Empty file modified ARCHITECTURE.md
100644 → 100755
Empty file.
Empty file modified CODE_FLOW.md
100644 → 100755
Empty file.
Empty file modified LICENSE
100644 → 100755
Empty file.
21 changes: 21 additions & 0 deletions README.md
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,28 @@
<img src="signshopheader.png" alt="SignShop LOGO">
</p>

## ⚡ Folia Compatible

This fork adds **full Folia support** for multi-threaded servers while maintaining complete backward compatibility with Bukkit/Spigot/Paper.

Scheduling goes through `SchedulerAdapter`, which picks Folia's region schedulers when it detects Folia
and the normal `BukkitScheduler` everywhere else.

---

## Supported Versions

Runs on Bukkit/Spigot/Paper/Folia from 1.13.2 through the year based drops: **26.1, 26.1.1, 26.1.2 and 26.2**.
Minecraft dropped the leading `1.` in 2026, so the drop after 1.21.11 is 26.1, not 1.26.1.

Builds against `spigot-api 26.2` and Java 21. `plugin.yml` keeps `api-version: 1.21` on purpose: it is a
minimum, not a target, so the same jar still loads on 1.21 servers. To build against an older drop:

```
mvn -Dspigot.version=26.1-R0.1-SNAPSHOT clean package
```

---

SignShop allows you to set up physical shops by punching a chest with your items you want to sell, then punching a sign (while holding redstone dust). It's easy to set up, and even easier to customize!

Expand Down
78 changes: 55 additions & 23 deletions pom.xml
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@

<groupId>org.wargamer2010</groupId>
<artifactId>SignShop</artifactId>
<version>5.2.0</version>
<version>5.2.3</version>

<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- Newest game drop we build against. Override to check older ones still compile,
e.g. mvn -Dspigot.version=26.1-R0.1-SNAPSHOT compile -->
<spigot.version>26.2-R0.1-SNAPSHOT</spigot.version>
</properties>
<repositories>
<repository>
Expand Down Expand Up @@ -55,114 +58,143 @@
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.21-R0.1-SNAPSHOT</version>
<version>${spigot.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations-java5</artifactId>
<version>24.0.1</version>
<version>24.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.github.MilkBowl</groupId>
<artifactId>VaultAPI</artifactId>
<version>1.7</version>
<version>1.7.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.github.GriefPrevention</groupId>
<artifactId>GriefPrevention</artifactId>
<version>16.18.4</version>
<version>18.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.sk89q.worldguard</groupId>
<artifactId>worldguard-bukkit</artifactId>
<version>7.0.0</version>
<version>7.0.18</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.sk89q.worldedit</groupId>
<artifactId>worldedit-bukkit</artifactId>
<version>7.1.0</version>
<version>7.4.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.palmergames.bukkit.towny</groupId>
<artifactId>towny</artifactId>
<version>0.100.3.0</version>
<version>0.103.2.0</version>
<scope>provided</scope>
</dependency>
<!-- Towny is built against paper-api these days and its Resident/Town classes extend
Adventure audiences, so javac needs these interfaces on the classpath. We never
call them ourselves, which keeps the plugin running on plain Spigot. -->
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-api</artifactId>
<version>4.26.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>world.bentobox</groupId>
<artifactId>bentobox</artifactId>
<version>2.4.1-SNAPSHOT</version>
<version>2.7.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.bekvon.bukkit</groupId>
<artifactId>residence</artifactId>
<version>4.8.8.2</version>
<groupId>com.github.Zrips</groupId>
<artifactId>Residence</artifactId>
<version>6.0.2.3</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.earth2me</groupId>
<artifactId>essentials</artifactId>
<version>2.22.0</version>
<groupId>net.essentialsx</groupId>
<artifactId>EssentialsX</artifactId>
<version>2.21.2</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>us.dynmap</groupId>
<artifactId>DynmapCoreAPI</artifactId>
<version>3.7-beta-6</version>
<version>3.8</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.25.2</version>
<version>3.53.2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.Zrips</groupId>
<groupId>com.github.Zrips</groupId>
<artifactId>CMI-API</artifactId>
<version>9.7.14.3</version>
<version>9.8.6.4</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.griefcraft.lwc</groupId>
<artifactId>LWCX</artifactId>
<version>2.2.5</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>nl.rutgerkok</groupId>
<artifactId>blocklocker</artifactId>
<version>1.7</version>
<version>1.13</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.github.angeschossen</groupId>
<artifactId>LandsAPI</artifactId>
<version>4.9.4</version>
<version>7.25.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-bukkit</artifactId>
<version>3.0.0</version>
<version>3.2.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
<version>2.14.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>21</source>
<target>21</target>
<excludes>
<!-- LWCX publishes no artifact we can resolve, so this hook is left out
of the build. Everything else it hooks into is a real dependency. -->
<exclude>**/hooks/LWCHook.java</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
Expand Down
Empty file modified signshopheader.png
100644 → 100755
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file modified src/main/java/org/wargamer2010/essentials/SetExpFix.java
100644 → 100755
Empty file.
Empty file modified src/main/java/org/wargamer2010/signshop/Seller.java
100644 → 100755
Empty file.
10 changes: 8 additions & 2 deletions src/main/java/org/wargamer2010/signshop/SignShop.java
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.event.Event;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
Expand Down Expand Up @@ -273,11 +274,11 @@ public void onEnable() {
}
//Setup worth
if (getSignShopConfig().getEnablePriceFromWorth()) {
if (Bukkit.getServer().getPluginManager().getPlugin("CMI") != null && Bukkit.getServer().getPluginManager().getPlugin("CMI").isEnabled()) {
if (isPluginRunning("CMI")) {
worthHandler = new CMIWorthHandler();
log("Using worth information from CMI.", Level.INFO);
}
else if (Bukkit.getServer().getPluginManager().getPlugin("Essentials") != null && Bukkit.getServer().getPluginManager().getPlugin("Essentials").isEnabled()) {
else if (isPluginRunning("Essentials")) {
worthHandler = new EssentialsWorthHandler();
log("Using worth information from Essentials.", Level.INFO);
}
Expand Down Expand Up @@ -309,6 +310,11 @@ else if (Bukkit.getServer().getPluginManager().getPlugin("Essentials") != null &
log("v" + pdfFile.getVersion() + " Enabled", Level.INFO);
}

private static boolean isPluginRunning(String name) {
Plugin plugin = Bukkit.getServer().getPluginManager().getPlugin(name);
return plugin != null && plugin.isEnabled();
}

@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command cmd, @NotNull String commandLabel, @NotNull String[] args) {
String commandName = cmd.getName().toLowerCase();
Expand Down
Empty file modified src/main/java/org/wargamer2010/signshop/Vault.java
100644 → 100755
Empty file.
Empty file modified src/main/java/org/wargamer2010/signshop/api/Reloadable.java
100644 → 100755
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file modified src/main/java/org/wargamer2010/signshop/data/BookFactory.java
100644 → 100755
Empty file.
Empty file modified src/main/java/org/wargamer2010/signshop/data/BookItem.java
100644 → 100755
Empty file.
Empty file modified src/main/java/org/wargamer2010/signshop/data/IBookItem.java
100644 → 100755
Empty file.
Empty file modified src/main/java/org/wargamer2010/signshop/data/IItemTags.java
100644 → 100755
Empty file.
Empty file modified src/main/java/org/wargamer2010/signshop/data/ItemTags.java
100644 → 100755
Empty file.
Empty file.
Empty file modified src/main/java/org/wargamer2010/signshop/data/SSDatabase.java
100644 → 100755
Empty file.
Empty file modified src/main/java/org/wargamer2010/signshop/data/SignShopBooks.java
100644 → 100755
Empty file.
Empty file.
12 changes: 9 additions & 3 deletions src/main/java/org/wargamer2010/signshop/data/Storage.java
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,9 @@ public static Storage init(File ymlFile) {

public static void dispose() {
instance = null;
fileSaveWorker.stop();
if (fileSaveWorker != null) {
fileSaveWorker.stop();
}
}

public static Storage get() {
Expand Down Expand Up @@ -225,8 +227,12 @@ private boolean loadSellerFromSettings(String key, HashMap<String,List<String>>
throw storageEx;
}
if(!itemUtil.clickedSign(seller_sign)) {
storageEx.setReason(StorageExceptionReason.SIGN_LOCATION_NOT_ACTUALLY_SIGN);
throw storageEx;
// On Folia, block checks during startup may fail due to threading
// Log a warning but continue loading - shop will be validated on first use
SignShop.log("Could not validate sign at " + seller_sign.getLocation() + " during startup (may be Folia threading issue). Will validate on first use.", java.util.logging.Level.WARNING);
// Don't throw exception - allow shop to load and be validated later
// storageEx.setReason(StorageExceptionReason.SIGN_LOCATION_NOT_ACTUALLY_SIGN);
// throw storageEx;
}
seller_activatables = signshopUtil.getBlocksFromLocStringList(getSetting(sellerSettings, "activatables"), world);
seller_containables = signshopUtil.getBlocksFromLocStringList(getSetting(sellerSettings, "containables"), world);
Expand Down
29 changes: 24 additions & 5 deletions src/main/java/org/wargamer2010/signshop/data/serialization/BukkitSerialization.java
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.util.io.BukkitObjectInputStream;
import org.bukkit.util.io.BukkitObjectOutputStream;
import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;

/**
* Legacy Java object serialization for ItemStacks.
Expand All @@ -23,10 +23,29 @@
* @see ItemSerializer
*/
public class BukkitSerialization {
/**
* SnakeYAML 2.6 (shipped with Minecraft 26.1 and up) dropped its bundled Base64Coder,
* so we encode ourselves. Same layout as before: 76 character lines separated by the
* platform line ending, and a lenient decoder so shops written by older builds
* (or on Windows, with CRLF) still load.
*/
private static final Base64.Encoder ENCODER =
Base64.getMimeEncoder(76, System.lineSeparator().getBytes());
private static final Base64.Decoder DECODER = Base64.getMimeDecoder();

private BukkitSerialization() {

}

/**
* Base64 encodes the given bytes the way snakeyaml used to: 76 character lines, and a line
* break after the last one as well, so re-saving a shop leaves the file byte for byte the same.
*/
private static String encodeBase64(byte[] data) {
String encoded = ENCODER.encodeToString(data);
return encoded.isEmpty() ? encoded : encoded + System.lineSeparator();
}

/**
* Converts the player inventory to a String array of Base64 strings. First string is the content and second string is the armor.
*
Expand Down Expand Up @@ -68,7 +87,7 @@ public static String itemStackArrayToBase64(ItemStack[] items) throws IllegalSta

// Serialize that array
dataOutput.close();
return Base64Coder.encodeLines(outputStream.toByteArray());
return encodeBase64(outputStream.toByteArray());
} catch (Exception e) {
throw new IllegalStateException("Unable to save item stacks.", e);
}
Expand Down Expand Up @@ -99,7 +118,7 @@ public static String toBase64(Inventory inventory) throws IllegalStateException

// Serialize that array
dataOutput.close();
return Base64Coder.encodeLines(outputStream.toByteArray());
return encodeBase64(outputStream.toByteArray());
} catch (Exception e) {
throw new IllegalStateException("Unable to save item stacks.", e);
}
Expand All @@ -117,7 +136,7 @@ public static String toBase64(Inventory inventory) throws IllegalStateException
*/
public static Inventory fromBase64(String data) throws IOException {
try {
ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64Coder.decodeLines(data));
ByteArrayInputStream inputStream = new ByteArrayInputStream(DECODER.decode(data));
BukkitObjectInputStream dataInput = new BukkitObjectInputStream(inputStream);
Inventory inventory = Bukkit.getServer().createInventory(null, dataInput.readInt());

Expand All @@ -142,7 +161,7 @@ public static Inventory fromBase64(String data) throws IOException {
*/
public static ItemStack[] itemStackArrayFromBase64(String data) throws IOException {
try {
ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64Coder.decodeLines(data));
ByteArrayInputStream inputStream = new ByteArrayInputStream(DECODER.decode(data));
BukkitObjectInputStream dataInput = new BukkitObjectInputStream(inputStream);
ItemStack[] items = new ItemStack[dataInput.readInt()];

Expand Down
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file modified src/main/java/org/wargamer2010/signshop/events/SSEvent.java
100644 → 100755
Empty file.
Empty file.
Empty file.
Empty file modified src/main/java/org/wargamer2010/signshop/events/SSLinkEvent.java
100644 → 100755
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file modified src/main/java/org/wargamer2010/signshop/hooks/BentoBoxHook.java
100644 → 100755
Empty file.
Empty file.
Empty file.
Empty file modified src/main/java/org/wargamer2010/signshop/hooks/Hook.java
100644 → 100755
Empty file.
Empty file modified src/main/java/org/wargamer2010/signshop/hooks/HookManager.java
100644 → 100755
Empty file.
Empty file modified src/main/java/org/wargamer2010/signshop/hooks/LWCHook.java
100644 → 100755
Empty file.
Empty file modified src/main/java/org/wargamer2010/signshop/hooks/LandsHook.java
100644 → 100755
Empty file.
Empty file.
Empty file modified src/main/java/org/wargamer2010/signshop/hooks/TownyHook.java
100644 → 100755
Empty file.
Empty file.
Empty file.
13 changes: 8 additions & 5 deletions src/main/java/org/wargamer2010/signshop/incompatibility/IncompatibilityDetector.java
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@
*
* {@literal @}Override
* public boolean isRelevantForCurrentVersion() {
* String version = Bukkit.getVersion();
* return version.contains("1.21.10") || version.contains("1.21.1");
* return versionUtil.isAtLeast("1.21.10");
* }
*
* {@literal @}Override
Expand Down Expand Up @@ -70,12 +69,16 @@ public interface IncompatibilityDetector {
* <p>This allows detectors to be automatically disabled if running on a Spigot
* version where the incompatibility doesn't exist (e.g., if Spigot fixes the bug).</p>
*
* <p>Compare with {@link org.wargamer2010.signshop.util.versionUtil#isAtLeast(String)} instead
* of matching on the version string. Minecraft switched from 1.x to a year based scheme
* (26.1, 26.1.1, 26.1.2, 26.2, ...), so a check like {@code version.contains("1.21")} quietly
* stops matching on newer servers.</p>
*
* <p><b>Example:</b></p>
* <pre>
* public boolean isRelevantForCurrentVersion() {
* String version = Bukkit.getVersion();
* // Only relevant for Spigot 1.21.10 through 1.21.19
* return version.contains("1.21.1") && !version.contains("1.21.2");
* // 1.21.10 up to, but not including, 26.2
* return versionUtil.isAtLeast("1.21.10") {@literal &&} !versionUtil.isAtLeast("26.2");
* }
* </pre>
*
Expand Down
Empty file.
Loading