Skip to content
Closed
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package org.wargamer2010.signshop.incompatibility.detectors;

import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
Expand All @@ -9,6 +8,7 @@
import org.wargamer2010.signshop.SignShop;
import org.wargamer2010.signshop.incompatibility.IncompatibilityDetector;
import org.wargamer2010.signshop.incompatibility.IncompatibilityType;
import org.wargamer2010.signshop.util.versionUtil;

import java.util.NoSuchElementException;

Expand Down Expand Up @@ -144,30 +144,19 @@ public IncompatibilityType detect(ItemStack item) {
/**
* Checks if this incompatibility affects the current Spigot version.
*
* <p><b>Logic:</b></p>
* <ul>
* <li>Check if version contains "1.21.1" (covers 1.21.10 through 1.21.19)</li>
* <li>Or check if version contains "1.21.2" or higher</li>
* <li>Returns false if fixed version is known and current version is >= fixed version</li>
* </ul>
* <p>Affects 1.21.10 and everything after it. Minecraft dropped the leading "1." in 2026,
* so the drops after 1.21.11 are 26.1, 26.1.1, 26.1.2, 26.2 and so on. Comparing the parts
* numerically keeps those in the right order, since 26 is greater than 1.</p>
*
* @return true if current Spigot version is affected by this incompatibility
*/
@Override
public boolean isRelevantForCurrentVersion() {
String version = Bukkit.getVersion();

// If there's a known fix version, check if we're past it
if (FIXED_IN_VERSION != null && isVersionGreaterOrEqual(version)) {
if (FIXED_IN_VERSION != null && versionUtil.isAtLeast(FIXED_IN_VERSION)) {
return false; // Issue is fixed in this version
}

// Check if we're on an affected version (1.21.10+)
// This is a simplified check - in production you'd want proper version parsing
return version.contains("1.21.1") || // 1.21.10-1.21.19
version.contains("1.21.2") || // 1.21.20+
version.contains("1.22") || // Future versions
version.contains("1.23"); // Future versions
return versionUtil.isAtLeast(FIRST_AFFECTED_VERSION);
}

/**
Expand All @@ -184,44 +173,6 @@ public String getFixedInVersion() {
// Helper Methods
// ========================================

/**
* Simplified version comparison.
*
* <p>NOTE: This is a basic implementation. For production use, consider
* a more robust version comparison library.</p>
*
* @param current Current version string
* @return true if current >= target
*/
private boolean isVersionGreaterOrEqual(String current) {
// Extract version numbers (simplified - doesn't handle all edge cases)
try {
String currentClean = current.replaceAll("[^0-9.]", "");
String targetClean = PlayerHeadIncompatibilityDetector.FIXED_IN_VERSION.replaceAll("[^0-9.]", "");

String[] currentParts = currentClean.split("\\.");
String[] targetParts = targetClean.split("\\.");

int maxLength = Math.max(currentParts.length, targetParts.length);

for (int i = 0; i < maxLength; i++) {
int currentPart = i < currentParts.length ?
Integer.parseInt(currentParts[i]) : 0;
int targetPart = i < targetParts.length ?
Integer.parseInt(targetParts[i]) : 0;

if (currentPart > targetPart) return true;
if (currentPart < targetPart) return false;
}

return true; // Equal

} catch (NumberFormatException e) {
// If parsing fails, assume version is relevant (safer)
return false;
}
}

/**
* Logs debug messages when debugging is enabled.
*
Expand Down
107 changes: 83 additions & 24 deletions src/main/java/org/wargamer2010/signshop/util/versionUtil.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@

package org.wargamer2010.signshop.util;

import org.bukkit.Bukkit;
import org.wargamer2010.signshop.SignShop;

import java.util.logging.Level;
import java.util.regex.Pattern;

/**
* Bukkit/Spigot version detection and compatibility checking.
Expand All @@ -19,10 +17,17 @@
* <li><b>Post145:</b> Bukkit 1.4.5-R0.3+ (modern API with ItemMeta)</li>
* </ul>
*
* <h2>Two numbering schemes</h2>
* <p>Minecraft numbered releases 1.0 up to 1.21.11, then switched to a year based
* scheme starting with 26.1 (26.1, 26.1.1, 26.1.2, 26.2, ...). Both look the same to
* the comparison below: the parts are compared as numbers, and since 26 &gt; 1 every
* year based release sorts above every 1.x release.</p>
*
* @see SSBukkitVersion
*/
public class versionUtil {
private static SSBukkitVersion cachedVersion = SSBukkitVersion.TBD;
private static int[] cachedServerVersion = null;

private versionUtil() {

Expand All @@ -35,18 +40,19 @@ private versionUtil() {
public static SSBukkitVersion getBukkitVersionType() {
if(cachedVersion == SSBukkitVersion.TBD) {
String bukkitversion = Bukkit.getServer().getBukkitVersion();
String[] versionbits = new String[1];
if(bukkitversion.contains("-"))
versionbits = bukkitversion.split("-");
if(versionbits.length < 2)
return SSBukkitVersion.Unknown;
String[] versionbits = bukkitversion.split("-");
if(versionbits.length < 2) {
cachedVersion = SSBukkitVersion.Unknown;
SignShop.log("Could not determine Bukkit compatibility from this string: " + bukkitversion, Level.SEVERE);
return cachedVersion;
}

int cmp = versionUtil.compare(versionbits[0], "1.4.5");
int cmp = compare(versionbits[0], "1.4.5");

if(cmp < 0) // < 1.4.5
cachedVersion = SSBukkitVersion.Pre145;
else if(cmp == 0) { // == 1.4.5
if(versionUtil.compare(versionbits[1], "R0.3") < 0) { // < 1.4.5-R0.3
if(compare(versionbits[1], "R0.3") < 0) { // < 1.4.5-R0.3
cachedVersion = SSBukkitVersion.Pre145; // It didn't have support for ItemMeta so pre-Major-Overhaul
} else { // >= 1.4.5-R0.3
cachedVersion = SSBukkitVersion.Post145;
Expand All @@ -56,12 +62,36 @@ else if(cmp == 0) { // == 1.4.5
cachedVersion = SSBukkitVersion.Post145;
}

if(cachedVersion == SSBukkitVersion.Unknown)
SignShop.log("Could not determine Bukkit compatibility from this string: " + Bukkit.getServer().getBukkitVersion(), Level.SEVERE);

return cachedVersion;
}

/**
* The Minecraft version this server runs, split into its numeric parts.
* "26.1.2-R0.1-SNAPSHOT" gives {26, 1, 2}, "1.21.11-R0.1-SNAPSHOT" gives {1, 21, 11}.
*
* @return the version parts, or an empty array if the version string made no sense
*/
public static int[] getServerVersion() {
if(cachedServerVersion == null)
cachedServerVersion = parse(Bukkit.getServer().getBukkitVersion());

return cachedServerVersion.clone();
}

/**
* Checks whether the running server is at least the given Minecraft version.
* Works across both numbering schemes, so isAtLeast("1.21.10") is also true on 26.1 and up.
*
* @param version version to check against, e.g. "1.21.10" or "26.1"
* @return true if the server is that version or newer, false if it is older or unreadable
*/
public static boolean isAtLeast(String version) {
int[] server = getServerVersion();
if(server.length == 0)
return false;

return compare(server, parse(version)) >= 0;
}

/**
* Compare v1 with v2 and returns the result as an int
Expand All @@ -70,22 +100,51 @@ else if(cmp == 0) { // == 1.4.5
* @param v2 Version string #2
* @return {@literal Returns < 0 if v1 < v2, > 0 if v1 > v2, 0 if v1 == v2}
*/
private static int compare(String v1, String v2) {
String s1 = normalisedVersion(v1);
String s2 = normalisedVersion(v2);
return s1.compareTo(s2);
public static int compare(String v1, String v2) {
return compare(parse(v1), parse(v2));
}

private static String normalisedVersion(String version) {
return normalisedVersion(version, ".", 4);
private static int compare(int[] v1, int[] v2) {
for(int i = 0; i < Math.max(v1.length, v2.length); i++) {
int left = i < v1.length ? v1[i] : 0;
int right = i < v2.length ? v2[i] : 0;

if(left != right)
return left < right ? -1 : 1;
}

return 0;
}

private static String normalisedVersion(String version, String sep, int maxWidth) {
String[] split = Pattern.compile(sep, Pattern.LITERAL).split(version);
StringBuilder sb = new StringBuilder((version.length() * 4)); // * 4 since we'll be adding chars
for (String s : split) {
sb.append(String.format("%" + maxWidth + 's', s));
/**
* Pulls the numbers out of a version string. Anything that isn't a number is dropped,
* so "1.21.11-R0.1-SNAPSHOT" stops at the dash and "R0.3" reads as {0, 3}.
*/
private static int[] parse(String version) {
if(version == null || version.isEmpty())
return new int[0];

String[] parts = version.split("-")[0].split("\\.");
int[] numbers = new int[parts.length];
int found = 0;

for(String part : parts) {
String digits = part.replaceAll("[^0-9]", "");
if(digits.isEmpty())
break;

try {
numbers[found++] = Integer.parseInt(digits);
} catch(NumberFormatException e) {
break; // Absurdly long number, nothing sensible left to compare
}
}
return sb.toString();

if(found == numbers.length)
return numbers;

int[] trimmed = new int[found];
System.arraycopy(numbers, 0, trimmed, 0, found);
return trimmed;
}
}