diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/papi/CraftEngineExpansion.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/papi/CraftEngineExpansion.java
new file mode 100644
index 000000000..e6c0866ed
--- /dev/null
+++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/papi/CraftEngineExpansion.java
@@ -0,0 +1,84 @@
+package net.momirealms.craftengine.bukkit.compatibility.papi;
+
+import me.clip.placeholderapi.expansion.PlaceholderExpansion;
+import net.momirealms.craftengine.bukkit.api.BukkitAdaptors;
+import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
+import net.momirealms.craftengine.core.plugin.CraftEngine;
+import net.momirealms.craftengine.core.plugin.context.CooldownData;
+import net.momirealms.craftengine.core.util.CountdownFormatter;
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class CraftEngineExpansion extends PlaceholderExpansion {
+ private final CraftEngine plugin;
+
+ public CraftEngineExpansion(CraftEngine plugin) {
+ this.plugin = plugin;
+ }
+
+ @NotNull
+ @Override
+ public String getIdentifier() {
+ return "ce";
+ }
+
+ @NotNull
+ @Override
+ public String getAuthor() {
+ return "jhqwqmc";
+ }
+
+ @NotNull
+ @Override
+ public String getVersion() {
+ return "1.0";
+ }
+
+ /**
+ * 用法:(小括号括起来的为必填,中括号括起来的为选填)
+ *
+ * %ce_cd_(key)|[format]%
+ */
+ @Override
+ public @Nullable String onPlaceholderRequest(Player bukkitPlayer, @NotNull String params) {
+ BukkitServerPlayer player = bukkitPlayer != null ? BukkitAdaptors.adapt(bukkitPlayer) : null;
+ int index = params.indexOf('_');
+ String action = index > 0 ? params.substring(0, index) : params;
+ String[] param;
+ if (index > 0) {
+ String substring = params.substring(index + 1);
+ int i = substring.indexOf('|');
+ if (i > 0) {
+ param = new String[]{substring.substring(0, i), substring.substring(i + 1)};
+ } else {
+ param = new String[]{substring};
+ }
+ } else {
+ param = new String[0];
+ }
+ return switch (action) {
+ case "cd", "cooldown" -> handleCooldown(player, param);
+ default -> null;
+ };
+ }
+
+ @Nullable
+ private static String handleCooldown(@Nullable BukkitServerPlayer player, String[] param) {
+ if (player == null || param.length < 1) {
+ return null;
+ }
+ CooldownData cooldown = player.cooldown();
+ if (cooldown == null) {
+ return null;
+ }
+ Long ms = cooldown.getCooldown(param[0]);
+ if (ms == null) {
+ return null;
+ }
+ if (param.length >= 2) {
+ return CountdownFormatter.of(param[1]).format(ms);
+ }
+ return String.valueOf(ms);
+ }
+}
diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/papi/PlaceholderAPIUtils.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/papi/PlaceholderAPIUtils.java
index 9f4dce361..fbfe15550 100644
--- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/papi/PlaceholderAPIUtils.java
+++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/papi/PlaceholderAPIUtils.java
@@ -21,5 +21,6 @@ public class PlaceholderAPIUtils {
new ImageExpansion(plugin).register();
new ShiftExpansion(plugin).register();
new CheckItemExpansion(plugin).register();
+ new CraftEngineExpansion(plugin).register();
}
}
diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java
index be5385367..3b6b2d0e3 100644
--- a/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java
+++ b/core/src/main/java/net/momirealms/craftengine/core/item/AbstractItemManager.java
@@ -603,7 +603,7 @@ public abstract class AbstractItemManager extends AbstractModelGenerator impl
// 如果不是原版物品,那么加入ce的标识符
if (!isVanillaItem)
- itemBuilder.dataModifier(new IdProcessor(id));
+ itemBuilder.dataModifier(new IdProcessor<>(id));
// 事件
Map>> eventTriggerListMap;
diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/ItemProcessorFactory.java b/core/src/main/java/net/momirealms/craftengine/core/item/ItemProcessorFactory.java
index 52eb62df3..8e3aac5ab 100644
--- a/core/src/main/java/net/momirealms/craftengine/core/item/ItemProcessorFactory.java
+++ b/core/src/main/java/net/momirealms/craftengine/core/item/ItemProcessorFactory.java
@@ -2,7 +2,7 @@ package net.momirealms.craftengine.core.item;
import net.momirealms.craftengine.core.item.processor.ItemProcessor;
-public interface ItemProcessorFactory {
+public interface ItemProcessorFactory {
- T create(Object arg);
+ ItemProcessor create(Object arg);
}
diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/processor/IdProcessor.java b/core/src/main/java/net/momirealms/craftengine/core/item/processor/IdProcessor.java
index c5d3806b5..ac42c108b 100644
--- a/core/src/main/java/net/momirealms/craftengine/core/item/processor/IdProcessor.java
+++ b/core/src/main/java/net/momirealms/craftengine/core/item/processor/IdProcessor.java
@@ -5,9 +5,9 @@ import net.momirealms.craftengine.core.item.ItemBuildContext;
import net.momirealms.craftengine.core.item.ItemProcessorFactory;
import net.momirealms.craftengine.core.util.Key;
-public class IdProcessor implements ItemProcessor {
+public class IdProcessor implements ItemProcessor {
public static final String CRAFT_ENGINE_ID = "craftengine:id";
- public static final ItemProcessorFactory FACTORY = new Factory();
+ public static final ItemProcessorFactory> FACTORY = new Factory<>();
private final Key argument;
public IdProcessor(Key argument) {
@@ -19,17 +19,17 @@ public class IdProcessor implements ItemProcessor {
}
@Override
- public Item> apply(Item> item, ItemBuildContext context) {
+ public Item apply(Item item, ItemBuildContext context) {
item.customId(this.argument);
return item;
}
- private static class Factory implements ItemProcessorFactory {
+ private static class Factory implements ItemProcessorFactory {
@Override
- public IdProcessor create(Object arg) {
+ public ItemProcessor create(Object arg) {
String id = arg.toString();
- return new IdProcessor(Key.of(id));
+ return new IdProcessor<>(Key.of(id));
}
}
}
diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/processor/ItemProcessor.java b/core/src/main/java/net/momirealms/craftengine/core/item/processor/ItemProcessor.java
index 661496897..6231646d0 100644
--- a/core/src/main/java/net/momirealms/craftengine/core/item/processor/ItemProcessor.java
+++ b/core/src/main/java/net/momirealms/craftengine/core/item/processor/ItemProcessor.java
@@ -4,11 +4,11 @@ import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.item.ItemBuildContext;
import net.momirealms.sparrow.nbt.CompoundTag;
-public interface ItemProcessor {
+public interface ItemProcessor {
- Item> apply(Item> item, ItemBuildContext context);
+ Item apply(Item item, ItemBuildContext context);
- default Item> prepareNetworkItem(Item> item, ItemBuildContext context, CompoundTag networkData) {
+ default Item prepareNetworkItem(Item item, ItemBuildContext context, CompoundTag networkData) {
return item;
}
}
diff --git a/core/src/main/java/net/momirealms/craftengine/core/item/processor/ItemProcessorType.java b/core/src/main/java/net/momirealms/craftengine/core/item/processor/ItemProcessorType.java
index ef3657306..b5154e2e8 100644
--- a/core/src/main/java/net/momirealms/craftengine/core/item/processor/ItemProcessorType.java
+++ b/core/src/main/java/net/momirealms/craftengine/core/item/processor/ItemProcessorType.java
@@ -3,5 +3,5 @@ package net.momirealms.craftengine.core.item.processor;
import net.momirealms.craftengine.core.item.ItemProcessorFactory;
import net.momirealms.craftengine.core.util.Key;
-public record ItemProcessorType(Key id, ItemProcessorFactory factory) {
+public record ItemProcessorType(Key id, ItemProcessorFactory factory) {
}
diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/CooldownData.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/CooldownData.java
index 7ac74b314..88e7184cc 100644
--- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/CooldownData.java
+++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/CooldownData.java
@@ -44,6 +44,10 @@ public class CooldownData {
this.cooldownMap.clear();
}
+ public Long getCooldown(String key) {
+ return this.cooldownMap.get(key);
+ }
+
public static byte[] toBytes(CooldownData data) throws IOException {
CompoundTag tag = new CompoundTag();
long currentTime = System.currentTimeMillis();
diff --git a/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java b/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java
index 2aff93d1c..1f643f31d 100644
--- a/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java
+++ b/core/src/main/java/net/momirealms/craftengine/core/registry/BuiltInRegistries.java
@@ -18,7 +18,6 @@ import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
import net.momirealms.craftengine.core.item.behavior.ItemBehaviorType;
import net.momirealms.craftengine.core.item.equipment.Equipment;
import net.momirealms.craftengine.core.item.equipment.EquipmentType;
-import net.momirealms.craftengine.core.item.processor.ItemProcessor;
import net.momirealms.craftengine.core.item.processor.ItemProcessorType;
import net.momirealms.craftengine.core.item.recipe.CustomSmithingTransformRecipe;
import net.momirealms.craftengine.core.item.recipe.Recipe;
@@ -66,7 +65,7 @@ import net.momirealms.craftengine.core.util.ResourceKey;
public final class BuiltInRegistries {
public static final Registry BLOCK = createDynamicBoundRegistry(Registries.BLOCK, 512);
public static final Registry> BLOCK_BEHAVIOR_TYPE = createConstantBoundRegistry(Registries.BLOCK_BEHAVIOR_TYPE, 64);
- public static final Registry> ITEM_PROCESSOR_TYPE = createConstantBoundRegistry(Registries.ITEM_PROCESSOR_TYPE, 64);
+ public static final Registry> ITEM_PROCESSOR_TYPE = createConstantBoundRegistry(Registries.ITEM_PROCESSOR_TYPE, 64);
public static final Registry> ITEM_BEHAVIOR_TYPE = createConstantBoundRegistry(Registries.ITEM_BEHAVIOR_TYPE, 64);
public static final Registry>> PROPERTY_TYPE = createConstantBoundRegistry(Registries.PROPERTY_TYPE, 16);
public static final Registry> LOOT_FUNCTION_TYPE = createConstantBoundRegistry(Registries.LOOT_FUNCTION_TYPE, 32);
diff --git a/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java b/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java
index f13ddf7f4..24ce76c49 100644
--- a/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java
+++ b/core/src/main/java/net/momirealms/craftengine/core/registry/Registries.java
@@ -18,7 +18,6 @@ import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
import net.momirealms.craftengine.core.item.behavior.ItemBehaviorType;
import net.momirealms.craftengine.core.item.equipment.Equipment;
import net.momirealms.craftengine.core.item.equipment.EquipmentType;
-import net.momirealms.craftengine.core.item.processor.ItemProcessor;
import net.momirealms.craftengine.core.item.processor.ItemProcessorType;
import net.momirealms.craftengine.core.item.recipe.CustomSmithingTransformRecipe;
import net.momirealms.craftengine.core.item.recipe.Recipe;
@@ -69,7 +68,7 @@ public final class Registries {
public static final Key ROOT_REGISTRY = Key.withDefaultNamespace("root");
public static final ResourceKey> BLOCK = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("block"));
- public static final ResourceKey>> ITEM_PROCESSOR_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("item_processor_type"));
+ public static final ResourceKey>> ITEM_PROCESSOR_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("item_processor_type"));
public static final ResourceKey>>> PROPERTY_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("property_type"));
public static final ResourceKey>> BLOCK_BEHAVIOR_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("block_behavior_type"));
public static final ResourceKey>> ITEM_BEHAVIOR_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("item_behavior_type"));
diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/CountdownFormatter.java b/core/src/main/java/net/momirealms/craftengine/core/util/CountdownFormatter.java
new file mode 100644
index 000000000..d1657faa6
--- /dev/null
+++ b/core/src/main/java/net/momirealms/craftengine/core/util/CountdownFormatter.java
@@ -0,0 +1,125 @@
+package net.momirealms.craftengine.core.util;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public final class CountdownFormatter {
+ private static final Pattern YEAR_PATTERN = Pattern.compile("[Yy]+");
+ private static final Pattern MONTH_PATTERN = Pattern.compile("M+");
+ private static final Pattern DAY_PATTERN = Pattern.compile("[Dd]+");
+ private static final Pattern HOUR_PATTERN = Pattern.compile("[Hh]+");
+ private static final Pattern MINUTE_PATTERN = Pattern.compile("m+");
+ private static final Pattern SECOND_PATTERN = Pattern.compile("s+");
+ private static final Pattern MILLIS_PATTERN = Pattern.compile("S+");
+ private final String pattern;
+ private final Matcher yearMatcher;
+ private final Matcher monthMatcher;
+ private final Matcher dayMatcher;
+ private final Matcher hourMatcher;
+ private final Matcher minuteMatcher;
+ private final Matcher secondMatcher;
+ private final Matcher millisMatcher;
+ private final boolean hasYear;
+ private final boolean hasMonth;
+ private final boolean hasDay;
+ private final boolean hasHour;
+ private final boolean hasMinute;
+ private final boolean hasSecond;
+ private final boolean hasMillis;
+
+ private CountdownFormatter(String pattern) {
+ this.pattern = pattern;
+ this.yearMatcher = YEAR_PATTERN.matcher(pattern);
+ this.monthMatcher = MONTH_PATTERN.matcher(pattern);
+ this.dayMatcher = DAY_PATTERN.matcher(pattern);
+ this.hourMatcher = HOUR_PATTERN.matcher(pattern);
+ this.minuteMatcher = MINUTE_PATTERN.matcher(pattern);
+ this.secondMatcher = SECOND_PATTERN.matcher(pattern);
+ this.millisMatcher = MILLIS_PATTERN.matcher(pattern);
+ this.hasYear = yearMatcher.find();
+ this.hasMonth = monthMatcher.find();
+ this.hasDay = dayMatcher.find();
+ this.hasHour = hourMatcher.find();
+ this.hasMinute = minuteMatcher.find();
+ this.hasSecond = secondMatcher.find();
+ this.hasMillis = millisMatcher.find();
+ }
+
+ public static CountdownFormatter of(String pattern) {
+ return new CountdownFormatter(pattern);
+ }
+
+ public String format(long millis) {
+ long years = 0, months = 0, days = 0, hours = 0, minutes = 0, seconds = 0;
+
+ if (!hasMillis) {
+ seconds = millis / 1000;
+ millis = 0;
+ }
+ if (!hasSecond) {
+ minutes = seconds / 60;
+ seconds = 0;
+ }
+ if (!hasMinute) {
+ hours = minutes / 60;
+ minutes = 0;
+ }
+ if (!hasHour) {
+ days = hours / 24;
+ hours = 0;
+ }
+ if (!hasDay) {
+ months = days / 30;
+ days = 0;
+ }
+ if (!hasMonth) {
+ years = months / 12;
+ months = 0;
+ }
+
+ if (hasMillis && hasSecond) {
+ seconds = millis / 1000;
+ millis %= 1000;
+ }
+ if (hasSecond && hasMinute) {
+ minutes = seconds / 60;
+ seconds %= 60;
+ }
+ if (hasMinute && hasHour) {
+ hours = minutes / 60;
+ minutes %= 60;
+ }
+ if (hasHour && hasDay) {
+ days = hours / 24;
+ hours %= 24;
+ }
+ if (hasDay && hasMonth) {
+ months = days / 30;
+ days %= 30;
+ }
+ if (hasMonth && hasYear) {
+ years = months / 12;
+ months %= 12;
+ }
+
+ StringBuilder result = new StringBuilder(pattern);
+ replaceUnit(result, yearMatcher, years);
+ replaceUnit(result, monthMatcher, months);
+ replaceUnit(result, dayMatcher, days);
+ replaceUnit(result, hourMatcher, hours);
+ replaceUnit(result, minuteMatcher, minutes);
+ replaceUnit(result, secondMatcher, seconds);
+ replaceUnit(result, millisMatcher, millis);
+
+ return result.toString();
+ }
+
+ private void replaceUnit(StringBuilder text, Matcher matcher, long value) {
+ matcher.reset(text);
+ if (matcher.find()) {
+ int length = matcher.group().length();
+ String formatted = String.format("%0" + length + "d", value);
+ text.replace(matcher.start(), matcher.end(), formatted);
+ }
+ }
+}