9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-26 02:19:23 +00:00

Merge pull request #133 from Xiao-MoMi/dev

0.0.50
This commit is contained in:
XiaoMoMi
2025-04-23 22:10:52 +08:00
committed by GitHub
88 changed files with 2021 additions and 945 deletions

View File

@@ -90,13 +90,19 @@ Given the extensive and intricate nature of plugin configurations, a modular tem
### 🛠️ Models
The plugin enables model inheritance and texture overrides through configuration, while supporting [all item models](https://misode.github.io/assets/item/) from version 1.21.4 onward. It incorporates a version migration system that automatically downgrades 1.21.4+ item models to legacy formats with maximum backward compatibility.
### Breaking Changes You Have to Know & Possible Incompatibility with Other Plugins
- CraftEngine injects into PalettedContainer to ensure efficient storage and synchronization of plugin block data. This may cause conflicts with some other plugins that modify the palette. When analyzing server performance using Spark, palette operation overhead will be attributed to the CraftEngine plugin in the profiling results..
- CraftEngine injects into FurnaceBlockEntity to modify its recipe fetching logic.
- CraftEngine uses real server-side blocks, any plugin relying on Bukkit's Material class will fail to correctly identify custom block types. The proper approach is to use alternatives like BlockState#getBlock (mojmap) instead of the Material class.
- CraftEngine implements 0-tick collision entities by extending certain Minecraft entities, ensuring hard collision works correctly on the server side (e.g., making a pig stand on a chair). However, some anti-cheat plugins do not check entity AABB (Axis-Aligned Bounding Box) properly when detecting player movement, which may lead to false flags.
- CraftEngine's custom recipe handling may not be fully compatible with other recipe-managing plugins.
## Inspired Projects
This project draws inspiration from the following open-source works:
This project draws inspiration and refers to some implementations from the following open-source works:
+ [Paper](https://github.com/PaperMC/Paper)
+ [LuckPerms](https://github.com/LuckPerms/LuckPerms)
+ [Fabric](https://github.com/FabricMC/fabric)
+ [packetevents](https://github.com/retrooper/packetevents)
+ [NBT](https://github.com/Querz/NBT)
+ [DataFixerUpper](https://github.com/Mojang/DataFixerUpper)
+ [ViaVersion](https://github.com/ViaVersion/ViaVersion)
@@ -142,7 +148,7 @@ repositories {
```
```kotlin
dependencies {
compileOnly("net.momirealms:craft-engine-core:0.0.48")
compileOnly("net.momirealms:craft-engine-bukkit:0.0.48")
compileOnly("net.momirealms:craft-engine-core:0.0.49")
compileOnly("net.momirealms:craft-engine-bukkit:0.0.49")
}
```
```

View File

@@ -170,12 +170,21 @@ furniture:
# Converts the invalid furniture to a valid one
convert:
"namespace:furniture_a": "namespace:furniture_b"
# Hide technical entities used for storing furniture metadata.
# NOTE:
# - These are INVISIBLE entities used internally for tracking furniture states
# - Recommended to keep enabled for better performance
hide-base-entity: true
# Requires a restart to apply
# interaction (best performance)
# boat (better compatibility with some anti-cheat plugin)
collision-entity-type: interaction
emoji:
chat: true
book: true
anvil: true
sign: true
image:
# Block image tags using minecraft:default font in these interfaces
@@ -252,8 +261,6 @@ image:
128: '\uf844'
256: '\uf845'
emoji: {}
recipe:
# Master switch for custom recipes
# NOTE: When enabled, plugin recipes will OVERRIDE vanilla recipes

View File

@@ -114,4 +114,28 @@ warning.config.model.generation.texture.invalid_resource_location: "<yellow>Issu
warning.config.model.generation.parent.invalid_resource_location: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' has a parent argument [<arg:2>] that contains legal characters. Please read https://minecraft.wiki/w/Resource_location#Legal_characters</yellow>"
warning.config.emoji.lack_keywords: "<yellow>Issue found in file <arg:0> - The emoji '<arg:1>' is missing the required 'keywords' argument.</yellow>"
warning.config.emoji.duplicated: "<yellow>Issue found in file <arg:0> - Duplicated emoji '<arg:1>'.</yellow>"
warning.config.emoji.invalid_image: "<yellow>Issue found in file <arg:0> - The emoji '<arg:1>' has an invalid 'image' argument '<arg:2>'.</yellow>"
warning.config.emoji.invalid_image: "<yellow>Issue found in file <arg:0> - The emoji '<arg:1>' has an invalid 'image' argument '<arg:2>'.</yellow>"
warning.config.advancement.duplicated: "<yellow>Issue found in file <arg:0> - Duplicated advancement '<arg:1>'.</yellow>"
warning.config.host.lack_type: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'type' for host.</yellow>"
warning.config.host.invalid_type: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Host 'type' [<arg:0>] is invalid. Please read https://mo-mi.gitbook.io/xiaomomi-plugins/craftengine/plugin-wiki/craftengine/resource-pack/host</yellow>"
warning.config.host.external.lack_url: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'url' for external host.</yellow>"
warning.config.host.alist.lack_api_url: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'api-url' for alist host.</yellow>"
warning.config.host.alist.lack_username: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'username' or environment variable 'CE_ALIST_USERNAME' for alist host.</yellow>"
warning.config.host.alist.lack_password: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'password' or environment variable 'CE_ALIST_PASSWORD' for alist host.</yellow>"
warning.config.host.alist.lack_upload_path: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'upload-path' for alist host.</yellow>"
warning.config.host.dropbox.lack_app_key: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'app-key' or environment variable 'CE_DROPBOX_APP_KEY' for dropbox host.</yellow>"
warning.config.host.dropbox.lack_app_secret: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'app-secret' or environment variable 'CE_DROPBOX_APP_SECRET' for dropbox host.</yellow>"
warning.config.host.dropbox.lack_refresh_token: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'refresh-token' or environment variable 'CE_DROPBOX_REFRESH_TOKEN' for dropbox host.</yellow>"
warning.config.host.dropbox.lack_upload_path: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'upload-path' for dropbox host.</yellow>"
warning.config.host.lobfile.lack_api_key: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'api-key' for lobfile host.</yellow>"
warning.config.host.onedrive.lack_client_id: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'client-id' or environment variable 'CE_ONEDRIVE_CLIENT_ID' for onedrive host.</yellow>"
warning.config.host.onedrive.lack_client_secret: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'client-secret' or environment variable 'CE_ONEDRIVE_CLIENT_SECRET' for onedrive host.</yellow>"
warning.config.host.onedrive.lack_refresh_token: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'refresh-token' or environment variable 'CE_ONEDRIVE_REFRESH_TOKEN' for onedrive host.</yellow>"
warning.config.host.onedrive.lack_upload_path: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'upload-path' for onedrive host.</yellow>"
warning.config.host.s3.lack_endpoint: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'endpoint' for s3 host.</yellow>"
warning.config.host.s3.lack_bucket: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'bucket' for s3 host.</yellow>"
warning.config.host.s3.lack_access_key_id: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'access-key-id' or environment variable 'CE_S3_ACCESS_KEY_ID' for s3 host.</yellow>"
warning.config.host.s3.lack_access_key_secret: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'access-key-secret' or environment variable 'CE_S3_ACCESS_KEY_SECRET' for s3 host.</yellow>"
warning.config.host.s3.lack_upload_path: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'upload-path' for s3 host.</yellow>"
warning.config.host.self.lack_ip: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Missing require argument 'ip' for self host.</yellow>"
warning.config.host.self.invalid_port: "<yellow>Issue found in config.yml at 'resource-pack.delivery.hosting' - Invalid 'port' [<arg:0>] for self host.</yellow>"

View File

@@ -114,4 +114,5 @@ warning.config.model.generation.texture.invalid_resource_location: "<yellow>在
warning.config.model.generation.parent.invalid_resource_location: "<yellow>在文件 <arg:0> 中发现问题 - 配置项 '<arg:1>' 的父模型参数 [<arg:2>] 包含非法字符请参考资源路径规范https://zh.minecraft.wiki/w/%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4ID#%E5%90%88%E6%B3%95%E5%AD%97%E7%AC%A6</yellow>"
warning.config.emoji.lack_keywords: "<yellow>在文件 <arg:0> 中发现问题 - 表情 '<arg:1>' 缺少必要的 'keywords' 配置</yellow>"
warning.config.emoji.duplicated: "<yellow>在文件 <arg:0> 中发现问题 - 表情 '<arg:1>' 重复定义</yellow>"
warning.config.emoji.invalid_image: "<yellow>在文件 <arg:0> 中发现问题 - 表情 '<arg:1>' 使用了无效的 'image' 图片参数 '<arg:2>'.</yellow>"
warning.config.emoji.invalid_image: "<yellow>在文件 <arg:0> 中发现问题 - 表情 '<arg:1>' 使用了无效的 'image' 图片参数 '<arg:2>'.</yellow>"
warning.config.advancement.duplicated: "<yellow>在文件 <arg:0> 中发现问题 - 进度 '<arg:1>' 重复定义</yellow>"

View File

@@ -0,0 +1,62 @@
package net.momirealms.craftengine.bukkit.advancement;
import com.google.gson.JsonElement;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.core.advancement.AbstractAdvancementManager;
import net.momirealms.craftengine.core.pack.LoadingSequence;
import net.momirealms.craftengine.core.pack.Pack;
import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser;
import net.momirealms.craftengine.core.plugin.locale.TranslationManager;
import net.momirealms.craftengine.core.util.GsonHelper;
import net.momirealms.craftengine.core.util.Key;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
public class BukkitAdvancementManager extends AbstractAdvancementManager {
private final BukkitCraftEngine plugin;
private final AdvancementParser advancementParser;
private final Map<Key, JsonElement> advancements = new HashMap<>();
public BukkitAdvancementManager(BukkitCraftEngine plugin) {
super(plugin);
this.plugin = plugin;
this.advancementParser = new AdvancementParser();
}
public void unload() {
advancements.clear();
}
@Override
public ConfigSectionParser parser() {
return this.advancementParser;
}
public class AdvancementParser implements ConfigSectionParser {
public static final String[] CONFIG_SECTION_NAME = new String[] {"advancements", "advancement"};
@Override
public String[] sectionId() {
return CONFIG_SECTION_NAME;
}
@Override
public int loadingSequence() {
return LoadingSequence.ADVANCEMENT;
}
@Override
public void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) {
if (advancements.containsKey(id)) {
TranslationManager.instance().log("warning.config.advancement.duplicated", path.toString(), id.toString());
return;
}
JsonElement jsonTree = GsonHelper.get().toJsonTree(section);
FastNMS.INSTANCE.registerAdvancement(id.decompose(), jsonTree);
advancements.put(id, jsonTree);
}
}
}

View File

@@ -32,6 +32,7 @@ import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockExplodeEvent;
import org.bukkit.event.block.BlockPhysicsEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.event.world.GenericGameEvent;
import org.bukkit.inventory.ItemStack;
@@ -42,7 +43,6 @@ public class BlockEventListener implements Listener {
private final BukkitCraftEngine plugin;
private final boolean enableNoteBlockCheck;
private final BukkitBlockManager manager;
// private static final Set<Material> WATER_BUCKETS = Arrays.stream(ItemKeys.WATER_BUCKETS).map(it -> Registry.MATERIAL.get(new NamespacedKey(it.namespace(), it.value()))).collect(Collectors.toSet());
public BlockEventListener(BukkitCraftEngine plugin, BukkitBlockManager manager, boolean enableNoteBlockCheck) {
this.plugin = plugin;
@@ -50,6 +50,16 @@ public class BlockEventListener implements Listener {
this.enableNoteBlockCheck = enableNoteBlockCheck;
}
@EventHandler(ignoreCancelled = true)
public void onPlayerAttack(EntityDamageByEntityEvent event) {
if (!VersionHelper.isVersionNewerThan1_20_5()) {
if (event.getDamager() instanceof Player player) {
BukkitServerPlayer serverPlayer = plugin.adapt(player);
serverPlayer.setClientSideCanBreakBlock(true);
}
}
}
@EventHandler(ignoreCancelled = true)
public void onPlaceBlock(BlockPlaceEvent event) {
Player player = event.getPlayer();

View File

@@ -1,16 +1,17 @@
package net.momirealms.craftengine.bukkit.entity.furniture;
import net.momirealms.craftengine.bukkit.nms.CollisionEntity;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.core.entity.furniture.Collider;
import net.momirealms.craftengine.core.entity.furniture.ColliderType;
public class BukkitCollider implements Collider {
private final CollisionEntity collisionEntity;
private final ColliderType type;
public BukkitCollider(CollisionEntity collisionEntity, ColliderType type) {
this.collisionEntity = collisionEntity;
this.type = type;
public BukkitCollider(Object world, Object aabb, double x, double y, double z, boolean canProjectileHit, boolean canCollide, boolean blocksBuilding) {
this.collisionEntity = BukkitFurnitureManager.COLLISION_ENTITY_TYPE == ColliderType.INTERACTION ?
FastNMS.INSTANCE.createCollisionInteraction(world, aabb, x, y, z, canProjectileHit, canCollide, blocksBuilding) :
FastNMS.INSTANCE.createCollisionBoat(world, aabb, x, y, z, canProjectileHit, canCollide, blocksBuilding);
}
@Override
@@ -23,11 +24,6 @@ public class BukkitCollider implements Collider {
return this.collisionEntity.getId();
}
@Override
public ColliderType type() {
return this.type;
}
@Override
public Object handle() {
return this.collisionEntity;

View File

@@ -39,6 +39,9 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
public static final NamespacedKey FURNITURE_SEAT_BASE_ENTITY_KEY = Objects.requireNonNull(NamespacedKey.fromString("craftengine:seat_to_base_entity"));
public static final NamespacedKey FURNITURE_SEAT_VECTOR_3F_KEY = Objects.requireNonNull(NamespacedKey.fromString("craftengine:seat_vector"));
public static final NamespacedKey FURNITURE_COLLISION = Objects.requireNonNull(NamespacedKey.fromString("craftengine:collision"));
public static Class<?> COLLISION_ENTITY_CLASS = Interaction.class;
public static Object NMS_COLLISION_ENTITY_TYPE = Reflections.instance$EntityType$INTERACTION;
public static ColliderType COLLISION_ENTITY_TYPE = ColliderType.INTERACTION;
private static BukkitFurnitureManager instance;
private final BukkitCraftEngine plugin;
private final FurnitureParser furnitureParser;
@@ -204,6 +207,9 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
@Override
public void delayedInit() {
COLLISION_ENTITY_CLASS = Config.colliderType() == ColliderType.INTERACTION ? Interaction.class : Boat.class;
NMS_COLLISION_ENTITY_TYPE = Config.colliderType() == ColliderType.INTERACTION ? Reflections.instance$EntityType$INTERACTION : Reflections.instance$EntityType$OAK_BOAT;
COLLISION_ENTITY_TYPE = Config.colliderType();
Bukkit.getPluginManager().registerEvents(this.dismountListener, this.plugin.bootstrap());
Bukkit.getPluginManager().registerEvents(this.furnitureEventListener, this.plugin.bootstrap());
for (World world : Bukkit.getWorlds()) {
@@ -211,7 +217,12 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
for (Entity entity : entities) {
if (entity instanceof ItemDisplay display) {
handleBaseEntityLoadEarly(display);
} else if (entity instanceof Interaction interaction) {
handleCollisionEntityLoadOnEntitiesLoad(interaction);
} else if (entity instanceof Boat boat) {
handleCollisionEntityLoadOnEntitiesLoad(boat);
} else if (entity instanceof Shulker shulker) {
// TODO 移除这一行,预计过一个月
handleCollisionEntityLoadOnEntitiesLoad(shulker);
}
}
@@ -310,29 +321,29 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
this.plugin.scheduler().sync().runLater(() -> handleBaseEntityLoadLate(display, depth + 1), 1, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4);
}
protected void handleCollisionEntityLoadLate(Shulker shulker, int depth) {
// remove the shulker if it's not a collision entity, it might be wrongly copied by WorldEdit
if (FastNMS.INSTANCE.method$CraftEntity$getHandle(shulker) instanceof CollisionEntity) {
protected void handleCollisionEntityLoadLate(Entity entity, int depth) {
// remove the entity if it's not a collision entity, it might be wrongly copied by WorldEdit
if (FastNMS.INSTANCE.method$CraftEntity$getHandle(entity) instanceof CollisionEntity) {
return;
}
// not a collision entity
Byte flag = shulker.getPersistentDataContainer().get(FURNITURE_COLLISION, PersistentDataType.BYTE);
Byte flag = entity.getPersistentDataContainer().get(FURNITURE_COLLISION, PersistentDataType.BYTE);
if (flag == null || flag != 1) {
return;
}
Location location = shulker.getLocation();
Location location = entity.getLocation();
World world = location.getWorld();
int chunkX = location.getBlockX() >> 4;
int chunkZ = location.getBlockZ() >> 4;
if (!FastNMS.INSTANCE.isPreventingStatusUpdates(world, chunkX, chunkZ)) {
shulker.remove();
entity.remove();
return;
}
if (depth > 2) return;
plugin.scheduler().sync().runLater(() -> {
handleCollisionEntityLoadLate(shulker, depth + 1);
handleCollisionEntityLoadLate(entity, depth + 1);
}, 1, world, chunkX, chunkZ);
}
@@ -364,20 +375,20 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
}
}
public void handleCollisionEntityLoadOnEntitiesLoad(Shulker shulker) {
public void handleCollisionEntityLoadOnEntitiesLoad(Entity collisionEntity) {
// faster
if (FastNMS.INSTANCE.method$CraftEntity$getHandle(shulker) instanceof CollisionEntity) {
shulker.remove();
if (FastNMS.INSTANCE.method$CraftEntity$getHandle(collisionEntity) instanceof CollisionEntity) {
collisionEntity.remove();
return;
}
// not a collision entity
Byte flag = shulker.getPersistentDataContainer().get(FURNITURE_COLLISION, PersistentDataType.BYTE);
Byte flag = collisionEntity.getPersistentDataContainer().get(FURNITURE_COLLISION, PersistentDataType.BYTE);
if (flag == null || flag != 1) {
return;
}
shulker.remove();
collisionEntity.remove();
}
private AnchorType getAnchorType(Entity baseEntity, CustomFurniture furniture) {

View File

@@ -2,7 +2,10 @@ package net.momirealms.craftengine.bukkit.entity.furniture;
import com.destroystokyo.paper.event.entity.EntityAddToWorldEvent;
import com.destroystokyo.paper.event.entity.EntityRemoveFromWorldEvent;
import org.bukkit.entity.*;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Entity;
import org.bukkit.entity.ItemDisplay;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
@@ -33,8 +36,8 @@ public class FurnitureEventListener implements Listener {
for (Entity entity : entities) {
if (entity instanceof ItemDisplay itemDisplay) {
this.manager.handleBaseEntityLoadEarly(itemDisplay);
} else if (entity instanceof Shulker shulker) {
this.manager.handleCollisionEntityLoadOnEntitiesLoad(shulker);
} else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) {
this.manager.handleCollisionEntityLoadOnEntitiesLoad(entity);
}
}
}
@@ -45,8 +48,8 @@ public class FurnitureEventListener implements Listener {
for (Entity entity : entities) {
if (entity instanceof ItemDisplay itemDisplay) {
this.manager.handleBaseEntityLoadEarly(itemDisplay);
} else if (entity instanceof Shulker shulker) {
this.manager.handleCollisionEntityLoadOnEntitiesLoad(shulker);
} else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) {
this.manager.handleCollisionEntityLoadOnEntitiesLoad(entity);
}
}
}
@@ -56,8 +59,8 @@ public class FurnitureEventListener implements Listener {
Entity entity = event.getEntity();
if (entity instanceof ItemDisplay itemDisplay) {
this.manager.handleBaseEntityLoadLate(itemDisplay, 0);
} else if (entity instanceof Shulker shulker) {
this.manager.handleCollisionEntityLoadLate(shulker, 0);
} else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) {
this.manager.handleCollisionEntityLoadLate(entity, 0);
}
}
@@ -70,7 +73,7 @@ public class FurnitureEventListener implements Listener {
for (Entity entity : entities) {
if (entity instanceof ItemDisplay) {
this.manager.handleBaseEntityUnload(entity);
} else if (entity instanceof Shulker) {
} else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) {
this.manager.handleCollisionEntityUnload(entity);
}
}
@@ -82,7 +85,7 @@ public class FurnitureEventListener implements Listener {
for (Entity entity : entities) {
if (entity instanceof ItemDisplay) {
this.manager.handleBaseEntityUnload(entity);
} else if (entity instanceof Shulker) {
} else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) {
this.manager.handleCollisionEntityUnload(entity);
}
}
@@ -93,7 +96,7 @@ public class FurnitureEventListener implements Listener {
Entity entity = event.getEntity();
if (entity instanceof ItemDisplay) {
this.manager.handleBaseEntityUnload(entity);
} else if (entity instanceof Shulker) {
} else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) {
this.manager.handleCollisionEntityUnload(entity);
}
}

View File

@@ -65,10 +65,7 @@ public class InteractionHitBox extends AbstractHitBox {
if (blocksBuilding() || this.canBeHitByProjectile()) {
AABB ceAABB = AABB.fromInteraction(new Vec3d(x + offset.x, y + offset.y, z - offset.z), this.size.x, this.size.y);
Object nmsAABB = FastNMS.INSTANCE.constructor$AABB(ceAABB.minX, ceAABB.minY, ceAABB.minZ, ceAABB.maxX, ceAABB.maxY, ceAABB.maxZ);
collider.accept(new BukkitCollider(
FastNMS.INSTANCE.createCollisionShulker(world.serverWorld(), nmsAABB, x, y, z, this.canBeHitByProjectile(), false, this.blocksBuilding()),
ColliderType.SHULKER
));
collider.accept(new BukkitCollider(world.serverWorld(), nmsAABB, x, y, z, this.canBeHitByProjectile(), false, this.blocksBuilding()));
}
}

View File

@@ -127,10 +127,7 @@ public class ShulkerHitBox extends AbstractHitBox {
Object level = world.serverWorld();
Object nmsAABB = FastNMS.INSTANCE.constructor$AABB(ceAABB.minX, ceAABB.minY, ceAABB.minZ, ceAABB.maxX, ceAABB.maxY, ceAABB.maxZ);
aabb.accept(entityId, ceAABB);
return new BukkitCollider(
FastNMS.INSTANCE.createCollisionShulker(level, nmsAABB, x, y, z, this.canBeHitByProjectile(), true, this.blocksBuilding()),
ColliderType.SHULKER
);
return new BukkitCollider(level, nmsAABB, x, y, z, this.canBeHitByProjectile(), true, this.blocksBuilding());
}
public AABB createAABB(Direction direction, Vector3f offset, double x, double y, double z) {

View File

@@ -129,9 +129,9 @@ public class BukkitFontManager extends AbstractFontManager implements Listener {
}
@SuppressWarnings("UnstableApiUsage")
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onAnvilRename(PrepareAnvilEvent event) {
if (super.emojiKeywordTrie == null) {
if (!Config.allowEmojiAnvil() || super.emojiKeywordTrie == null) {
return;
}
ItemStack result = event.getResult();
@@ -164,6 +164,7 @@ public class BukkitFontManager extends AbstractFontManager implements Listener {
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onSignChange(SignChangeEvent event) {
if (!Config.allowEmojiSign()) return;
Player player = event.getPlayer();
List<Component> lines = event.lines();
for (int i = 0; i < lines.size(); i++) {
@@ -193,6 +194,7 @@ public class BukkitFontManager extends AbstractFontManager implements Listener {
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onPlayerEditBook(PlayerEditBookEvent event) {
if (!event.isSigning()) return;
if (!Config.allowEmojiBook()) return;
Player player = event.getPlayer();
BookMeta newBookMeta = event.getNewBookMeta();
List<?> pages = newBookMeta.pages();
@@ -222,20 +224,30 @@ public class BukkitFontManager extends AbstractFontManager implements Listener {
try {
Object originalMessage = Reflections.field$AsyncChatDecorateEvent$originalMessage.get(event);
String rawJsonMessage = ComponentUtils.paperAdventureToJson(originalMessage);
EmojiTextProcessResult processResult = replaceJsonEmoji(rawJsonMessage, this.plugin.adapt(player));
boolean hasChanged = processResult.replaced();
if (!player.hasPermission(FontManager.BYPASS_CHAT)) {
IllegalCharacterProcessResult result = processIllegalCharacters(processResult.text());
if (result.has()) {
Object component = ComponentUtils.jsonToPaperAdventure(result.text());
Reflections.method$AsyncChatDecorateEvent$result.invoke(event, component);
if (Config.allowEmojiChat()) {
EmojiTextProcessResult processResult = replaceJsonEmoji(rawJsonMessage, this.plugin.adapt(player));
boolean hasChanged = processResult.replaced();
if (!player.hasPermission(FontManager.BYPASS_CHAT)) {
IllegalCharacterProcessResult result = processIllegalCharacters(processResult.text());
if (result.has()) {
Object component = ComponentUtils.jsonToPaperAdventure(result.text());
Reflections.method$AsyncChatDecorateEvent$result.invoke(event, component);
} else if (hasChanged) {
Object component = ComponentUtils.jsonToPaperAdventure(processResult.text());
Reflections.method$AsyncChatDecorateEvent$result.invoke(event, component);
}
} else if (hasChanged) {
Object component = ComponentUtils.jsonToPaperAdventure(processResult.text());
Reflections.method$AsyncChatDecorateEvent$result.invoke(event, component);
}
} else if (hasChanged) {
Object component = ComponentUtils.jsonToPaperAdventure(processResult.text());
Reflections.method$AsyncChatDecorateEvent$result.invoke(event, component);
} else {
if (!player.hasPermission(FontManager.BYPASS_CHAT)) {
IllegalCharacterProcessResult result = processIllegalCharacters(rawJsonMessage);
if (result.has()) {
Object component = ComponentUtils.jsonToPaperAdventure(result.text());
Reflections.method$AsyncChatDecorateEvent$result.invoke(event, component);
}
}
}
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);

View File

@@ -128,8 +128,7 @@ public class BukkitCustomItem implements CustomItem<ItemStack> {
for (ItemDataModifier<ItemStack> modifier : dataModifiers()) {
modifier.apply(wrapped, context);
}
wrapped.load();
return wrapped;
return BukkitCraftEngine.instance().itemManager().wrap(wrapped.load());
}
@Override

View File

@@ -60,7 +60,7 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
}
private static BukkitItemManager instance;
private final BukkitItemFactory factory;
private final BukkitItemFactory<? extends ItemWrapper<ItemStack>> factory;
private final BukkitCraftEngine plugin;
private final ItemEventListener itemEventListener;
private final DebugStickListener debugStickListener;
@@ -143,11 +143,6 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
}
}
@Override
public Object encodeJava(Key componentType, @Nullable Object component) {
return this.factory.encodeJava(componentType, component);
}
public static BukkitItemManager instance() {
return instance;
}

View File

@@ -0,0 +1,99 @@
package net.momirealms.craftengine.bukkit.item;
import com.google.gson.JsonElement;
import com.saicone.rtag.data.ComponentType;
import com.saicone.rtag.tag.TagBase;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.core.item.ItemWrapper;
import net.momirealms.craftengine.core.util.Key;
import org.bukkit.inventory.ItemStack;
@SuppressWarnings("UnstableApiUsage")
public class ComponentItemWrapper implements ItemWrapper<ItemStack> {
private final ItemStack item;
public ComponentItemWrapper(final ItemStack item) {
this.item = item;
}
public ComponentItemWrapper(final ItemStack item, int count) {
this.item = item;
this.item.setAmount(count);
}
public void resetComponent(Object type) {
FastNMS.INSTANCE.resetComponent(this.getLiteralObject(), ensureDataComponentType(type));
}
public void setComponent(Object type, final Object value) {
if (value instanceof JsonElement jsonElement) {
setJsonComponent(type, jsonElement);
} else if (TagBase.isTag(value)) {
setNBTComponent(type, value);
} else {
setJavaComponent(type, value);
}
}
public Object getComponent(Object type) {
return FastNMS.INSTANCE.getComponent(getLiteralObject(), ensureDataComponentType(type));
}
public boolean hasComponent(Object type) {
return FastNMS.INSTANCE.hasComponent(getLiteralObject(), ensureDataComponentType(type));
}
public void setJavaComponent(Object type, Object value) {
ComponentType.parseJava(type, value).ifPresent(it -> FastNMS.INSTANCE.setComponent(this.getLiteralObject(), ensureDataComponentType(type), it));
}
public void setJsonComponent(Object type, JsonElement value) {
ComponentType.parseJson(type, value).ifPresent(it -> FastNMS.INSTANCE.setComponent(this.getLiteralObject(), ensureDataComponentType(type), it));
}
public void setNBTComponent(Object type, Object value) {
ComponentType.parseNbt(type, value).ifPresent(it -> FastNMS.INSTANCE.setComponent(this.getLiteralObject(), ensureDataComponentType(type), it));
}
private Object ensureDataComponentType(Object type) {
assert Reflections.clazz$DataComponentType != null;
if (!Reflections.clazz$DataComponentType.isInstance(type)) {
Key key = Key.of(type.toString());
return FastNMS.INSTANCE.getComponentType(key.namespace(), key.value());
}
return type;
}
@Override
public ItemWrapper<ItemStack> copyWithCount(int count) {
ItemStack copied = item.clone();
copied.setAmount(count);
return new ComponentItemWrapper(copied);
}
@Override
public ItemStack getItem() {
return this.item;
}
@Override
public ItemStack load() {
return this.item;
}
@Override
public Object getLiteralObject() {
return FastNMS.INSTANCE.field$CraftItemStack$handle(this.item);
}
@Override
public int count() {
return this.item.getAmount();
}
@Override
public void count(int amount) {
this.item.setAmount(amount);
}
}

View File

@@ -0,0 +1,35 @@
package net.momirealms.craftengine.bukkit.item;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.core.item.ComponentKeys;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.VersionHelper;
public class ComponentTypes {
public static final Object CUSTOM_MODEL_DATA = getComponentType(ComponentKeys.CUSTOM_MODEL_DATA);
public static final Object CUSTOM_NAME = getComponentType(ComponentKeys.CUSTOM_NAME);
public static final Object ITEM_NAME = getComponentType(ComponentKeys.ITEM_NAME);
public static final Object LORE = getComponentType(ComponentKeys.LORE);
public static final Object DAMAGE = getComponentType(ComponentKeys.DAMAGE);
public static final Object MAX_DAMAGE = getComponentType(ComponentKeys.MAX_DAMAGE);
public static final Object ENCHANTMENT_GLINT_OVERRIDE = getComponentType(ComponentKeys.ENCHANTMENT_GLINT_OVERRIDE);
public static final Object ENCHANTMENTS = getComponentType(ComponentKeys.ENCHANTMENTS);
public static final Object STORED_ENCHANTMENTS = getComponentType(ComponentKeys.STORED_ENCHANTMENTS);
public static final Object UNBREAKABLE = getComponentType(ComponentKeys.UNBREAKABLE);
public static final Object MAX_STACK_SIZE = getComponentType(ComponentKeys.MAX_STACK_SIZE);
public static final Object EQUIPPABLE = getComponentType(ComponentKeys.EQUIPPABLE);
public static final Object ITEM_MODEL = getComponentType(ComponentKeys.ITEM_MODEL);
public static final Object TOOLTIP_STYLE = getComponentType(ComponentKeys.TOOLTIP_STYLE);
public static final Object JUKEBOX_PLAYABLE = getComponentType(ComponentKeys.JUKEBOX_PLAYABLE);
public static final Object TRIM = getComponentType(ComponentKeys.TRIM);
public static final Object REPAIR_COST = getComponentType(ComponentKeys.REPAIR_COST);
public static final Object CUSTOM_DATA = getComponentType(ComponentKeys.CUSTOM_DATA);
public static final Object PROFILE = getComponentType(ComponentKeys.PROFILE);
private ComponentTypes() {}
private static Object getComponentType(Key key) {
if (!VersionHelper.isVersionNewerThan1_20_5()) return null;
return FastNMS.INSTANCE.getComponentType(key.namespace(), key.value());
}
}

View File

@@ -4,14 +4,11 @@ import com.saicone.rtag.RtagItem;
import net.momirealms.craftengine.core.item.ItemWrapper;
import org.bukkit.inventory.ItemStack;
import java.util.Map;
@SuppressWarnings("UnstableApiUsage")
public class RTagItemWrapper implements ItemWrapper<ItemStack> {
public class LegacyItemWrapper implements ItemWrapper<ItemStack> {
private final RtagItem rtagItem;
private int count;
public RTagItemWrapper(RtagItem rtagItem, int count) {
public LegacyItemWrapper(RtagItem rtagItem, int count) {
this.rtagItem = rtagItem;
this.count = count;
}
@@ -23,72 +20,43 @@ public class RTagItemWrapper implements ItemWrapper<ItemStack> {
return itemStack;
}
@Override
public boolean set(Object value, Object... path) {
return this.rtagItem.set(value, path);
}
@Override
public boolean add(Object value, Object... path) {
return this.rtagItem.add(value, path);
}
@Override
public <V> V get(Object... path) {
return this.rtagItem.get(path);
}
@Override
public void setComponent(Object path, Object value) {
this.rtagItem.setComponent(path, value);
}
@Override
public Object getComponent(Object path) {
return this.rtagItem.getComponent(path);
}
@Override
public int count() {
return this.count;
}
@Override
public void count(int amount) {
if (amount < 0) amount = 0;
this.count = amount;
}
@Override
public <V> V getExact(Object... path) {
return this.rtagItem.get(path);
}
@Override
public void removeComponent(Object path) {
this.rtagItem.removeComponent(path);
public boolean remove(Object... path) {
return this.rtagItem.remove(path);
}
@Override
public boolean hasComponent(Object path) {
return this.rtagItem.hasComponent(path);
public boolean hasTag(Object... path) {
return this.rtagItem.hasTag(path);
}
@Override
public void update() {
this.rtagItem.update();
}
@Override
public boolean remove(Object... path) {
return this.rtagItem.remove(path);
}
@Override
public boolean hasTag(Object... path) {
return this.rtagItem.hasTag(path);
}
@Override
public ItemStack load() {
ItemStack itemStack = this.rtagItem.load();
@@ -96,13 +64,6 @@ public class RTagItemWrapper implements ItemWrapper<ItemStack> {
return itemStack;
}
@Override
public ItemStack loadCopy() {
ItemStack itemStack = this.rtagItem.loadCopy();
itemStack.setAmount(this.count);
return itemStack;
}
@Override
public Object getLiteralObject() {
return this.rtagItem.getLiteralObject();
@@ -110,11 +71,6 @@ public class RTagItemWrapper implements ItemWrapper<ItemStack> {
@Override
public ItemWrapper<ItemStack> copyWithCount(int count) {
return new RTagItemWrapper(new RtagItem(this.rtagItem.loadCopy()), count);
}
@Override
public Map<String, Object> getData() {
return this.rtagItem.get();
return new LegacyItemWrapper(new RtagItem(this.rtagItem.loadCopy()), count);
}
}

View File

@@ -24,7 +24,6 @@ import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.world.collision.AABB;
import org.bukkit.Location;
import org.bukkit.World;
import org.joml.Quaternionf;
import java.nio.file.Path;
import java.util.ArrayList;

View File

@@ -63,11 +63,16 @@ public class LiquidCollisionBlockItemBehavior extends BlockItemBehavior {
public ItemBehavior create(Pack pack, Path path, Key key, Map<String, Object> arguments) {
Object id = arguments.get("block");
if (id == null) {
throw new IllegalArgumentException("Missing required parameter 'block' for on_liquid_block_item behavior");
throw new IllegalArgumentException("Missing required parameter 'block' for liquid_collision_block_item behavior");
}
int offset = MiscUtils.getAsInt(arguments.getOrDefault("y-offset", 1));
if (id instanceof Map<?, ?> map) {
BukkitBlockManager.instance().parser().parseSection(pack, path, key, MiscUtils.castToMap(map, false));
if (map.containsKey(key.toString())) {
// 防呆
BukkitBlockManager.instance().parser().parseSection(pack, path, key, MiscUtils.castToMap(map.get(key.toString()), false));
} else {
BukkitBlockManager.instance().parser().parseSection(pack, path, key, MiscUtils.castToMap(map, false));
}
return new LiquidCollisionBlockItemBehavior(key, offset);
} else {
return new LiquidCollisionBlockItemBehavior(Key.of(id.toString()), offset);

View File

@@ -1,35 +1,40 @@
package net.momirealms.craftengine.bukkit.item.factory;
import com.saicone.rtag.RtagItem;
import net.momirealms.craftengine.bukkit.item.RTagItemWrapper;
import com.google.gson.JsonElement;
import net.momirealms.craftengine.bukkit.util.ItemTags;
import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.core.item.EquipmentData;
import net.momirealms.craftengine.core.item.ItemFactory;
import net.momirealms.craftengine.core.item.ItemWrapper;
import net.momirealms.craftengine.core.item.modifier.IdModifier;
import net.momirealms.craftengine.core.item.JukeboxPlayable;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.util.Key;
import org.bukkit.NamespacedKey;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
import java.util.Optional;
public abstract class BukkitItemFactory extends ItemFactory<CraftEngine, RTagItemWrapper, ItemStack> {
public abstract class BukkitItemFactory<W extends ItemWrapper<ItemStack>> extends ItemFactory<W, ItemStack> {
protected BukkitItemFactory(CraftEngine plugin) {
super(plugin);
}
public static BukkitItemFactory create(CraftEngine plugin) {
public static BukkitItemFactory<? extends ItemWrapper<ItemStack>> create(CraftEngine plugin) {
Objects.requireNonNull(plugin, "plugin");
switch (plugin.serverVersion()) {
case "1.20", "1.20.1", "1.20.2", "1.20.3", "1.20.4" -> {
return new UniversalItemFactory(plugin);
}
case "1.20.5", "1.20.6",
"1.21", "1.21.1", "1.21.2", "1.21.3" -> {
return new ComponentItemFactory(plugin);
case "1.20.5", "1.20.6"-> {
return new ComponentItemFactory1_20_5(plugin);
}
case "1.21", "1.21.1" -> {
return new ComponentItemFactory1_21(plugin);
}
case "1.21.2", "1.21.3" -> {
return new ComponentItemFactory1_21_2(plugin);
}
case "1.21.4" -> {
return new ComponentItemFactory1_21_4(plugin);
@@ -42,79 +47,32 @@ public abstract class BukkitItemFactory extends ItemFactory<CraftEngine, RTagIte
}
@Override
protected Key id(ItemWrapper<ItemStack> item) {
Object id = item.get(IdModifier.CRAFT_ENGINE_ID);
if (id == null) {
NamespacedKey key = item.getItem().getType().getKey();
return Key.of(key.getNamespace(), key.getKey());
}
return Key.of(id.toString());
}
@Override
protected Optional<Key> customId(ItemWrapper<ItemStack> item) {
Object id = item.get(IdModifier.CRAFT_ENGINE_ID);
if (id == null) return Optional.empty();
return Optional.of(Key.of(id.toString()));
}
@Override
protected boolean isBlockItem(ItemWrapper<ItemStack> item) {
protected boolean isBlockItem(W item) {
return item.getItem().getType().isBlock();
}
@Override
protected Key vanillaId(ItemWrapper<ItemStack> item) {
protected Key vanillaId(W item) {
return Key.of(item.getItem().getType().getKey().asString());
}
@Override
protected ItemWrapper<ItemStack> wrapInternal(ItemStack item) {
return new RTagItemWrapper(new RtagItem(item), item.getAmount());
protected Key id(W item) {
return customId(item).orElse(vanillaId(item));
}
@Override
protected void setTag(ItemWrapper<ItemStack> item, Object value, Object... path) {
item.set(value, path);
}
@Override
protected Object getTag(ItemWrapper<ItemStack> item, Object... path) {
return item.get(path);
}
@Override
protected boolean hasTag(ItemWrapper<ItemStack> item, Object... path) {
return item.hasTag(path);
}
@Override
protected boolean removeTag(ItemWrapper<ItemStack> item, Object... path) {
return item.remove(path);
}
@Override
protected void update(ItemWrapper<ItemStack> item) {
item.update();
}
@Override
protected ItemStack load(ItemWrapper<ItemStack> item) {
protected ItemStack load(W item) {
return item.load();
}
@Override
protected ItemStack getItem(ItemWrapper<ItemStack> item) {
protected ItemStack getItem(W item) {
return item.getItem();
}
@Override
protected ItemStack loadCopy(ItemWrapper<ItemStack> item) {
return item.loadCopy();
}
@Override
protected boolean is(ItemWrapper<ItemStack> item, Key itemTag) {
protected boolean is(W item, Key itemTag) {
Object literalObject = item.getLiteralObject();
Object tag = ItemTags.getOrCreate(itemTag);
try {
@@ -123,4 +81,89 @@ public abstract class BukkitItemFactory extends ItemFactory<CraftEngine, RTagIte
return false;
}
}
@Override
protected JsonElement encodeJson(Object type, Object component) {
throw new UnsupportedOperationException("This feature is only available on 1.20.5+");
}
@Override
public Object encodeJava(Object componentType, @Nullable Object component) {
throw new UnsupportedOperationException("This feature is only available on 1.20.5+");
}
@Override
protected void resetComponent(W item, Object type) {
throw new UnsupportedOperationException("This feature is only available on 1.20.5+");
}
@Override
protected void setComponent(W item, Object type, Object value) {
throw new UnsupportedOperationException("This feature is only available on 1.20.5+");
}
@Override
protected Object getComponent(W item, Object type) {
throw new UnsupportedOperationException("This feature is only available on 1.20.5+");
}
@Override
protected boolean hasComponent(W item, Object type) {
throw new UnsupportedOperationException("This feature is only available on 1.20.5+");
}
@Override
protected void removeComponent(W item, Object type) {
throw new UnsupportedOperationException("This feature is only available on 1.20.5+");
}
@Override
protected Optional<String> tooltipStyle(W item) {
throw new UnsupportedOperationException("This feature is only available on 1.21.2+");
}
@Override
protected void tooltipStyle(W item, String data) {
throw new UnsupportedOperationException("This feature is only available on 1.21.2+");
}
@Override
protected Optional<JukeboxPlayable> jukeboxSong(W item) {
throw new UnsupportedOperationException("This feature is only available on 1.21+");
}
@Override
protected void jukeboxSong(W item, JukeboxPlayable data) {
throw new UnsupportedOperationException("This feature is only available on 1.21+");
}
@Override
protected Optional<Boolean> glint(W item) {
throw new UnsupportedOperationException("This feature is only available on 1.20.5+");
}
@Override
protected void glint(W item, Boolean glint) {
throw new UnsupportedOperationException("This feature is only available on 1.20.5+");
}
@Override
protected Optional<String> itemModel(W item) {
throw new UnsupportedOperationException("This feature is only available on 1.21.2+");
}
@Override
protected void itemModel(W item, String data) {
throw new UnsupportedOperationException("This feature is only available on 1.21.2+");
}
@Override
protected Optional<EquipmentData> equippable(W item) {
throw new UnsupportedOperationException("This feature is only available on 1.21.2+");
}
@Override
protected void equippable(W item, EquipmentData data) {
throw new UnsupportedOperationException("This feature is only available on 1.21.2+");
}
}

View File

@@ -1,391 +0,0 @@
package net.momirealms.craftengine.bukkit.item.factory;
import com.google.gson.JsonElement;
import com.saicone.rtag.RtagItem;
import com.saicone.rtag.data.ComponentType;
import com.saicone.rtag.item.ItemObject;
import net.momirealms.craftengine.bukkit.item.RTagItemWrapper;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.util.EnchantmentUtils;
import net.momirealms.craftengine.bukkit.util.KeyUtils;
import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.core.item.ComponentKeys;
import net.momirealms.craftengine.core.item.Enchantment;
import net.momirealms.craftengine.core.item.ItemWrapper;
import net.momirealms.craftengine.core.item.Trim;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.util.Key;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@SuppressWarnings("UnstableApiUsage")
public class ComponentItemFactory extends BukkitItemFactory {
public ComponentItemFactory(CraftEngine plugin) {
super(plugin);
}
@Override
protected void setComponent(ItemWrapper<ItemStack> item, Key type, Object value) {
if (value instanceof JsonElement jsonElement) {
setJsonComponentDirectly(item, type, jsonElement);
} else {
setJavaComponentDirectly(item, type, value);
}
}
@Override
protected void resetComponent(ItemWrapper<ItemStack> item, Key type) {
FastNMS.INSTANCE.resetComponent(item.getLiteralObject(), KeyUtils.toResourceLocation(type));
}
@Override
protected Object getComponent(ItemWrapper<ItemStack> item, Key type) {
return item.getComponent(type);
}
@Override
protected boolean hasComponent(ItemWrapper<ItemStack> item, Key type) {
return item.hasComponent(type);
}
@Override
protected void removeComponent(ItemWrapper<ItemStack> item, Key type) {
FastNMS.INSTANCE.removeComponent(item.getLiteralObject(), KeyUtils.toResourceLocation(type));
}
protected void setJavaComponentDirectly(ItemWrapper<ItemStack> item, Key type, Object value) {
ComponentType.parseJava(type, value).ifPresent(it -> FastNMS.INSTANCE.setComponent(item.getLiteralObject(), KeyUtils.toResourceLocation(type), it));
}
protected void setJsonComponentDirectly(ItemWrapper<ItemStack> item, Key type, JsonElement value) {
ComponentType.parseJson(type, value).ifPresent(it -> FastNMS.INSTANCE.setComponent(item.getLiteralObject(), KeyUtils.toResourceLocation(type), it));
}
protected void setNBTComponentDirectly(ItemWrapper<ItemStack> item, Key type, Object value) {
ComponentType.parseNbt(type, value).ifPresent(it -> FastNMS.INSTANCE.setComponent(item.getLiteralObject(), KeyUtils.toResourceLocation(type), it));
}
@Override
public Object encodeJava(Key componentType, @Nullable Object component) {
return ComponentType.encodeJava(componentType, component).orElse(null);
}
@Override
protected JsonElement encodeJson(Key type, Object component) {
return ComponentType.encodeJson(type, component).orElse(null);
}
@Override
protected void customModelData(ItemWrapper<ItemStack> item, Integer data) {
if (data == null) {
resetComponent(item, ComponentKeys.CUSTOM_MODEL_DATA);
} else {
setJavaComponentDirectly(item, ComponentKeys.CUSTOM_MODEL_DATA, data);
}
}
@Override
protected Optional<Integer> customModelData(ItemWrapper<ItemStack> item) {
if (!item.hasComponent(ComponentKeys.CUSTOM_MODEL_DATA)) return Optional.empty();
return Optional.ofNullable(
(Integer) ComponentType.encodeJava(
ComponentKeys.CUSTOM_MODEL_DATA,
item.getComponent(ComponentKeys.CUSTOM_MODEL_DATA)
).orElse(null));
}
@Override
protected void customName(ItemWrapper<ItemStack> item, String json) {
if (json == null) {
resetComponent(item, ComponentKeys.CUSTOM_NAME);
} else {
setJavaComponentDirectly(item, ComponentKeys.CUSTOM_NAME, json);
}
}
@Override
protected Optional<String> customName(ItemWrapper<ItemStack> item) {
if (!item.hasComponent(ComponentKeys.CUSTOM_NAME)) return Optional.empty();
return Optional.ofNullable(
(String) ComponentType.encodeJava(
ComponentKeys.CUSTOM_NAME,
item.getComponent(ComponentKeys.CUSTOM_NAME)
).orElse(null)
);
}
@Override
protected void itemName(ItemWrapper<ItemStack> item, String json) {
if (json == null) {
resetComponent(item, ComponentKeys.ITEM_NAME);
} else {
setJavaComponentDirectly(item, ComponentKeys.ITEM_NAME, json);
}
}
@Override
protected Optional<String> itemName(ItemWrapper<ItemStack> item) {
if (!item.hasComponent(ComponentKeys.ITEM_NAME)) return Optional.empty();
return Optional.ofNullable(
(String) ComponentType.encodeJava(
ComponentKeys.ITEM_NAME,
item.getComponent(ComponentKeys.ITEM_NAME)
).orElse(null)
);
}
@Override
protected void skull(ItemWrapper<ItemStack> item, String skullData) {
if (skullData == null) {
resetComponent(item, ComponentKeys.PROFILE);
} else {
Map<String, Object> profile = Map.of("properties", List.of(Map.of("name", "textures", "value", skullData)));
setJavaComponentDirectly(item, ComponentKeys.PROFILE, profile);
}
}
@SuppressWarnings("unchecked")
@Override
protected Optional<List<String>> lore(ItemWrapper<ItemStack> item) {
if (!item.hasComponent(ComponentKeys.LORE)) return Optional.empty();
return Optional.ofNullable(
(List<String>) ComponentType.encodeJava(
ComponentKeys.LORE,
item.getComponent(ComponentKeys.LORE)
).orElse(null)
);
}
@Override
protected void lore(ItemWrapper<ItemStack> item, List<String> lore) {
if (lore == null || lore.isEmpty()) {
resetComponent(item, ComponentKeys.LORE);
} else {
setJavaComponentDirectly(item, ComponentKeys.LORE, lore);
}
}
@Override
protected boolean unbreakable(ItemWrapper<ItemStack> item) {
return item.hasComponent(ComponentKeys.UNBREAKABLE);
}
@Override
protected void unbreakable(ItemWrapper<ItemStack> item, boolean unbreakable) {
if (unbreakable) {
resetComponent(item, ComponentKeys.UNBREAKABLE);
} else {
setJavaComponentDirectly(item, ComponentKeys.UNBREAKABLE, true);
}
}
@Override
protected Optional<Boolean> glint(ItemWrapper<ItemStack> item) {
return Optional.ofNullable((Boolean) item.getComponent(ComponentKeys.ENCHANTMENT_GLINT_OVERRIDE));
}
@Override
protected void glint(ItemWrapper<ItemStack> item, Boolean glint) {
if (glint == null) {
resetComponent(item, ComponentKeys.ENCHANTMENT_GLINT_OVERRIDE);
} else {
setJavaComponentDirectly(item, ComponentKeys.ENCHANTMENT_GLINT_OVERRIDE, glint);
}
}
@Override
protected Optional<Integer> damage(ItemWrapper<ItemStack> item) {
if (!item.hasComponent(ComponentKeys.DAMAGE)) return Optional.empty();
return Optional.ofNullable(
(Integer) ComponentType.encodeJava(
ComponentKeys.DAMAGE,
item.getComponent(ComponentKeys.DAMAGE)
).orElse(null)
);
}
@Override
protected void damage(ItemWrapper<ItemStack> item, Integer damage) {
if (damage == null) {
resetComponent(item, ComponentKeys.DAMAGE);
} else {
setJavaComponentDirectly(item, ComponentKeys.DAMAGE, damage);
}
}
@Override
protected Optional<Integer> maxDamage(ItemWrapper<ItemStack> item) {
if (!item.hasComponent(ComponentKeys.MAX_DAMAGE)) return Optional.of((int) item.getItem().getType().getMaxDurability());
return Optional.ofNullable(
(Integer) ComponentType.encodeJava(
ComponentKeys.MAX_DAMAGE,
item.getComponent(ComponentKeys.MAX_DAMAGE)
).orElse(null)
);
}
@Override
protected void maxDamage(ItemWrapper<ItemStack> item, Integer damage) {
if (damage == null) {
resetComponent(item, ComponentKeys.MAX_DAMAGE);
} else {
setJavaComponentDirectly(item, ComponentKeys.MAX_DAMAGE, damage);
}
}
@Override
protected Optional<Enchantment> getEnchantment(ItemWrapper<ItemStack> item, Key key) {
Object enchant = item.getComponent(ComponentKeys.ENCHANTMENTS);
try {
Map<String, Integer> map = EnchantmentUtils.toMap(enchant);
Integer level = map.get(key.toString());
if (level == null) return Optional.empty();
return Optional.of(new Enchantment(key, level));
} catch (ReflectiveOperationException e) {
plugin.logger().warn("Failed to get enchantment " + key, e);
return Optional.empty();
}
}
@Override
protected void enchantments(ItemWrapper<ItemStack> item, List<Enchantment> enchantments) {
if (enchantments == null || enchantments.isEmpty()) {
resetComponent(item, ComponentKeys.ENCHANTMENTS);
} else {
Map<String, Integer> enchants = new HashMap<>();
for (Enchantment enchantment : enchantments) {
enchants.put(enchantment.id().toString(), enchantment.level());
}
setJavaComponentDirectly(item, ComponentKeys.ENCHANTMENTS, enchants);
}
}
@Override
protected void storedEnchantments(ItemWrapper<ItemStack> item, List<Enchantment> enchantments) {
if (enchantments == null || enchantments.isEmpty()) {
resetComponent(item, ComponentKeys.STORED_ENCHANTMENTS);
} else {
Map<String, Integer> enchants = new HashMap<>();
for (Enchantment enchantment : enchantments) {
enchants.put(enchantment.id().toString(), enchantment.level());
}
setJavaComponentDirectly(item, ComponentKeys.STORED_ENCHANTMENTS, enchants);
}
}
@Override
protected void addEnchantment(ItemWrapper<ItemStack> item, Enchantment enchantment) {
Object enchant = item.getComponent(ComponentKeys.ENCHANTMENTS);
try {
Map<String, Integer> map = EnchantmentUtils.toMap(enchant);
map.put(enchantment.toString(), enchantment.level());
setJavaComponentDirectly(item, ComponentKeys.ENCHANTMENTS, map);
} catch (ReflectiveOperationException e) {
plugin.logger().warn("Failed to add enchantment", e);
}
}
@Override
protected void addStoredEnchantment(ItemWrapper<ItemStack> item, Enchantment enchantment) {
Object enchant = item.getComponent(ComponentKeys.STORED_ENCHANTMENTS);
try {
Map<String, Integer> map = EnchantmentUtils.toMap(enchant);
map.put(enchantment.toString(), enchantment.level());
setJavaComponentDirectly(item, ComponentKeys.STORED_ENCHANTMENTS, map);
} catch (ReflectiveOperationException e) {
plugin.logger().warn("Failed to add stored enchantment", e);
}
}
@Override
protected void itemFlags(ItemWrapper<ItemStack> item, List<String> flags) {
throw new UnsupportedOperationException("This feature is not available on 1.20.5+");
}
@Override
protected int maxStackSize(ItemWrapper<ItemStack> item) {
if (!item.hasComponent(ComponentKeys.MAX_STACK_SIZE)) return item.getItem().getType().getMaxStackSize();
return Optional.ofNullable((Integer) ComponentType.encodeJava(ComponentKeys.MAX_STACK_SIZE, item.getComponent(ComponentKeys.MAX_STACK_SIZE)).orElse(null))
.orElse(item.getItem().getType().getMaxStackSize());
}
@Override
protected void maxStackSize(ItemWrapper<ItemStack> item, Integer maxStackSize) {
if (maxStackSize == null) {
resetComponent(item, ComponentKeys.MAX_STACK_SIZE);
} else {
setJavaComponentDirectly(item, ComponentKeys.MAX_STACK_SIZE, maxStackSize);
}
}
@Override
protected void repairCost(ItemWrapper<ItemStack> item, Integer data) {
if (data == null) {
resetComponent(item, ComponentKeys.REPAIR_COST);
} else {
setJavaComponentDirectly(item, ComponentKeys.REPAIR_COST, data);
}
}
@Override
protected Optional<Integer> repairCost(ItemWrapper<ItemStack> item) {
if (!item.hasComponent(ComponentKeys.REPAIR_COST)) return Optional.empty();
return Optional.ofNullable((Integer) ComponentType.encodeJava(ComponentKeys.REPAIR_COST, item.getComponent(ComponentKeys.REPAIR_COST)).orElse(null));
}
@Override
protected ItemWrapper<ItemStack> mergeCopy(ItemWrapper<ItemStack> item1, ItemWrapper<ItemStack> item2) {
Object itemStack1 = item1.getLiteralObject();
Object itemStack2 = item2.getLiteralObject();
try {
Object itemStack3 = Reflections.method$ItemStack$transmuteCopy.invoke(itemStack1, Reflections.method$ItemStack$getItem.invoke(itemStack2), 1);
Reflections.method$ItemStack$applyComponents.invoke(itemStack3, Reflections.method$ItemStack$getComponentsPatch.invoke(itemStack2));
return new RTagItemWrapper(new RtagItem(ItemObject.asCraftMirror(itemStack3)), item2.count());
} catch (Exception e) {
this.plugin.logger().warn("Failed to merge item", e);
}
return null;
}
@Override
protected void merge(ItemWrapper<ItemStack> item1, ItemWrapper<ItemStack> item2) {
// load previous changes on nms items
Object itemStack1 = item1.getLiteralObject();
Object itemStack2 = item2.getLiteralObject();
try {
Reflections.method$ItemStack$applyComponents.invoke(itemStack1, Reflections.method$ItemStack$getComponentsPatch.invoke(itemStack2));
} catch (Exception e) {
plugin.logger().warn("Failed to merge item", e);
}
}
@Override
protected void trim(ItemWrapper<ItemStack> item, Trim trim) {
if (trim == null) {
resetComponent(item, ComponentKeys.TRIM);
} else {
setJavaComponentDirectly(item, ComponentKeys.TRIM, Map.of(
"pattern", trim.pattern(),
"material", trim.material()
));
}
}
@Override
protected Optional<Trim> trim(ItemWrapper<ItemStack> item) {
if (!item.hasComponent(ComponentKeys.TRIM)) return Optional.empty();
Optional<Object> trim = ComponentType.encodeJava(ComponentKeys.TRIM, item.getComponent(ComponentKeys.TRIM));
if (trim.isEmpty()) {
return Optional.empty();
}
@SuppressWarnings("unchecked")
Map<String, String> trimMap = (Map<String, String>) trim.get();
return Optional.of(new Trim(trimMap.get("pattern"), trimMap.get("material")));
}
}

View File

@@ -0,0 +1,410 @@
package net.momirealms.craftengine.bukkit.item.factory;
import com.google.gson.JsonElement;
import com.saicone.rtag.data.ComponentType;
import com.saicone.rtag.item.ItemObject;
import net.momirealms.craftengine.bukkit.item.ComponentItemWrapper;
import net.momirealms.craftengine.bukkit.item.ComponentTypes;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.util.EnchantmentUtils;
import net.momirealms.craftengine.core.item.Enchantment;
import net.momirealms.craftengine.core.item.Trim;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.util.Key;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@SuppressWarnings("UnstableApiUsage")
public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemWrapper> {
public ComponentItemFactory1_20_5(CraftEngine plugin) {
super(plugin);
}
@Override
protected void customId(ComponentItemWrapper item, Key id) {
FastNMS.INSTANCE.setCustomItemId(item.getLiteralObject(), id.toString());
}
@Override
protected Optional<Key> customId(ComponentItemWrapper item) {
return Optional.ofNullable(FastNMS.INSTANCE.getCustomItemId(item.getLiteralObject())).map(Key::of);
}
@Override
protected ComponentItemWrapper wrapInternal(ItemStack item) {
return new ComponentItemWrapper(item);
}
@Override
protected Object getTag(ComponentItemWrapper item, Object... path) {
throw new UnsupportedOperationException("This feature is not available on 1.20.5+");
}
@Override
protected void setTag(ComponentItemWrapper item, Object value, Object... path) {
throw new UnsupportedOperationException("This feature is not available on 1.20.5+");
}
@Override
protected boolean hasTag(ComponentItemWrapper item, Object... path) {
throw new UnsupportedOperationException("This feature is not available on 1.20.5+");
}
@Override
protected boolean removeTag(ComponentItemWrapper item, Object... path) {
throw new UnsupportedOperationException("This feature is not available on 1.20.5+");
}
@Override
protected Optional<String> tooltipStyle(ComponentItemWrapper item) {
throw new UnsupportedOperationException("This feature is not available on 1.21.2+");
}
@Override
protected void tooltipStyle(ComponentItemWrapper item, String data) {
throw new UnsupportedOperationException("This feature is not available on 1.21.2+");
}
@Override
protected void setComponent(ComponentItemWrapper item, Object type, Object value) {
item.setComponent(type, value);
}
@Override
protected void resetComponent(ComponentItemWrapper item, Object type) {
item.resetComponent(type);
}
@Override
protected Object getComponent(ComponentItemWrapper item, Object type) {
return item.getComponent(type);
}
@Override
protected boolean hasComponent(ComponentItemWrapper item, Object type) {
return item.hasComponent(type);
}
@Override
protected void removeComponent(ComponentItemWrapper item, Object type) {
item.resetComponent(type);
}
@Override
public Object encodeJava(Object componentType, @Nullable Object component) {
return ComponentType.encodeJava(componentType, component).orElse(null);
}
@Override
protected JsonElement encodeJson(Object type, Object component) {
return ComponentType.encodeJson(type, component).orElse(null);
}
@Override
protected void customModelData(ComponentItemWrapper item, Integer data) {
if (data == null) {
item.resetComponent(ComponentTypes.CUSTOM_MODEL_DATA);
} else {
item.setJavaComponent(ComponentTypes.CUSTOM_MODEL_DATA, data);
}
}
@Override
protected Optional<Integer> customModelData(ComponentItemWrapper item) {
if (!item.hasComponent(ComponentTypes.CUSTOM_MODEL_DATA)) return Optional.empty();
return Optional.ofNullable(
(Integer) ComponentType.encodeJava(
ComponentTypes.CUSTOM_MODEL_DATA,
item.getComponent(ComponentTypes.CUSTOM_MODEL_DATA)
).orElse(null));
}
@Override
protected void customName(ComponentItemWrapper item, String json) {
if (json == null) {
item.resetComponent(ComponentTypes.CUSTOM_NAME);
} else {
item.setJavaComponent(ComponentTypes.CUSTOM_NAME, json);
}
}
@Override
protected Optional<String> customName(ComponentItemWrapper item) {
if (!item.hasComponent(ComponentTypes.CUSTOM_NAME)) return Optional.empty();
return Optional.ofNullable(
(String) ComponentType.encodeJava(
ComponentTypes.CUSTOM_NAME,
item.getComponent(ComponentTypes.CUSTOM_NAME)
).orElse(null)
);
}
@Override
protected void itemName(ComponentItemWrapper item, String json) {
if (json == null) {
item.resetComponent(ComponentTypes.ITEM_NAME);
} else {
item.setJavaComponent(ComponentTypes.ITEM_NAME, json);
}
}
@Override
protected Optional<String> itemName(ComponentItemWrapper item) {
if (!item.hasComponent(ComponentTypes.ITEM_NAME)) return Optional.empty();
return Optional.ofNullable(
(String) ComponentType.encodeJava(
ComponentTypes.ITEM_NAME,
item.getComponent(ComponentTypes.ITEM_NAME)
).orElse(null)
);
}
@Override
protected void skull(ComponentItemWrapper item, String skullData) {
if (skullData == null) {
item.resetComponent(ComponentTypes.PROFILE);
} else {
Map<String, Object> profile = Map.of("properties", List.of(Map.of("name", "textures", "value", skullData)));
item.setJavaComponent(ComponentTypes.PROFILE, profile);
}
}
@SuppressWarnings("unchecked")
@Override
protected Optional<List<String>> lore(ComponentItemWrapper item) {
if (!item.hasComponent(ComponentTypes.LORE)) return Optional.empty();
return Optional.ofNullable(
(List<String>) ComponentType.encodeJava(
ComponentTypes.LORE,
item.getComponent(ComponentTypes.LORE)
).orElse(null)
);
}
@Override
protected void lore(ComponentItemWrapper item, List<String> lore) {
if (lore == null || lore.isEmpty()) {
item.resetComponent(ComponentTypes.LORE);
} else {
item.setJavaComponent(ComponentTypes.LORE, lore);
}
}
@Override
protected boolean unbreakable(ComponentItemWrapper item) {
return item.hasComponent(ComponentTypes.UNBREAKABLE);
}
@Override
protected void unbreakable(ComponentItemWrapper item, boolean unbreakable) {
if (unbreakable) {
item.resetComponent(ComponentTypes.UNBREAKABLE);
} else {
item.setJavaComponent(ComponentTypes.UNBREAKABLE, true);
}
}
@Override
protected Optional<Boolean> glint(ComponentItemWrapper item) {
return Optional.ofNullable((Boolean) item.getComponent(ComponentTypes.ENCHANTMENT_GLINT_OVERRIDE));
}
@Override
protected void glint(ComponentItemWrapper item, Boolean glint) {
if (glint == null) {
item.resetComponent(ComponentTypes.ENCHANTMENT_GLINT_OVERRIDE);
} else {
item.setJavaComponent(ComponentTypes.ENCHANTMENT_GLINT_OVERRIDE, glint);
}
}
@Override
protected Optional<Integer> damage(ComponentItemWrapper item) {
if (!item.hasComponent(ComponentTypes.DAMAGE)) return Optional.empty();
return Optional.ofNullable(
(Integer) ComponentType.encodeJava(
ComponentTypes.DAMAGE,
item.getComponent(ComponentTypes.DAMAGE)
).orElse(null)
);
}
@Override
protected void damage(ComponentItemWrapper item, Integer damage) {
if (damage == null) {
item.resetComponent(ComponentTypes.DAMAGE);
} else {
item.setJavaComponent(ComponentTypes.DAMAGE, damage);
}
}
@Override
protected Optional<Integer> maxDamage(ComponentItemWrapper item) {
if (!item.hasComponent(ComponentTypes.MAX_DAMAGE)) return Optional.of((int) item.getItem().getType().getMaxDurability());
return Optional.ofNullable(
(Integer) ComponentType.encodeJava(
ComponentTypes.MAX_DAMAGE,
item.getComponent(ComponentTypes.MAX_DAMAGE)
).orElse(null)
);
}
@Override
protected void maxDamage(ComponentItemWrapper item, Integer damage) {
if (damage == null) {
item.resetComponent(ComponentTypes.MAX_DAMAGE);
} else {
item.setJavaComponent(ComponentTypes.MAX_DAMAGE, damage);
}
}
@Override
protected Optional<Enchantment> getEnchantment(ComponentItemWrapper item, Key key) {
Object enchant = item.getComponent(ComponentTypes.ENCHANTMENTS);
try {
Map<String, Integer> map = EnchantmentUtils.toMap(enchant);
Integer level = map.get(key.toString());
if (level == null) return Optional.empty();
return Optional.of(new Enchantment(key, level));
} catch (ReflectiveOperationException e) {
plugin.logger().warn("Failed to get enchantment " + key, e);
return Optional.empty();
}
}
@Override
protected void enchantments(ComponentItemWrapper item, List<Enchantment> enchantments) {
if (enchantments == null || enchantments.isEmpty()) {
item.resetComponent(ComponentTypes.ENCHANTMENTS);
} else {
Map<String, Integer> enchants = new HashMap<>();
for (Enchantment enchantment : enchantments) {
enchants.put(enchantment.id().toString(), enchantment.level());
}
item.setJavaComponent(ComponentTypes.ENCHANTMENTS, enchants);
}
}
@Override
protected void storedEnchantments(ComponentItemWrapper item, List<Enchantment> enchantments) {
if (enchantments == null || enchantments.isEmpty()) {
item.resetComponent(ComponentTypes.STORED_ENCHANTMENTS);
} else {
Map<String, Integer> enchants = new HashMap<>();
for (Enchantment enchantment : enchantments) {
enchants.put(enchantment.id().toString(), enchantment.level());
}
item.setJavaComponent(ComponentTypes.STORED_ENCHANTMENTS, enchants);
}
}
@Override
protected void addEnchantment(ComponentItemWrapper item, Enchantment enchantment) {
Object enchant = item.getComponent(ComponentTypes.ENCHANTMENTS);
try {
Map<String, Integer> map = EnchantmentUtils.toMap(enchant);
map.put(enchantment.toString(), enchantment.level());
item.setJavaComponent(ComponentTypes.ENCHANTMENTS, map);
} catch (ReflectiveOperationException e) {
plugin.logger().warn("Failed to add enchantment", e);
}
}
@Override
protected void addStoredEnchantment(ComponentItemWrapper item, Enchantment enchantment) {
Object enchant = item.getComponent(ComponentTypes.STORED_ENCHANTMENTS);
try {
Map<String, Integer> map = EnchantmentUtils.toMap(enchant);
map.put(enchantment.toString(), enchantment.level());
item.setJavaComponent(ComponentTypes.STORED_ENCHANTMENTS, map);
} catch (ReflectiveOperationException e) {
plugin.logger().warn("Failed to add stored enchantment", e);
}
}
@Override
protected void itemFlags(ComponentItemWrapper item, List<String> flags) {
throw new UnsupportedOperationException("This feature is not available on 1.20.5+");
}
@Override
protected int maxStackSize(ComponentItemWrapper item) {
if (!item.hasComponent(ComponentTypes.MAX_STACK_SIZE)) return item.getItem().getType().getMaxStackSize();
return Optional.ofNullable((Integer) ComponentType.encodeJava(ComponentTypes.MAX_STACK_SIZE, item.getComponent(ComponentTypes.MAX_STACK_SIZE)).orElse(null))
.orElse(item.getItem().getType().getMaxStackSize());
}
@Override
protected void maxStackSize(ComponentItemWrapper item, Integer maxStackSize) {
if (maxStackSize == null) {
item.resetComponent(ComponentTypes.MAX_STACK_SIZE);
} else {
item.setJavaComponent(ComponentTypes.MAX_STACK_SIZE, maxStackSize);
}
}
@Override
protected void repairCost(ComponentItemWrapper item, Integer data) {
if (data == null) {
item.resetComponent(ComponentTypes.REPAIR_COST);
} else {
item.setJavaComponent(ComponentTypes.REPAIR_COST, data);
}
}
@Override
protected Optional<Integer> repairCost(ComponentItemWrapper item) {
if (!item.hasComponent(ComponentTypes.REPAIR_COST)) return Optional.empty();
return Optional.ofNullable((Integer) ComponentType.encodeJava(ComponentTypes.REPAIR_COST, item.getComponent(ComponentTypes.REPAIR_COST)).orElse(null));
}
@Override
protected void trim(ComponentItemWrapper item, Trim trim) {
if (trim == null) {
item.resetComponent(ComponentTypes.TRIM);
} else {
item.setJavaComponent(ComponentTypes.TRIM, Map.of(
"pattern", trim.pattern(),
"material", trim.material()
));
}
}
@Override
protected Optional<Trim> trim(ComponentItemWrapper item) {
if (!item.hasComponent(ComponentTypes.TRIM)) return Optional.empty();
Optional<Object> trim = ComponentType.encodeJava(ComponentTypes.TRIM, item.getComponent(ComponentTypes.TRIM));
if (trim.isEmpty()) {
return Optional.empty();
}
@SuppressWarnings("unchecked")
Map<String, String> trimMap = (Map<String, String>) trim.get();
return Optional.of(new Trim(trimMap.get("pattern"), trimMap.get("material")));
}
@Override
protected ComponentItemWrapper mergeCopy(ComponentItemWrapper item1, ComponentItemWrapper item2) {
Object itemStack1 = item1.getLiteralObject();
Object itemStack2 = item2.getLiteralObject();
Object itemStack3 = FastNMS.INSTANCE.method$ItemStack$transmuteCopy(itemStack1, itemStack2);
FastNMS.INSTANCE.method$ItemStack$applyComponents(itemStack3, FastNMS.INSTANCE.method$ItemStack$getComponentsPatch(itemStack2));
return new ComponentItemWrapper(ItemObject.asCraftMirror(itemStack3), item2.count());
}
@Override
protected void merge(ComponentItemWrapper item1, ComponentItemWrapper item2) {
Object itemStack1 = item1.getLiteralObject();
Object itemStack2 = item2.getLiteralObject();
try {
FastNMS.INSTANCE.method$ItemStack$applyComponents(itemStack1, FastNMS.INSTANCE.method$ItemStack$getComponentsPatch(itemStack2));
} catch (Exception e) {
plugin.logger().warn("Failed to merge item", e);
}
}
}

View File

@@ -0,0 +1,38 @@
package net.momirealms.craftengine.bukkit.item.factory;
import com.saicone.rtag.data.ComponentType;
import net.momirealms.craftengine.bukkit.item.ComponentItemWrapper;
import net.momirealms.craftengine.bukkit.item.ComponentTypes;
import net.momirealms.craftengine.core.item.JukeboxPlayable;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import java.util.Map;
import java.util.Optional;
@SuppressWarnings("UnstableApiUsage")
public class ComponentItemFactory1_21 extends ComponentItemFactory1_20_5 {
public ComponentItemFactory1_21(CraftEngine plugin) {
super(plugin);
}
@Override
protected Optional<JukeboxPlayable> jukeboxSong(ComponentItemWrapper item) {
if (!item.hasComponent(ComponentTypes.JUKEBOX_PLAYABLE)) return Optional.empty();
@SuppressWarnings("unchecked")
Map<String, Object> map = (Map<String, Object>) ComponentType.encodeJava(
ComponentTypes.JUKEBOX_PLAYABLE,
item.getComponent(ComponentTypes.JUKEBOX_PLAYABLE)
).orElse(null);
if (map == null) return Optional.empty();
return Optional.of(new JukeboxPlayable((String) map.get("song"), (boolean) map.getOrDefault("show_in_tooltip", true)));
}
@Override
protected void jukeboxSong(ComponentItemWrapper item, JukeboxPlayable data) {
item.setJavaComponent(ComponentTypes.JUKEBOX_PLAYABLE, Map.of(
"song", data.song(),
"show_in_tooltip", true
));
}
}

View File

@@ -0,0 +1,71 @@
package net.momirealms.craftengine.bukkit.item.factory;
import com.saicone.rtag.data.ComponentType;
import net.momirealms.craftengine.bukkit.item.ComponentItemWrapper;
import net.momirealms.craftengine.bukkit.item.ComponentTypes;
import net.momirealms.craftengine.core.item.EquipmentData;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import java.util.Optional;
@SuppressWarnings("UnstableApiUsage")
public class ComponentItemFactory1_21_2 extends ComponentItemFactory1_21 {
public ComponentItemFactory1_21_2(CraftEngine plugin) {
super(plugin);
}
@Override
protected void tooltipStyle(ComponentItemWrapper item, String data) {
if (data == null) {
item.resetComponent(ComponentTypes.TOOLTIP_STYLE);
} else {
item.setJavaComponent(ComponentTypes.TOOLTIP_STYLE, data);
}
}
@Override
protected Optional<String> tooltipStyle(ComponentItemWrapper item) {
if (!item.hasComponent(ComponentTypes.TOOLTIP_STYLE)) return Optional.empty();
return Optional.ofNullable(
(String) ComponentType.encodeJava(
ComponentTypes.TOOLTIP_STYLE,
item.getComponent(ComponentTypes.TOOLTIP_STYLE)
).orElse(null)
);
}
@Override
protected void itemModel(ComponentItemWrapper item, String data) {
if (data == null) {
item.resetComponent(ComponentTypes.ITEM_MODEL);
} else {
item.setJavaComponent(ComponentTypes.ITEM_MODEL, data);
}
}
@Override
protected Optional<String> itemModel(ComponentItemWrapper item) {
if (!item.hasComponent(ComponentTypes.ITEM_MODEL)) return Optional.empty();
return Optional.ofNullable(
(String) ComponentType.encodeJava(
ComponentTypes.ITEM_MODEL,
item.getComponent(ComponentTypes.ITEM_MODEL)
).orElse(null)
);
}
@Override
protected void equippable(ComponentItemWrapper item, EquipmentData data) {
if (data == null) {
item.resetComponent(ComponentTypes.EQUIPPABLE);
} else {
item.setJavaComponent(ComponentTypes.EQUIPPABLE, data.toMap());
}
}
@Override
protected Optional<EquipmentData> equippable(ComponentItemWrapper item) {
throw new UnsupportedOperationException("Not implemented yet.");
}
}

View File

@@ -1,26 +1,25 @@
package net.momirealms.craftengine.bukkit.item.factory;
import com.saicone.rtag.data.ComponentType;
import net.momirealms.craftengine.core.item.ComponentKeys;
import net.momirealms.craftengine.core.item.ItemWrapper;
import net.momirealms.craftengine.bukkit.item.ComponentItemWrapper;
import net.momirealms.craftengine.bukkit.item.ComponentTypes;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import org.bukkit.inventory.ItemStack;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@SuppressWarnings("UnstableApiUsage")
public class ComponentItemFactory1_21_4 extends ComponentItemFactory {
public class ComponentItemFactory1_21_4 extends ComponentItemFactory1_21_2 {
public ComponentItemFactory1_21_4(CraftEngine plugin) {
super(plugin);
}
@Override
protected Optional<Integer> customModelData(ItemWrapper<ItemStack> item) {
if (!item.hasComponent(ComponentKeys.CUSTOM_MODEL_DATA)) return Optional.empty();
Optional<Object> optional = ComponentType.encodeJava(ComponentKeys.CUSTOM_MODEL_DATA, item.getComponent(ComponentKeys.CUSTOM_MODEL_DATA));
protected Optional<Integer> customModelData(ComponentItemWrapper item) {
if (!item.hasComponent(ComponentTypes.CUSTOM_MODEL_DATA)) return Optional.empty();
Optional<Object> optional = ComponentType.encodeJava(ComponentTypes.CUSTOM_MODEL_DATA, item.getComponent(ComponentTypes.CUSTOM_MODEL_DATA));
if (optional.isEmpty()) return Optional.empty();
@SuppressWarnings("unchecked")
Map<String, Object> data = (Map<String, Object>) optional.get();
@@ -31,11 +30,11 @@ public class ComponentItemFactory1_21_4 extends ComponentItemFactory {
}
@Override
protected void customModelData(ItemWrapper<ItemStack> item, Integer data) {
protected void customModelData(ComponentItemWrapper item, Integer data) {
if (data == null) {
resetComponent(item, ComponentKeys.CUSTOM_MODEL_DATA);
item.resetComponent(ComponentTypes.CUSTOM_MODEL_DATA);
} else {
setComponent(item, ComponentKeys.CUSTOM_MODEL_DATA, Map.of("floats", List.of(data.floatValue())));
item.setJavaComponent(ComponentTypes.CUSTOM_MODEL_DATA, Map.of("floats", List.of(data.floatValue())));
}
}
}

View File

@@ -5,12 +5,12 @@ import com.google.gson.JsonElement;
import com.saicone.rtag.data.ComponentType;
import com.saicone.rtag.tag.TagList;
import com.saicone.rtag.util.ChatComponent;
import net.momirealms.craftengine.bukkit.item.ComponentItemWrapper;
import net.momirealms.craftengine.bukkit.item.ComponentTypes;
import net.momirealms.craftengine.bukkit.util.ComponentUtils;
import net.momirealms.craftengine.core.item.ComponentKeys;
import net.momirealms.craftengine.core.item.ItemWrapper;
import net.momirealms.craftengine.core.item.JukeboxPlayable;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.util.GsonHelper;
import org.bukkit.inventory.ItemStack;
import java.util.ArrayList;
import java.util.List;
@@ -24,41 +24,41 @@ public class ComponentItemFactory1_21_5 extends ComponentItemFactory1_21_4 {
}
@Override
protected void customName(ItemWrapper<ItemStack> item, String json) {
protected void customName(ComponentItemWrapper item, String json) {
if (json == null) {
resetComponent(item, ComponentKeys.CUSTOM_NAME);
item.resetComponent(ComponentTypes.CUSTOM_NAME);
} else {
setNBTComponentDirectly(item, ComponentKeys.CUSTOM_NAME, ChatComponent.toTag(ComponentUtils.jsonToMinecraft(json)));
item.setNBTComponent(ComponentTypes.CUSTOM_NAME, ChatComponent.toTag(ComponentUtils.jsonToMinecraft(json)));
}
}
@Override
protected Optional<String> customName(ItemWrapper<ItemStack> item) {
if (!item.hasComponent(ComponentKeys.CUSTOM_NAME)) return Optional.empty();
return ComponentType.encodeJson(ComponentKeys.CUSTOM_NAME, item.getComponent(ComponentKeys.CUSTOM_NAME)).map(jsonElement -> GsonHelper.get().toJson(jsonElement));
protected Optional<String> customName(ComponentItemWrapper item) {
if (!item.hasComponent(ComponentTypes.CUSTOM_NAME)) return Optional.empty();
return ComponentType.encodeJson(ComponentTypes.CUSTOM_NAME, item.getComponent(ComponentTypes.CUSTOM_NAME)).map(jsonElement -> GsonHelper.get().toJson(jsonElement));
}
@Override
protected void itemName(ItemWrapper<ItemStack> item, String json) {
protected void itemName(ComponentItemWrapper item, String json) {
if (json == null) {
resetComponent(item, ComponentKeys.ITEM_NAME);
item.resetComponent(ComponentTypes.ITEM_NAME);
} else {
setNBTComponentDirectly(item, ComponentKeys.ITEM_NAME, ChatComponent.toTag(ComponentUtils.jsonToMinecraft(json)));
item.setNBTComponent(ComponentTypes.ITEM_NAME, ChatComponent.toTag(ComponentUtils.jsonToMinecraft(json)));
}
}
@Override
protected Optional<String> itemName(ItemWrapper<ItemStack> item) {
if (!item.hasComponent(ComponentKeys.ITEM_NAME)) return Optional.empty();
return ComponentType.encodeJson(ComponentKeys.ITEM_NAME, item.getComponent(ComponentKeys.ITEM_NAME)).map(jsonElement -> GsonHelper.get().toJson(jsonElement));
protected Optional<String> itemName(ComponentItemWrapper item) {
if (!item.hasComponent(ComponentTypes.ITEM_NAME)) return Optional.empty();
return ComponentType.encodeJson(ComponentTypes.ITEM_NAME, item.getComponent(ComponentTypes.ITEM_NAME)).map(jsonElement -> GsonHelper.get().toJson(jsonElement));
}
@Override
protected Optional<List<String>> lore(ItemWrapper<ItemStack> item) {
if (!item.hasComponent(ComponentKeys.LORE)) return Optional.empty();
protected Optional<List<String>> lore(ComponentItemWrapper item) {
if (!item.hasComponent(ComponentTypes.LORE)) return Optional.empty();
return ComponentType.encodeJson(
ComponentKeys.LORE,
item.getComponent(ComponentKeys.LORE)
ComponentTypes.LORE,
item.getComponent(ComponentTypes.LORE)
).map(list -> {
List<String> lore = new ArrayList<>();
for (JsonElement jsonElement : (JsonArray) list) {
@@ -69,15 +69,30 @@ public class ComponentItemFactory1_21_5 extends ComponentItemFactory1_21_4 {
}
@Override
protected void lore(ItemWrapper<ItemStack> item, List<String> lore) {
protected void lore(ComponentItemWrapper item, List<String> lore) {
if (lore == null || lore.isEmpty()) {
resetComponent(item, ComponentKeys.LORE);
item.resetComponent(ComponentTypes.LORE);
} else {
List<Object> loreTags = new ArrayList<>();
for (String json : lore) {
loreTags.add(ChatComponent.toTag(ComponentUtils.jsonToMinecraft(json)));
}
setNBTComponentDirectly(item, ComponentKeys.LORE, TagList.newTag(loreTags));
item.setNBTComponent(ComponentTypes.LORE, TagList.newTag(loreTags));
}
}
@Override
protected Optional<JukeboxPlayable> jukeboxSong(ComponentItemWrapper item) {
if (!item.hasComponent(ComponentTypes.JUKEBOX_PLAYABLE)) return Optional.empty();
String song = (String) ComponentType.encodeJava(
ComponentTypes.JUKEBOX_PLAYABLE,
item.getComponent(ComponentTypes.JUKEBOX_PLAYABLE)).orElse(null);
if (song == null) return Optional.empty();
return Optional.of(new JukeboxPlayable(song, true));
}
@Override
protected void jukeboxSong(ComponentItemWrapper item, JukeboxPlayable data) {
item.setJavaComponent(ComponentTypes.JUKEBOX_PLAYABLE, data.song());
}
}

View File

@@ -1,15 +1,14 @@
package net.momirealms.craftengine.bukkit.item.factory;
import com.google.gson.JsonElement;
import com.saicone.rtag.RtagItem;
import com.saicone.rtag.item.ItemObject;
import com.saicone.rtag.tag.TagBase;
import com.saicone.rtag.tag.TagCompound;
import com.saicone.rtag.tag.TagList;
import net.momirealms.craftengine.bukkit.item.RTagItemWrapper;
import net.momirealms.craftengine.bukkit.item.LegacyItemWrapper;
import net.momirealms.craftengine.core.item.Enchantment;
import net.momirealms.craftengine.core.item.ItemWrapper;
import net.momirealms.craftengine.core.item.Trim;
import net.momirealms.craftengine.core.item.modifier.IdModifier;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.SkullUtils;
@@ -17,54 +16,55 @@ import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
import java.nio.charset.StandardCharsets;
import java.util.*;
public class UniversalItemFactory extends BukkitItemFactory {
public class UniversalItemFactory extends BukkitItemFactory<LegacyItemWrapper> {
public UniversalItemFactory(CraftEngine plugin) {
super(plugin);
}
@Override
protected void resetComponent(ItemWrapper<ItemStack> item, Key type) {
throw new UnsupportedOperationException("This feature is only available on 1.20.5+");
protected LegacyItemWrapper wrapInternal(ItemStack item) {
return new LegacyItemWrapper(new RtagItem(item), item.getAmount());
}
@Override
protected JsonElement encodeJson(Key type, Object component) {
throw new UnsupportedOperationException("This feature is only available on 1.20.5+");
protected void setTag(LegacyItemWrapper item, Object value, Object... path) {
item.set(value, path);
}
@Override
protected void setComponent(ItemWrapper<ItemStack> item, Key type, Object value) {
throw new UnsupportedOperationException("This feature is only available on 1.20.5+");
protected Object getTag(LegacyItemWrapper item, Object... path) {
return item.get(path);
}
@Override
protected Object getComponent(ItemWrapper<ItemStack> item, Key type) {
throw new UnsupportedOperationException("This feature is only available on 1.20.5+");
protected boolean hasTag(LegacyItemWrapper item, Object... path) {
return item.hasTag(path);
}
@Override
protected boolean hasComponent(ItemWrapper<ItemStack> item, Key type) {
throw new UnsupportedOperationException("This feature is only available on 1.20.5+");
protected boolean removeTag(LegacyItemWrapper item, Object... path) {
return item.remove(path);
}
@Override
protected void removeComponent(ItemWrapper<ItemStack> item, Key type) {
throw new UnsupportedOperationException("This feature is only available on 1.20.5+");
protected Optional<Key> customId(LegacyItemWrapper item) {
Object id = item.get(IdModifier.CRAFT_ENGINE_ID);
if (id == null) return Optional.empty();
return Optional.of(Key.of(id.toString()));
}
@Override
public Object encodeJava(Key componentType, @Nullable Object component) {
throw new UnsupportedOperationException("This feature is only available on 1.20.5+");
protected void customId(LegacyItemWrapper item, Key id) {
item.set(id.toString(), IdModifier.CRAFT_ENGINE_ID);
}
@Override
protected void customName(ItemWrapper<ItemStack> item, String json) {
protected void customName(LegacyItemWrapper item, String json) {
if (json != null) {
item.set(json, "display", "Name");
} else {
@@ -73,23 +73,23 @@ public class UniversalItemFactory extends BukkitItemFactory {
}
@Override
protected Optional<String> customName(ItemWrapper<ItemStack> item) {
protected Optional<String> customName(LegacyItemWrapper item) {
if (!item.hasTag("display", "Name")) return Optional.empty();
return Optional.of(item.get("display", "Name"));
}
@Override
protected void itemName(ItemWrapper<ItemStack> item, String json) {
protected void itemName(LegacyItemWrapper item, String json) {
customName(item, json);
}
@Override
protected Optional<String> itemName(ItemWrapper<ItemStack> item) {
protected Optional<String> itemName(LegacyItemWrapper item) {
return customName(item);
}
@Override
protected void customModelData(ItemWrapper<ItemStack> item, Integer data) {
protected void customModelData(LegacyItemWrapper item, Integer data) {
if (data == null) {
item.remove("CustomModelData");
} else {
@@ -98,13 +98,13 @@ public class UniversalItemFactory extends BukkitItemFactory {
}
@Override
protected Optional<Integer> customModelData(ItemWrapper<ItemStack> item) {
protected Optional<Integer> customModelData(LegacyItemWrapper item) {
if (!item.hasTag("CustomModelData")) return Optional.empty();
return Optional.of(item.get("CustomModelData"));
}
@Override
protected void skull(ItemWrapper<ItemStack> item, String skullData) {
protected void skull(LegacyItemWrapper item, String skullData) {
if (skullData == null) {
item.remove("SkullOwner");
} else {
@@ -117,13 +117,13 @@ public class UniversalItemFactory extends BukkitItemFactory {
}
@Override
protected Optional<List<String>> lore(ItemWrapper<ItemStack> item) {
protected Optional<List<String>> lore(LegacyItemWrapper item) {
if (!item.hasTag("display", "Lore")) return Optional.empty();
return Optional.of(item.get("display", "Lore"));
}
@Override
protected void lore(ItemWrapper<ItemStack> item, List<String> lore) {
protected void lore(LegacyItemWrapper item, List<String> lore) {
if (lore == null || lore.isEmpty()) {
item.remove("display", "Lore");
} else {
@@ -132,48 +132,38 @@ public class UniversalItemFactory extends BukkitItemFactory {
}
@Override
protected boolean unbreakable(ItemWrapper<ItemStack> item) {
protected boolean unbreakable(LegacyItemWrapper item) {
return Optional.ofNullable((Boolean) item.get("Unbreakable")).orElse(false);
}
@Override
protected void unbreakable(ItemWrapper<ItemStack> item, boolean unbreakable) {
protected void unbreakable(LegacyItemWrapper item, boolean unbreakable) {
item.set(unbreakable, "Unbreakable");
}
@Override
protected Optional<Boolean> glint(ItemWrapper<ItemStack> item) {
return Optional.of(false);
}
@Override
protected void glint(ItemWrapper<ItemStack> item, Boolean glint) {
throw new UnsupportedOperationException("This feature is only available on 1.20.5+");
}
@Override
protected Optional<Integer> damage(ItemWrapper<ItemStack> item) {
protected Optional<Integer> damage(LegacyItemWrapper item) {
if (!item.hasTag("Damage")) return Optional.empty();
return Optional.of(item.get("Damage"));
}
@Override
protected void damage(ItemWrapper<ItemStack> item, Integer damage) {
protected void damage(LegacyItemWrapper item, Integer damage) {
item.set(damage, "Damage");
}
@Override
protected Optional<Integer> maxDamage(ItemWrapper<ItemStack> item) {
protected Optional<Integer> maxDamage(LegacyItemWrapper item) {
return Optional.of((int) item.getItem().getType().getMaxDurability());
}
@Override
protected void maxDamage(ItemWrapper<ItemStack> item, Integer damage) {
protected void maxDamage(LegacyItemWrapper item, Integer damage) {
throw new UnsupportedOperationException("This feature is only available on 1.20.5+");
}
@Override
protected void enchantments(ItemWrapper<ItemStack> item, List<Enchantment> enchantments) {
protected void enchantments(LegacyItemWrapper item, List<Enchantment> enchantments) {
if (enchantments == null || enchantments.isEmpty()) {
item.remove("Enchantments");
return;
@@ -186,7 +176,7 @@ public class UniversalItemFactory extends BukkitItemFactory {
}
@Override
protected void storedEnchantments(ItemWrapper<ItemStack> item, List<Enchantment> enchantments) {
protected void storedEnchantments(LegacyItemWrapper item, List<Enchantment> enchantments) {
if (enchantments == null || enchantments.isEmpty()) {
item.remove("StoredEnchantments");
return;
@@ -199,7 +189,7 @@ public class UniversalItemFactory extends BukkitItemFactory {
}
@Override
protected void addEnchantment(ItemWrapper<ItemStack> item, Enchantment enchantment) {
protected void addEnchantment(LegacyItemWrapper item, Enchantment enchantment) {
Object enchantments = item.getExact("Enchantments");
if (enchantments != null) {
for (Object enchant : TagList.getValue(enchantments)) {
@@ -215,7 +205,7 @@ public class UniversalItemFactory extends BukkitItemFactory {
}
@Override
protected void addStoredEnchantment(ItemWrapper<ItemStack> item, Enchantment enchantment) {
protected void addStoredEnchantment(LegacyItemWrapper item, Enchantment enchantment) {
Object enchantments = item.getExact("StoredEnchantments");
if (enchantments != null) {
for (Object enchant : TagList.getValue(enchantments)) {
@@ -232,14 +222,14 @@ public class UniversalItemFactory extends BukkitItemFactory {
@SuppressWarnings("deprecation")
@Override
protected Optional<Enchantment> getEnchantment(ItemWrapper<ItemStack> item, Key key) {
protected Optional<Enchantment> getEnchantment(LegacyItemWrapper item, Key key) {
int level = item.getItem().getEnchantmentLevel(Objects.requireNonNull(Registry.ENCHANTMENT.get(new NamespacedKey(key.namespace(), key.value()))));
if (level <= 0) return Optional.empty();
return Optional.of(new Enchantment(key, level));
}
@Override
protected void itemFlags(ItemWrapper<ItemStack> item, List<String> flags) {
protected void itemFlags(LegacyItemWrapper item, List<String> flags) {
if (flags == null || flags.isEmpty()) {
item.remove("HideFlags");
return;
@@ -253,44 +243,28 @@ public class UniversalItemFactory extends BukkitItemFactory {
}
@Override
protected int maxStackSize(ItemWrapper<ItemStack> item) {
protected int maxStackSize(LegacyItemWrapper item) {
return item.getItem().getType().getMaxStackSize();
}
@Override
protected void maxStackSize(ItemWrapper<ItemStack> item, Integer maxStackSize) {
protected void maxStackSize(LegacyItemWrapper item, Integer maxStackSize) {
throw new UnsupportedOperationException("This feature is only available on 1.20.5+");
}
@Override
protected void repairCost(ItemWrapper<ItemStack> item, Integer data) {
protected void repairCost(LegacyItemWrapper item, Integer data) {
item.set(data, "RepairCost");
}
@Override
protected Optional<Integer> repairCost(ItemWrapper<ItemStack> item) {
protected Optional<Integer> repairCost(LegacyItemWrapper item) {
if (!item.hasTag("RepairCost")) return Optional.empty();
return Optional.of(item.get("RepairCost"));
}
@Override
protected ItemWrapper<ItemStack> mergeCopy(ItemWrapper<ItemStack> item1, ItemWrapper<ItemStack> item2) {
Object itemStack = ItemObject.copy(item2.getLiteralObject());
ItemObject.setCustomDataTag(itemStack, TagCompound.clone(ItemObject.getCustomDataTag(item1.getLiteralObject())));
// one more step than vanilla
TagCompound.merge(ItemObject.getCustomDataTag(itemStack), ItemObject.getCustomDataTag(item2.getLiteralObject()), true, true);
return new RTagItemWrapper(new RtagItem(ItemObject.asCraftMirror(itemStack)), item2.count());
}
@Override
protected void merge(ItemWrapper<ItemStack> item1, ItemWrapper<ItemStack> item2) {
// load previous changes on nms items
item1.load();
TagCompound.merge(ItemObject.getCustomDataTag(item1.getLiteralObject()), ItemObject.getCustomDataTag(item2.getLiteralObject()), true, true);
}
@Override
protected void trim(ItemWrapper<ItemStack> item, Trim trim) {
protected void trim(LegacyItemWrapper item, Trim trim) {
if (trim == null) {
item.remove("Trim");
return;
@@ -300,10 +274,28 @@ public class UniversalItemFactory extends BukkitItemFactory {
}
@Override
protected Optional<Trim> trim(ItemWrapper<ItemStack> item) {
protected Optional<Trim> trim(LegacyItemWrapper item) {
String material = item.get("Trim", "material");
String pattern = item.get("Trim", "pattern");
if (material == null || pattern == null) return Optional.empty();
return Optional.of(new Trim(material, pattern));
}
@Override
protected LegacyItemWrapper mergeCopy(LegacyItemWrapper item1, LegacyItemWrapper item2) {
Object itemStack = ItemObject.copy(item2.getLiteralObject());
ItemObject.setCustomDataTag(itemStack, TagCompound.clone(ItemObject.getCustomDataTag(item1.getLiteralObject())));
// one more step than vanilla
TagCompound.merge(ItemObject.getCustomDataTag(itemStack), ItemObject.getCustomDataTag(item2.getLiteralObject()), true, true);
return new LegacyItemWrapper(new RtagItem(ItemObject.asCraftMirror(itemStack)), item2.count());
}
@Override
protected void merge(LegacyItemWrapper item1, LegacyItemWrapper item2) {
// load previous changes on nms items
item1.load();
TagCompound.merge(ItemObject.getCustomDataTag(item1.getLiteralObject()), ItemObject.getCustomDataTag(item2.getLiteralObject()), true, true);
// update wrapped item
item1.update();
}
}

View File

@@ -3,6 +3,7 @@ package net.momirealms.craftengine.bukkit.item.recipe;
import com.destroystokyo.paper.event.inventory.PrepareResultEvent;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.item.ComponentTypes;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.injector.BukkitInjector;
@@ -447,7 +448,7 @@ public class RecipeEventListener implements Listener {
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOW)
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onAnvilCombineItems(PrepareAnvilEvent event) {
AnvilInventory inventory = event.getInventory();
ItemStack first = inventory.getFirstItem();
@@ -490,7 +491,7 @@ public class RecipeEventListener implements Listener {
}
@SuppressWarnings("UnstableApiUsage")
@EventHandler(ignoreCancelled = true, priority = EventPriority.NORMAL)
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOW)
public void onAnvilRepairItems(PrepareAnvilEvent event) {
AnvilInventory inventory = event.getInventory();
ItemStack first = inventory.getFirstItem();
@@ -511,7 +512,7 @@ public class RecipeEventListener implements Listener {
return;
}
Item<ItemStack> wrappedFirst = BukkitItemManager.instance().wrap(first);
Item<ItemStack> wrappedFirst = BukkitItemManager.instance().wrap(first.clone());
int maxDamage = wrappedFirst.maxDamage().orElse(0);
int damage = wrappedFirst.damage().orElse(0);
@@ -587,7 +588,7 @@ public class RecipeEventListener implements Listener {
} catch (ReflectiveOperationException e) {
plugin.logger().warn("Failed to get hover name", e);
}
} else if (VersionHelper.isVersionNewerThan1_20_5() && wrappedFirst.hasComponent(ComponentKeys.CUSTOM_NAME)) {
} else if (VersionHelper.isVersionNewerThan1_20_5() && wrappedFirst.hasComponent(ComponentTypes.CUSTOM_NAME)) {
repairCost += 1;
wrappedFirst.customName(null);
} else if (!VersionHelper.isVersionNewerThan1_20_5() && wrappedFirst.hasTag("display", "Name")) {
@@ -639,12 +640,12 @@ public class RecipeEventListener implements Listener {
}
afterPenalty = calculateIncreasedRepairCost(afterPenalty);
wrappedFirst.repairCost(afterPenalty);
event.setResult(wrappedFirst.loadCopy());
event.setResult(wrappedFirst.load());
}
}
@SuppressWarnings("UnstableApiUsage")
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH)
@EventHandler(ignoreCancelled = true, priority = EventPriority.NORMAL)
public void onAnvilRenameItem(PrepareAnvilEvent event) {
AnvilInventory inventory = event.getInventory();
ItemStack first = inventory.getFirstItem();

View File

@@ -1,6 +1,7 @@
package net.momirealms.craftengine.bukkit.plugin;
import net.momirealms.antigrieflib.AntiGriefLib;
import net.momirealms.craftengine.bukkit.advancement.BukkitAdvancementManager;
import net.momirealms.craftengine.bukkit.api.event.CraftEngineReloadEvent;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.block.behavior.BukkitBlockBehaviors;
@@ -156,6 +157,7 @@ public class BukkitCraftEngine extends CraftEngine {
super.soundManager = new BukkitSoundManager(this);
super.vanillaLootManager = new BukkitVanillaLootManager(this);
super.fontManager = new BukkitFontManager(this);
super.advancementManager = new BukkitAdvancementManager(this);
super.onPluginEnable();
// compatibility
// register expansion
@@ -245,6 +247,11 @@ public class BukkitCraftEngine extends CraftEngine {
return (BukkitBlockManager) blockManager;
}
@Override
public BukkitAdvancementManager advancementManager() {
return (BukkitAdvancementManager) advancementManager;
}
@Override
public BukkitFurnitureManager furnitureManager() {
return (BukkitFurnitureManager) furnitureManager;

View File

@@ -1,8 +1,10 @@
package net.momirealms.craftengine.bukkit.plugin.command.feature;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
import net.momirealms.craftengine.core.util.Key;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.incendo.cloud.Command;
@@ -19,6 +21,7 @@ public class TestCommand extends BukkitCommandFeature<CommandSender> {
.senderType(Player.class)
.handler(context -> {
Player player = context.sender();
player.getInventory().addItem(BukkitItemManager.instance().createWrappedItem(Key.from("default:topaz"), null).getItem());
});
}

View File

@@ -1516,7 +1516,7 @@ public class PacketConsumers {
event.setCancelled(true);
}
}
} else if (entityType == Reflections.instance$EntityType$SHULKER) {
} else if (entityType == BukkitFurnitureManager.NMS_COLLISION_ENTITY_TYPE) {
// Cancel collider entity packet
int entityId = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$entityId(packet);
LoadedFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(entityId);

View File

@@ -97,13 +97,13 @@ public class BukkitServerPlayer extends Player {
this.serverPlayerRef = new WeakReference<>(FastNMS.INSTANCE.method$CraftPlayer$getHandle(player));
this.uuid = player.getUniqueId();
this.name = player.getName();
if (Reflections.method$CraftPlayer$setSimplifyContainerDesyncCheck != null) {
try {
Reflections.method$CraftPlayer$setSimplifyContainerDesyncCheck.invoke(player, true);
} catch (Exception e) {
this.plugin.logger().warn("Failed to setSimplifyContainerDesyncCheck", e);
}
}
// if (Reflections.method$CraftPlayer$setSimplifyContainerDesyncCheck != null) {
// try {
// Reflections.method$CraftPlayer$setSimplifyContainerDesyncCheck.invoke(player, true);
// } catch (Exception e) {
// this.plugin.logger().warn("Failed to setSimplifyContainerDesyncCheck", e);
// }
// }
}
@Override
@@ -317,7 +317,17 @@ public class BukkitServerPlayer extends Player {
public void tick() {
// not fully online
if (serverPlayer() == null) return;
this.gameTicks = FastNMS.INSTANCE.field$MinecraftServer$currentTick();
if (VersionHelper.isFolia()) {
try {
Object serverPlayer = serverPlayer();
Object gameMode = FastNMS.INSTANCE.field$ServerPlayer$gameMode(serverPlayer);
this.gameTicks = (int) Reflections.field$ServerPlayerGameMode$gameTicks.get(gameMode);
} catch (ReflectiveOperationException e) {
CraftEngine.instance().logger().warn("Failed to get game tick for " + name(), e);
}
} else {
this.gameTicks = FastNMS.INSTANCE.field$MinecraftServer$currentTick();
}
if (this.isDestroyingBlock) {
this.tickBlockDestroy();
}

View File

@@ -2919,11 +2919,11 @@ public class Reflections {
)
);
// public static final Field field$ServerPlayer$gameMode = requireNonNull(
// ReflectionUtils.getDeclaredField(
// clazz$ServerPlayer, clazz$ServerPlayerGameMode, 0
// )
// );
public static final Field field$ServerPlayer$gameMode = requireNonNull(
ReflectionUtils.getDeclaredField(
clazz$ServerPlayer, clazz$ServerPlayerGameMode, 0
)
);
public static final Field field$ServerPlayerGameMode$destroyProgressStart = requireNonNull(
ReflectionUtils.getDeclaredField(
@@ -2931,11 +2931,11 @@ public class Reflections {
)
);
// public static final Field field$ServerPlayerGameMode$gameTicks = requireNonNull(
// ReflectionUtils.getDeclaredField(
// clazz$ServerPlayerGameMode, int.class, 1
// )
// );
public static final Field field$ServerPlayerGameMode$gameTicks = requireNonNull(
ReflectionUtils.getDeclaredField(
clazz$ServerPlayerGameMode, int.class, 1
)
);
public static final Field field$ServerPlayerGameMode$delayedTickStart = requireNonNull(
ReflectionUtils.getDeclaredField(
@@ -3773,6 +3773,7 @@ public class Reflections {
public static final Object instance$EntityType$FALLING_BLOCK;
public static final Object instance$EntityType$INTERACTION;
public static final Object instance$EntityType$SHULKER;
public static final Object instance$EntityType$OAK_BOAT;
static {
try {
@@ -3790,6 +3791,8 @@ public class Reflections {
instance$EntityType$SHULKER = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, shulker);
Object armorStand = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "armor_stand");
instance$EntityType$ARMOR_STAND = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, armorStand);
Object oakBoat = VersionHelper.isVersionNewerThan1_21_2() ? FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "oak_boat") : FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", "boat");
instance$EntityType$OAK_BOAT = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$ENTITY_TYPE, oakBoat);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
@@ -5294,25 +5297,25 @@ public class Reflections {
)
);
// 1.20.5+
public static final Method method$ItemStack$transmuteCopy = ReflectionUtils.getMethod(
clazz$ItemStack, clazz$ItemStack, clazz$ItemLike, int.class
);
// // 1.20.5+
// public static final Method method$ItemStack$transmuteCopy = ReflectionUtils.getMethod(
// clazz$ItemStack, clazz$ItemStack, clazz$ItemLike, int.class
// );
// 1.20.5+
public static final Class<?> clazz$DataComponentPatch = ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("core.component.DataComponentPatch")
);
// 1.20.5+
public static final Method method$ItemStack$getComponentsPatch = Optional.ofNullable(clazz$DataComponentPatch)
.map(it -> ReflectionUtils.getMethod(clazz$ItemStack, it))
.orElse(null);
// 1.20.5+
public static final Method method$ItemStack$applyComponents = Optional.ofNullable(clazz$DataComponentPatch)
.map(it -> ReflectionUtils.getMethod(clazz$ItemStack, void.class, it))
.orElse(null);
// // 1.20.5+
// public static final Method method$ItemStack$getComponentsPatch = Optional.ofNullable(clazz$DataComponentPatch)
// .map(it -> ReflectionUtils.getMethod(clazz$ItemStack, it))
// .orElse(null);
//
// // 1.20.5+ WRONG!!!
// public static final Method method$ItemStack$applyComponents = Optional.ofNullable(clazz$DataComponentPatch)
// .map(it -> ReflectionUtils.getMethod(clazz$ItemStack, void.class, it))
// .orElse(null);
public static final Method method$ItemStack$getItem = requireNonNull(
ReflectionUtils.getMethod(
@@ -6512,4 +6515,10 @@ public class Reflections {
? ReflectionUtils.getConstructor(clazz$ServerboundResourcePackPacket, UUID.class, clazz$ServerboundResourcePackPacket$Action)
: ReflectionUtils.getConstructor(clazz$ServerboundResourcePackPacket, clazz$ServerboundResourcePackPacket$Action)
);
public static final Class<?> clazz$DataComponentType = ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass(
"core.component.DataComponentType"
)
);
}

View File

@@ -0,0 +1,11 @@
package net.momirealms.craftengine.core.advancement;
import net.momirealms.craftengine.core.plugin.CraftEngine;
public abstract class AbstractAdvancementManager implements AdvancementManager {
private final CraftEngine plugin;
public AbstractAdvancementManager(CraftEngine plugin) {
this.plugin = plugin;
}
}

View File

@@ -0,0 +1,9 @@
package net.momirealms.craftengine.core.advancement;
import net.momirealms.craftengine.core.plugin.Manageable;
import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser;
public interface AdvancementManager extends Manageable {
ConfigSectionParser parser();
}

View File

@@ -6,7 +6,5 @@ public interface Collider {
int entityId();
ColliderType type();
Object handle();
}

View File

@@ -1,5 +1,6 @@
package net.momirealms.craftengine.core.entity.furniture;
public enum ColliderType {
SHULKER
INTERACTION,
BOAT
}

View File

@@ -8,14 +8,58 @@ import java.util.List;
import java.util.Optional;
public class AbstractItem<W extends ItemWrapper<I>, I> implements Item<I> {
private final ItemFactory<?, W, I> factory;
private final ItemWrapper<I> item;
private final ItemFactory<W, I> factory;
private final W item;
AbstractItem(ItemFactory<?, W, I> factory, ItemWrapper<I> item) {
AbstractItem(ItemFactory<W, I> factory, W item) {
this.factory = factory;
this.item = item;
}
@Override
public Item<I> itemModel(String data) {
this.factory.itemModel(this.item, data);
return this;
}
@Override
public Optional<String> itemModel() {
return this.factory.itemModel(this.item);
}
@Override
public Optional<JukeboxPlayable> jukeboxSong() {
return this.factory.jukeboxSong(this.item);
}
@Override
public Item<I> jukeboxSong(JukeboxPlayable data) {
this.factory.jukeboxSong(this.item, data);
return this;
}
@Override
public Optional<EquipmentData> equippable() {
return this.factory.equippable(this.item);
}
@Override
public Item<I> equippable(EquipmentData data) {
this.factory.equippable(this.item, data);
return this;
}
@Override
public Item<I> tooltipStyle(String data) {
this.factory.tooltipStyle(this.item, data);
return this;
}
@Override
public Optional<String> tooltipStyle() {
return this.factory.tooltipStyle(this.item);
}
@Override
public Item<I> damage(Integer data) {
this.factory.damage(this.item, data);
@@ -85,6 +129,12 @@ public class AbstractItem<W extends ItemWrapper<I>, I> implements Item<I> {
return this.factory.customId(this.item);
}
@Override
public Item<I> customId(Key data) {
this.factory.customId(this.item, data);
return this;
}
@Override
public int count() {
return this.item.count();
@@ -124,8 +174,9 @@ public class AbstractItem<W extends ItemWrapper<I>, I> implements Item<I> {
}
@Override
public Optional<String> itemName() {
return this.factory.itemName(this.item);
public Item<I> customName(String displayName) {
this.factory.customName(this.item, displayName);
return this;
}
@Override
@@ -150,10 +201,10 @@ public class AbstractItem<W extends ItemWrapper<I>, I> implements Item<I> {
return this.factory.unbreakable(this.item);
}
@Override
public Item<I> customName(String displayName) {
this.factory.customName(this.item, displayName);
return this;
public Optional<String> itemName() {
return this.factory.itemName(this.item);
}
@Override
@@ -236,37 +287,37 @@ public class AbstractItem<W extends ItemWrapper<I>, I> implements Item<I> {
}
@Override
public boolean hasComponent(Key type) {
public boolean hasComponent(Object type) {
return this.factory.hasComponent(this.item, type);
}
@Override
public void removeComponent(Key type) {
public void removeComponent(Object type) {
this.factory.removeComponent(this.item, type);
}
@Override
public Object getComponent(Key type) {
public Object getComponent(Object type) {
return this.factory.getComponent(this.item, type);
}
@Override
public Object getJavaTypeComponent(Key type) {
public Object getJavaTypeComponent(Object type) {
return this.factory.encodeJava(type, getComponent(type));
}
@Override
public JsonElement getJsonTypeComponent(Key type) {
public JsonElement getJsonTypeComponent(Object type) {
return this.factory.encodeJson(type, getComponent(type));
}
@Override
public void setComponent(Key type, Object value) {
public void setComponent(Object type, Object value) {
this.factory.setComponent(this.item, type, value);
}
@Override
public void resetComponent(Key type) {
public void resetComponent(Object type) {
this.factory.resetComponent(this.item, type);
}
@@ -280,19 +331,10 @@ public class AbstractItem<W extends ItemWrapper<I>, I> implements Item<I> {
return this.factory.load(this.item);
}
@SuppressWarnings({"unchecked"})
@Override
public I loadCopy() {
return this.factory.loadCopy(this.item);
}
@Override
public void update() {
this.factory.update(this.item);
}
@Override
public Item<I> copyWithCount(int count) {
return new AbstractItem<>(this.factory, this.item.copyWithCount(count));
public AbstractItem<W, I> copyWithCount(int count) {
return new AbstractItem<>(this.factory, (W) this.item.copyWithCount(count));
}
@Override
@@ -307,13 +349,13 @@ public class AbstractItem<W extends ItemWrapper<I>, I> implements Item<I> {
@SuppressWarnings({"unchecked", "rawtypes"})
@Override
public Item<I> mergeCopy(Item<?> another) {
return new AbstractItem<>(this.factory, this.factory.mergeCopy(this.item, ((AbstractItem) another).item));
public AbstractItem<W, I> mergeCopy(Item<?> another) {
return new AbstractItem<>(this.factory, this.factory.mergeCopy(this.item, (W) ((AbstractItem) another).item));
}
@SuppressWarnings({"unchecked", "rawtypes"})
@Override
public void merge(Item<I> another) {
this.factory.merge(this.item, ((AbstractItem) another).item);
this.factory.merge(this.item, (W) ((AbstractItem) another).item);
}
}

View File

@@ -238,7 +238,7 @@ public abstract class AbstractItemManager<I> extends AbstractModelGenerator impl
if (VersionHelper.isVersionNewerThan1_21()) {
registerDataFunction((obj) -> {
String song = obj.toString();
return new JukeboxSongModifier<>(Key.of(song));
return new JukeboxSongModifier<>(new JukeboxPlayable(song, true));
}, "jukebox-playable");
}
if (VersionHelper.isVersionNewerThan1_21_2()) {

View File

@@ -30,6 +30,8 @@ public interface Item<I> {
Optional<Key> customId();
Item<I> customId(Key id);
int count();
Item<I> count(int amount);
@@ -66,8 +68,24 @@ public interface Item<I> {
Optional<String> itemName();
Item<I> itemModel(String itemModel);
Optional<String> itemModel();
Item<I> tooltipStyle(String tooltipStyle);
Optional<String> tooltipStyle();
Item<I> lore(List<String> lore);
Optional<JukeboxPlayable> jukeboxSong();
Item<I> jukeboxSong(JukeboxPlayable song);
Optional<EquipmentData> equippable();
Item<I> equippable(EquipmentData equipmentData);
Optional<List<String>> lore();
Item<I> unbreakable(boolean unbreakable);
@@ -96,28 +114,24 @@ public interface Item<I> {
boolean removeTag(Object... path);
boolean hasComponent(Key type);
boolean hasComponent(Object type);
void removeComponent(Key type);
void removeComponent(Object type);
Object getComponent(Key type);
Object getComponent(Object type);
Object getJavaTypeComponent(Key type);
Object getJavaTypeComponent(Object type);
JsonElement getJsonTypeComponent(Key type);
JsonElement getJsonTypeComponent(Object type);
void setComponent(Key type, Object value);
void setComponent(Object type, Object value);
void resetComponent(Key type);
void resetComponent(Object type);
I getItem();
I load();
I loadCopy();
void update();
int maxStackSize();
Item<I> maxStackSize(int amount);

View File

@@ -1,7 +1,7 @@
package net.momirealms.craftengine.core.item;
import com.google.gson.JsonElement;
import net.momirealms.craftengine.core.plugin.Plugin;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.util.Key;
import org.jetbrains.annotations.Nullable;
@@ -9,10 +9,10 @@ import java.util.List;
import java.util.Objects;
import java.util.Optional;
public abstract class ItemFactory<P extends Plugin, W extends ItemWrapper<I>, I> {
protected final P plugin;
public abstract class ItemFactory<W extends ItemWrapper<I>, I> {
protected final CraftEngine plugin;
protected ItemFactory(P plugin) {
protected ItemFactory(CraftEngine plugin) {
this.plugin = plugin;
}
@@ -21,107 +21,121 @@ public abstract class ItemFactory<P extends Plugin, W extends ItemWrapper<I>, I>
return new AbstractItem<>(this, wrapInternal(item));
}
public abstract Object encodeJava(Key componentType, @Nullable Object component);
protected abstract W mergeCopy(W item1, W item2);
protected abstract JsonElement encodeJson(Key type, Object component);
protected abstract void merge(W item1, W item2);
protected abstract ItemWrapper<I> wrapInternal(I item);
protected abstract Object encodeJava(Object type, @Nullable Object component);
protected abstract Object getTag(ItemWrapper<I> item, Object... path);
protected abstract JsonElement encodeJson(Object type, Object component);
protected abstract void setTag(ItemWrapper<I> item, Object value, Object... path);
protected abstract W wrapInternal(I item);
protected abstract boolean hasTag(ItemWrapper<I> item, Object... path);
protected abstract Object getTag(W item, Object... path);
protected abstract boolean removeTag(ItemWrapper<I> item, Object... path);
protected abstract void setTag(W item, Object value, Object... path);
protected abstract void setComponent(ItemWrapper<I> item, Key type, Object value);
protected abstract boolean hasTag(W item, Object... path);
protected abstract Object getComponent(ItemWrapper<I> item, Key type);
protected abstract boolean removeTag(W item, Object... path);
protected abstract boolean hasComponent(ItemWrapper<I> item, Key type);
protected abstract void setComponent(W item, Object type, Object value);
protected abstract void removeComponent(ItemWrapper<I> item, Key type);
protected abstract Object getComponent(W item, Object type);
protected abstract void resetComponent(ItemWrapper<I> item, Key type);
protected abstract boolean hasComponent(W item, Object type);
protected abstract void update(ItemWrapper<I> item);
protected abstract void removeComponent(W item, Object type);
protected abstract I load(ItemWrapper<I> item);
protected abstract void resetComponent(W item, Object type);
protected abstract I getItem(ItemWrapper<I> item);
protected abstract I load(W item);
protected abstract I loadCopy(ItemWrapper<I> item);
protected abstract I getItem(W item);
protected abstract void customModelData(ItemWrapper<I> item, Integer data);
protected abstract void customModelData(W item, Integer data);
protected abstract Optional<Integer> customModelData(ItemWrapper<I> item);
protected abstract Optional<Integer> customModelData(W item);
protected abstract void customName(ItemWrapper<I> item, String json);
protected abstract void customName(W item, String json);
protected abstract Optional<String> customName(ItemWrapper<I> item);
protected abstract Optional<String> customName(W item);
protected abstract void itemName(ItemWrapper<I> item, String json);
protected abstract void itemName(W item, String json);
protected abstract Optional<String> itemName(ItemWrapper<I> item);
protected abstract Optional<String> itemName(W item);
protected abstract void skull(ItemWrapper<I> item, String skullData);
protected abstract void skull(W item, String skullData);
protected abstract Optional<List<String>> lore(ItemWrapper<I> item);
protected abstract Optional<List<String>> lore(W item);
protected abstract void lore(ItemWrapper<I> item, List<String> lore);
protected abstract void lore(W item, List<String> lore);
protected abstract boolean unbreakable(ItemWrapper<I> item);
protected abstract boolean unbreakable(W item);
protected abstract void unbreakable(ItemWrapper<I> item, boolean unbreakable);
protected abstract void unbreakable(W item, boolean unbreakable);
protected abstract Optional<Boolean> glint(ItemWrapper<I> item);
protected abstract Optional<Boolean> glint(W item);
protected abstract void glint(ItemWrapper<I> item, Boolean glint);
protected abstract void glint(W item, Boolean glint);
protected abstract Optional<Integer> damage(ItemWrapper<I> item);
protected abstract Optional<Integer> damage(W item);
protected abstract void damage(ItemWrapper<I> item, Integer damage);
protected abstract void damage(W item, Integer damage);
protected abstract Optional<Integer> maxDamage(ItemWrapper<I> item);
protected abstract Optional<Integer> maxDamage(W item);
protected abstract void maxDamage(ItemWrapper<I> item, Integer damage);
protected abstract void maxDamage(W item, Integer damage);
protected abstract void enchantments(ItemWrapper<I> item, List<Enchantment> enchantments);
protected abstract void enchantments(W item, List<Enchantment> enchantments);
protected abstract void storedEnchantments(ItemWrapper<I> item, List<Enchantment> enchantments);
protected abstract void storedEnchantments(W item, List<Enchantment> enchantments);
protected abstract void addEnchantment(ItemWrapper<I> item, Enchantment enchantment);
protected abstract void addEnchantment(W item, Enchantment enchantment);
protected abstract void addStoredEnchantment(ItemWrapper<I> item, Enchantment enchantment);
protected abstract void addStoredEnchantment(W item, Enchantment enchantment);
protected abstract Optional<Enchantment> getEnchantment(ItemWrapper<I> item, Key key);
protected abstract Optional<Enchantment> getEnchantment(W item, Key key);
protected abstract void itemFlags(ItemWrapper<I> item, List<String> flags);
protected abstract void itemFlags(W item, List<String> flags);
protected abstract Key id(ItemWrapper<I> item);
protected abstract Key id(W item);
protected abstract Optional<Key> customId(ItemWrapper<I> item);
protected abstract Optional<Key> customId(W item);
protected abstract Key vanillaId(ItemWrapper<I> item);
protected abstract void customId(W item, Key id);
protected abstract int maxStackSize(ItemWrapper<I> item);
protected abstract Key vanillaId(W item);
protected abstract void maxStackSize(ItemWrapper<I> item, Integer maxStackSize);
protected abstract int maxStackSize(W item);
protected abstract boolean is(ItemWrapper<I> item, Key itemTag);
protected abstract void maxStackSize(W item, Integer maxStackSize);
protected abstract boolean isBlockItem(ItemWrapper<I> item);
protected abstract boolean is(W item, Key itemTag);
protected abstract void repairCost(ItemWrapper<I> item, Integer data);
protected abstract boolean isBlockItem(W item);
protected abstract Optional<Integer> repairCost(ItemWrapper<I> item);
protected abstract void repairCost(W item, Integer data);
protected abstract void trim(ItemWrapper<I> item, Trim trim);
protected abstract Optional<Integer> repairCost(W item);
protected abstract Optional<Trim> trim(ItemWrapper<I> item);
protected abstract void trim(W item, Trim trim);
protected abstract ItemWrapper<I> mergeCopy(ItemWrapper<I> item1, ItemWrapper<I> item2);
protected abstract Optional<Trim> trim(W item);
protected abstract void merge(ItemWrapper<I> item1, ItemWrapper<I> item2);
protected abstract void tooltipStyle(W item, String data);
protected abstract Optional<String> tooltipStyle(W item);
protected abstract void jukeboxSong(W item, JukeboxPlayable data);
protected abstract Optional<JukeboxPlayable> jukeboxSong(W item);
protected abstract void itemModel(W item, String data);
protected abstract Optional<String> itemModel(W item);
protected abstract void equippable(W item, EquipmentData data);
protected abstract Optional<EquipmentData> equippable(W item);
}

View File

@@ -78,6 +78,4 @@ public interface ItemManager<T> extends Manageable, ModelGenerator {
Collection<Suggestion> cachedSuggestions();
Collection<Suggestion> cachedTotemSuggestions();
Object encodeJava(Key componentType, @Nullable Object component);
}

View File

@@ -1,44 +1,16 @@
package net.momirealms.craftengine.core.item;
import java.util.Map;
public interface ItemWrapper<I> {
I getItem();
void update();
I load();
I loadCopy();
Object getLiteralObject();
boolean set(Object value, Object... path);
boolean add(Object value, Object... path);
<V> V get(Object... path);
<V> V getExact(Object... path);
boolean remove(Object... path);
boolean hasTag(Object... path);
void removeComponent(Object type);
boolean hasComponent(Object type);
void setComponent(Object type, Object value);
Object getComponent(Object type);
int count();
void count(int amount);
ItemWrapper<I> copyWithCount(int count);
Map<String, Object> getData();
}

View File

@@ -0,0 +1,4 @@
package net.momirealms.craftengine.core.item;
public record JukeboxPlayable(String song, boolean showInToolTip) {
}

View File

@@ -1,6 +1,5 @@
package net.momirealms.craftengine.core.item.modifier;
import net.momirealms.craftengine.core.item.ComponentKeys;
import net.momirealms.craftengine.core.item.EquipmentData;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.item.ItemBuildContext;
@@ -19,11 +18,11 @@ public class EquippableModifier<I> implements ItemDataModifier<I> {
@Override
public void apply(Item<I> item, ItemBuildContext context) {
item.setComponent(ComponentKeys.EQUIPPABLE, this.data.toMap());
item.equippable(this.data);
}
@Override
public void remove(Item<I> item) {
item.removeComponent(ComponentKeys.EQUIPPABLE);
item.equippable(null);
}
}

View File

@@ -1,29 +1,11 @@
package net.momirealms.craftengine.core.item.modifier;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import net.momirealms.craftengine.core.item.ComponentKeys;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.item.ItemBuildContext;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.VersionHelper;
import java.util.Map;
import java.util.function.BiConsumer;
public class IdModifier<I> implements ItemDataModifier<I> {
public static final String CRAFT_ENGINE_ID = "craftengine:id";
private static final BiConsumer<Item<?>, String> ID_SETTER = VersionHelper.isVersionNewerThan1_20_5() ?
((item, id) -> {
JsonElement element = item.getJsonTypeComponent(ComponentKeys.CUSTOM_DATA);
if (element instanceof JsonObject jo) {
jo.add(CRAFT_ENGINE_ID, new JsonPrimitive(id));
item.setComponent(ComponentKeys.CUSTOM_DATA, jo);
} else {
item.setComponent(ComponentKeys.CUSTOM_DATA, Map.of(CRAFT_ENGINE_ID, id));
}
}) : ((item, id) -> item.setTag(id, CRAFT_ENGINE_ID));
private final Key argument;
public IdModifier(Key argument) {
@@ -37,11 +19,11 @@ public class IdModifier<I> implements ItemDataModifier<I> {
@Override
public void apply(Item<I> item, ItemBuildContext context) {
ID_SETTER.accept(item, argument.toString());
item.customId(this.argument);
}
@Override
public void remove(Item<I> item) {
item.removeTag(CRAFT_ENGINE_ID);
// WHY DO YOU WANT TO REMOVE CRAFTENGINE ID?
}
}

View File

@@ -1,6 +1,5 @@
package net.momirealms.craftengine.core.item.modifier;
import net.momirealms.craftengine.core.item.ComponentKeys;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.item.ItemBuildContext;
import net.momirealms.craftengine.core.util.Key;
@@ -19,11 +18,11 @@ public class ItemModelModifier<I> implements ItemDataModifier<I> {
@Override
public void apply(Item<I> item, ItemBuildContext context) {
item.setComponent(ComponentKeys.ITEM_MODEL, this.data.toString());
item.itemModel(this.data.toString());
}
@Override
public void remove(Item<I> item) {
item.removeComponent(ComponentKeys.ITEM_MODEL);
item.itemModel(null);
}
}

View File

@@ -1,17 +1,13 @@
package net.momirealms.craftengine.core.item.modifier;
import net.momirealms.craftengine.core.item.ComponentKeys;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.item.ItemBuildContext;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.VersionHelper;
import java.util.Map;
import net.momirealms.craftengine.core.item.JukeboxPlayable;
public class JukeboxSongModifier<I> implements ItemDataModifier<I> {
private final Key song;
private final JukeboxPlayable song;
public JukeboxSongModifier(Key song) {
public JukeboxSongModifier(JukeboxPlayable song) {
this.song = song;
}
@@ -22,18 +18,11 @@ public class JukeboxSongModifier<I> implements ItemDataModifier<I> {
@Override
public void apply(Item<I> item, ItemBuildContext context) {
if (VersionHelper.isVersionNewerThan1_21_5()) {
item.setComponent(ComponentKeys.JUKEBOX_PLAYABLE, song.toString());
} else {
item.setComponent(ComponentKeys.JUKEBOX_PLAYABLE, Map.of(
"song", song.toString(),
"show_in_tooltip", true
));
}
item.jukeboxSong(this.song);
}
@Override
public void remove(Item<I> item) {
item.removeComponent(ComponentKeys.JUKEBOX_PLAYABLE);
item.jukeboxSong(null);
}
}

View File

@@ -21,7 +21,7 @@ public class LoreModifier<I> implements ItemDataModifier<I> {
@Override
public void apply(Item<I> item, ItemBuildContext context) {
item.lore(argument.stream().map(it -> AdventureHelper.componentToJson(AdventureHelper.miniMessage().deserialize(
item.lore(this.argument.stream().map(it -> AdventureHelper.componentToJson(AdventureHelper.miniMessage().deserialize(
it, context.tagResolvers()))).toList());
}

View File

@@ -1,6 +1,5 @@
package net.momirealms.craftengine.core.item.modifier;
import net.momirealms.craftengine.core.item.ComponentKeys;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.item.ItemBuildContext;
import net.momirealms.craftengine.core.util.Key;
@@ -19,11 +18,11 @@ public class TooltipStyleModifier<I> implements ItemDataModifier<I> {
@Override
public void apply(Item<I> item, ItemBuildContext context) {
item.setComponent(ComponentKeys.TOOLTIP_STYLE, argument.toString());
item.tooltipStyle(argument.toString());
}
@Override
public void remove(Item<I> item) {
item.removeComponent(ComponentKeys.TOOLTIP_STYLE);
item.tooltipStyle(null);
}
}

View File

@@ -1,10 +1,8 @@
package net.momirealms.craftengine.core.item.modifier;
import net.momirealms.craftengine.core.item.ComponentKeys;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.item.ItemBuildContext;
import net.momirealms.craftengine.core.item.Trim;
import net.momirealms.craftengine.core.util.VersionHelper;
public class TrimModifier<I> implements ItemDataModifier<I> {
private final String material;
@@ -23,21 +21,10 @@ public class TrimModifier<I> implements ItemDataModifier<I> {
@Override
public void apply(Item<I> item, ItemBuildContext context) {
item.trim(new Trim(this.material, this.pattern));
if (VersionHelper.isVersionNewerThan1_20_5()) {
} else {
}
}
@Override
public void remove(Item<I> item) {
item.trim(null);
if (VersionHelper.isVersionNewerThan1_20_5()) {
item.removeComponent(ComponentKeys.TRIM);
} else {
item.removeTag("Trim");
}
}
}

View File

@@ -21,6 +21,8 @@ import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser;
import net.momirealms.craftengine.core.plugin.config.StringKeyConstructor;
import net.momirealms.craftengine.core.plugin.locale.I18NData;
import net.momirealms.craftengine.core.plugin.locale.LocalizedException;
import net.momirealms.craftengine.core.plugin.locale.TranslationManager;
import net.momirealms.craftengine.core.sound.AbstractSoundManager;
import net.momirealms.craftengine.core.sound.SoundEvent;
import net.momirealms.craftengine.core.util.*;
@@ -151,7 +153,13 @@ public abstract class AbstractPackManager implements PackManager {
if (list == null || list.isEmpty()) {
this.resourcePackHost = NoneHost.INSTANCE;
} else {
this.resourcePackHost = ResourcePackHosts.fromMap(MiscUtils.castToMap(list.get(0), false));
try {
// we might add multiple host methods in future versions
this.resourcePackHost = ResourcePackHosts.fromMap(MiscUtils.castToMap(list.get(0), false));
} catch (LocalizedException e) {
TranslationManager.instance().log(e.node(), e.arguments());
this.resourcePackHost = NoneHost.INSTANCE;
}
}
}

View File

@@ -14,4 +14,5 @@ public class LoadingSequence {
public static final int JUKEBOX_SONG = 100;
public static final int VANILLA_LOOTS = 110;
public static final int EMOJI = 120;
public static final int ADVANCEMENT = 130;
}

View File

@@ -29,6 +29,9 @@ public class ExactPathMatcher implements PathMatcher {
@Override
public PathMatcher create(Map<String, Object> arguments) {
String path = (String) arguments.get("path");
if (path == null) {
throw new IllegalArgumentException("The 'path' argument must not be null");
}
return new ExactPathMatcher(path);
}
}

View File

@@ -30,7 +30,7 @@ public class FilenameMatcher implements PathMatcher {
public PathMatcher create(Map<String, Object> arguments) {
String name = (String) arguments.get("name");
if (name == null) {
throw new IllegalArgumentException("The name argument must not be null");
throw new IllegalArgumentException("The 'name' argument must not be null");
}
return new FilenameMatcher(name);
}

View File

@@ -1,6 +1,7 @@
package net.momirealms.craftengine.core.pack.host;
import net.momirealms.craftengine.core.pack.host.impl.*;
import net.momirealms.craftengine.core.plugin.locale.LocalizedException;
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
import net.momirealms.craftengine.core.registry.Holder;
import net.momirealms.craftengine.core.registry.Registries;
@@ -19,6 +20,7 @@ public class ResourcePackHosts {
public static final Key ALIST = Key.of("craftengine:alist");
public static final Key DROPBOX = Key.of("craftengine:dropbox");
public static final Key ONEDRIVE = Key.of("craftengine:onedrive");
public static final Key GITLAB = Key.of("craftengine:gitlab");
static {
register(NONE, NoneHost.FACTORY);
@@ -29,6 +31,7 @@ public class ResourcePackHosts {
register(ALIST, AlistHost.FACTORY);
register(DROPBOX, DropboxHost.FACTORY);
register(ONEDRIVE, OneDriveHost.FACTORY);
register(GITLAB, GitLabHost.FACTORY);
}
public static void register(Key key, ResourcePackHostFactory factory) {
@@ -40,12 +43,12 @@ public class ResourcePackHosts {
public static ResourcePackHost fromMap(Map<String, Object> map) {
String type = (String) map.get("type");
if (type == null) {
throw new NullPointerException("host type cannot be null");
throw new LocalizedException("warning.config.host.external.lack_url");
}
Key key = Key.withDefaultNamespace(type, "craftengine");
ResourcePackHostFactory factory = BuiltInRegistries.RESOURCE_PACK_HOST_FACTORY.getValue(key);
if (factory == null) {
throw new IllegalArgumentException("Unknown resource pack host type: " + type);
throw new LocalizedException("warning.config.host.invalid_type", type);
}
return factory.create(map);
}

View File

@@ -8,6 +8,7 @@ import net.momirealms.craftengine.core.pack.host.ResourcePackHost;
import net.momirealms.craftengine.core.pack.host.ResourcePackHostFactory;
import net.momirealms.craftengine.core.pack.host.ResourcePackHosts;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.locale.LocalizedException;
import net.momirealms.craftengine.core.util.*;
import javax.annotation.Nullable;
@@ -293,22 +294,22 @@ public class AlistHost implements ResourcePackHost {
boolean useEnv = (boolean) arguments.getOrDefault("use-environment-variables", false);
String apiUrl = (String) arguments.get("api-url");
if (apiUrl == null || apiUrl.isEmpty()) {
throw new IllegalArgumentException("'api-url' cannot be empty for Alist host");
throw new LocalizedException("warning.config.host.alist.lack_api_url");
}
String userName = useEnv ? System.getenv("CE_ALIST_USERNAME") : (String) arguments.get("username");
if (userName == null || userName.isEmpty()) {
throw new IllegalArgumentException("'username' cannot be empty for Alist host");
throw new LocalizedException("warning.config.host.alist.lack_username");
}
String password = useEnv ? System.getenv("CE_ALIST_PASSWORD") : (String) arguments.get("password");
if (password == null || password.isEmpty()) {
throw new IllegalArgumentException("'password' cannot be empty for Alist host");
throw new LocalizedException("warning.config.host.alist.lack_password");
}
String filePassword = useEnv ? System.getenv("CE_ALIST_FILE_PASSWORD") : (String) arguments.getOrDefault("file-password", "");
String otpCode = (String) arguments.get("otp-code");
Duration jwtTokenExpiration = Duration.ofHours((int) arguments.getOrDefault("jwt-token-expiration", 48));
String uploadPath = (String) arguments.get("upload-path");
if (uploadPath == null || uploadPath.isEmpty()) {
throw new IllegalArgumentException("'upload-path' cannot be empty for Alist host");
throw new LocalizedException("warning.config.host.alist.lack_upload_path");
}
boolean disableUpload = (boolean) arguments.getOrDefault("disable-upload", false);
ProxySelector proxy = MiscUtils.getProxySelector(arguments.get("proxy"));

View File

@@ -7,6 +7,7 @@ import net.momirealms.craftengine.core.pack.host.ResourcePackHost;
import net.momirealms.craftengine.core.pack.host.ResourcePackHostFactory;
import net.momirealms.craftengine.core.pack.host.ResourcePackHosts;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.locale.LocalizedException;
import net.momirealms.craftengine.core.util.GsonHelper;
import net.momirealms.craftengine.core.util.HashUtils;
import net.momirealms.craftengine.core.util.Key;
@@ -262,23 +263,26 @@ public class DropboxHost implements ResourcePackHost {
}
public static class Factory implements ResourcePackHostFactory {
@Override
public ResourcePackHost create(Map<String, Object> arguments) {
boolean useEnv = (boolean) arguments.getOrDefault("use-environment-variables", false);
String appKey = useEnv ? System.getenv("CE_DROPBOX_APP_KEY") : (String) arguments.get("app-key");
if (appKey == null || appKey.isEmpty()) {
throw new IllegalArgumentException("Missing required 'app-key' configuration");
throw new LocalizedException("warning.config.host.dropbox.lack_app_key");
}
String appSecret = useEnv ? System.getenv("CE_DROPBOX_APP_SECRET") : (String) arguments.get("app-secret");
if (appSecret == null || appSecret.isEmpty()) {
throw new IllegalArgumentException("Missing required 'app-secret' configuration");
throw new LocalizedException("warning.config.host.dropbox.lack_app_secret");
}
String refreshToken = useEnv ? System.getenv("CE_DROPBOX_REFRESH_TOKEN") : (String) arguments.get("refresh-token");
if (refreshToken == null || refreshToken.isEmpty()) {
throw new IllegalArgumentException("Missing required 'refresh-token' configuration");
throw new LocalizedException("warning.config.host.dropbox.lack_refresh_token");
}
String uploadPath = (String) arguments.get("upload-path");
if (uploadPath == null || uploadPath.isEmpty()) {
throw new LocalizedException("warning.config.host.dropbox.lack_upload_path");
}
String uploadPath = (String) arguments.getOrDefault("upload-path", "resource_pack.zip");
ProxySelector proxy = MiscUtils.getProxySelector(arguments.get("proxy"));
return new DropboxHost(appKey, appSecret, refreshToken, "/" + uploadPath, proxy);
}

View File

@@ -4,6 +4,7 @@ import net.momirealms.craftengine.core.pack.host.ResourcePackDownloadData;
import net.momirealms.craftengine.core.pack.host.ResourcePackHost;
import net.momirealms.craftengine.core.pack.host.ResourcePackHostFactory;
import net.momirealms.craftengine.core.pack.host.ResourcePackHosts;
import net.momirealms.craftengine.core.plugin.locale.LocalizedException;
import net.momirealms.craftengine.core.util.Key;
import java.nio.file.Path;
@@ -46,7 +47,7 @@ public class ExternalHost implements ResourcePackHost {
public ResourcePackHost create(Map<String, Object> arguments) {
String url = (String) arguments.get("url");
if (url == null || url.isEmpty()) {
throw new IllegalArgumentException("'url' cannot be empty for external host");
throw new LocalizedException("warning.config.host.external.lack_url");
}
String uuid = (String) arguments.get("uuid");
if (uuid == null || uuid.isEmpty()) {

View File

@@ -0,0 +1,199 @@
package net.momirealms.craftengine.core.pack.host.impl;
import com.google.gson.reflect.TypeToken;
import net.momirealms.craftengine.core.pack.host.ResourcePackDownloadData;
import net.momirealms.craftengine.core.pack.host.ResourcePackHost;
import net.momirealms.craftengine.core.pack.host.ResourcePackHostFactory;
import net.momirealms.craftengine.core.pack.host.ResourcePackHosts;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.util.GsonHelper;
import net.momirealms.craftengine.core.util.HashUtils;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ProxySelector;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.*;
import java.util.concurrent.CompletableFuture;
public class GitLabHost implements ResourcePackHost {
public static final Factory FACTORY = new Factory();
private final String gitlabUrl;
private final String accessToken;
private final String projectId;
private final ProxySelector proxy;
private String url;
private String sha1;
private UUID uuid;
public GitLabHost(String gitlabUrl, String accessToken, String projectId, ProxySelector proxy) {
this.gitlabUrl = gitlabUrl;
this.accessToken = accessToken;
this.projectId = projectId;
this.proxy = proxy;
this.readCacheFromDisk();
}
public void readCacheFromDisk() {
Path cachePath = CraftEngine.instance().dataFolderPath().resolve("gitlab.cache");
if (!Files.exists(cachePath)) return;
try (InputStream is = Files.newInputStream(cachePath)) {
Map<String, String> cache = GsonHelper.get().fromJson(
new InputStreamReader(is),
new TypeToken<Map<String, String>>(){}.getType()
);
this.url = cache.get("url");
this.sha1 = cache.get("sha1");
String uuidString = cache.get("uuid");
if (uuidString != null && !uuidString.isEmpty()) {
this.uuid = UUID.fromString(uuidString);
}
CraftEngine.instance().logger().info("[GitLab] Loaded cached resource pack info");
} catch (Exception e) {
CraftEngine.instance().logger().warn(
"[GitLab] Failed to read cache file: " + e.getMessage());
}
}
public void saveCacheToDisk() {
Map<String, String> cache = new HashMap<>();
cache.put("url", this.url);
cache.put("sha1", this.sha1);
cache.put("uuid", this.uuid != null ? this.uuid.toString() : "");
Path cachePath = CraftEngine.instance().dataFolderPath().resolve("gitlab.cache");
try {
Files.writeString(
cachePath,
GsonHelper.get().toJson(cache),
StandardOpenOption.CREATE,
StandardOpenOption.TRUNCATE_EXISTING
);
} catch (IOException e) {
CraftEngine.instance().logger().warn(
"[GitLab] Failed to save cache: " + e.getMessage());
}
}
@Override
public boolean canUpload() {
return true;
}
@Override
public Key type() {
return ResourcePackHosts.GITLAB;
}
@Override
public CompletableFuture<List<ResourcePackDownloadData>> requestResourcePackDownloadLink(UUID player) {
if (url == null) return CompletableFuture.completedFuture(Collections.emptyList());
return CompletableFuture.completedFuture(List.of(ResourcePackDownloadData.of(this.url, this.uuid, this.sha1)));
}
@Override
public CompletableFuture<Void> upload(Path resourcePackPath) {
CompletableFuture<Void> future = new CompletableFuture<>();
CraftEngine.instance().scheduler().executeAsync(() -> {
this.sha1 = HashUtils.calculateLocalFileSha1(resourcePackPath);
this.uuid = UUID.nameUUIDFromBytes(this.sha1.getBytes(StandardCharsets.UTF_8));
try (HttpClient client = HttpClient.newBuilder().proxy(this.proxy).build()) {
String boundary = UUID.randomUUID().toString();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(this.gitlabUrl + "/api/v4/projects/" + this.projectId + "/uploads"))
.header("PRIVATE-TOKEN", this.accessToken)
.header("Content-Type", "multipart/form-data; boundary=" + boundary)
.POST(buildMultipartBody(resourcePackPath, boundary))
.build();
long uploadStart = System.currentTimeMillis();
CraftEngine.instance().logger().info(
"[GitLab] Initiating resource pack upload...");
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenAccept(response -> {
long uploadTime = System.currentTimeMillis() - uploadStart;
CraftEngine.instance().logger().info(
"[GitLab] Upload request completed in " + uploadTime + "ms");
if (response.statusCode() == 200 || response.statusCode() == 201) {
Map<String, Object> json = GsonHelper.parseJsonToMap(response.body());
if (json.containsKey("full_path")) {
this.url = this.gitlabUrl + json.get("full_path");
future.complete(null);
saveCacheToDisk();
return;
}
}
CraftEngine.instance().logger().warn("[GitLab] Upload failed: " + response.body());
future.completeExceptionally(new RuntimeException("Upload failed: " + response.body()));
})
.exceptionally(ex -> {
CraftEngine.instance().logger().warn(
"[GitLab] Upload error: " + ex.getMessage());
future.completeExceptionally(ex);
return null;
});
} catch (IOException e) {
CraftEngine.instance().logger().warn(
"[GitLab] Failed to upload resource pack: " + e.getMessage());
}
});
return future;
}
private HttpRequest.BodyPublisher buildMultipartBody(Path filePath, String boundary) throws IOException {
List<byte[]> parts = new ArrayList<>();
String filePartHeader = "--" + boundary + "\r\n" +
"Content-Disposition: form-data; name=\"file\"; filename=\"" + filePath.getFileName() + "\"\r\n" +
"Content-Type: application/octet-stream\r\n\r\n";
parts.add(filePartHeader.getBytes());
parts.add(Files.readAllBytes(filePath));
parts.add("\r\n".getBytes());
String endBoundary = "--" + boundary + "--\r\n";
parts.add(endBoundary.getBytes());
return HttpRequest.BodyPublishers.ofByteArrays(parts);
}
public static class Factory implements ResourcePackHostFactory {
@Override
public ResourcePackHost create(Map<String, Object> arguments) {
boolean useEnv = (boolean) arguments.getOrDefault("use-environment-variables", false);
String gitlabUrl = (String) arguments.get("gitlab-url");
if (gitlabUrl == null || gitlabUrl.isEmpty()) {
throw new IllegalArgumentException("'gitlab-url' cannot be empty for GitLab host");
}
if (gitlabUrl.endsWith("/")) {
gitlabUrl = gitlabUrl.substring(0, gitlabUrl.length() - 1);
}
String accessToken = useEnv ? System.getenv("CE_GITLAB_ACCESS_TOKEN") : (String) arguments.get("access-token");
if (accessToken == null || accessToken.isEmpty()) {
throw new IllegalArgumentException("Missing required 'access-token' configuration");
}
String projectId = (String) arguments.get("project-id");
if (projectId == null || projectId.isEmpty()) {
throw new IllegalArgumentException("Missing required 'project-id' configuration");
}
projectId = URLEncoder.encode(projectId, StandardCharsets.UTF_8).replace("/", "%2F");
ProxySelector proxy = MiscUtils.getProxySelector(arguments.get("proxy"));
return new GitLabHost(gitlabUrl, accessToken, projectId, proxy);
}
}
}

View File

@@ -6,6 +6,7 @@ import net.momirealms.craftengine.core.pack.host.ResourcePackHost;
import net.momirealms.craftengine.core.pack.host.ResourcePackHostFactory;
import net.momirealms.craftengine.core.pack.host.ResourcePackHosts;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.locale.LocalizedException;
import net.momirealms.craftengine.core.util.GsonHelper;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
@@ -273,7 +274,7 @@ public class LobFileHost implements ResourcePackHost {
boolean useEnv = (boolean) arguments.getOrDefault("use-environment-variables", false);
String apiKey = useEnv ? System.getenv("CE_LOBFILE_API_KEY") : (String) arguments.get("api-key");
if (apiKey == null || apiKey.isEmpty()) {
throw new RuntimeException("Missing 'api-key' for LobFileHost");
throw new LocalizedException("warning.config.host.lobfile.lack_api_key");
}
ProxySelector proxy = MiscUtils.getProxySelector(arguments.get("proxy"));
return new LobFileHost(apiKey, proxy);

View File

@@ -7,6 +7,7 @@ import net.momirealms.craftengine.core.pack.host.ResourcePackHost;
import net.momirealms.craftengine.core.pack.host.ResourcePackHostFactory;
import net.momirealms.craftengine.core.pack.host.ResourcePackHosts;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.locale.LocalizedException;
import net.momirealms.craftengine.core.util.*;
import java.io.FileNotFoundException;
@@ -233,19 +234,19 @@ public class OneDriveHost implements ResourcePackHost {
boolean useEnv = (boolean) arguments.getOrDefault("use-environment-variables", false);
String clientId = useEnv ? System.getenv("CE_ONEDRIVE_CLIENT_ID") : (String) arguments.get("client-id");
if (clientId == null || clientId.isEmpty()) {
throw new IllegalArgumentException("Missing required 'client-id' configuration");
throw new LocalizedException("warning.config.host.onedrive.lack_client_id");
}
String clientSecret = useEnv ? System.getenv("CE_ONEDRIVE_CLIENT_SECRET") : (String) arguments.get("client-secret");
if (clientSecret == null || clientSecret.isEmpty()) {
throw new IllegalArgumentException("Missing required 'client-secret' configuration");
throw new LocalizedException("warning.config.host.onedrive.lack_client_secret");
}
String refreshToken = useEnv ? System.getenv("CE_ONEDRIVE_REFRESH_TOKEN") : (String) arguments.get("refresh-token");
if (refreshToken == null || refreshToken.isEmpty()) {
throw new IllegalArgumentException("Missing required 'refresh-token' configuration");
throw new LocalizedException("warning.config.host.onedrive.lack_refresh_token");
}
String uploadPath = (String) arguments.getOrDefault("upload-path", "resource_pack.zip");
if (uploadPath == null || uploadPath.isEmpty()) {
throw new IllegalArgumentException("Invalid 'upload-path' configuration");
throw new LocalizedException("warning.config.host.onedrive.lack_upload_path");
}
ProxySelector proxy = MiscUtils.getProxySelector(arguments.get("proxy"));
return new OneDriveHost(clientId, clientSecret, refreshToken, uploadPath, proxy);

View File

@@ -5,6 +5,7 @@ import net.momirealms.craftengine.core.pack.host.ResourcePackHost;
import net.momirealms.craftengine.core.pack.host.ResourcePackHostFactory;
import net.momirealms.craftengine.core.pack.host.ResourcePackHosts;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.locale.LocalizedException;
import net.momirealms.craftengine.core.util.HashUtils;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
@@ -159,26 +160,26 @@ public class S3Host implements ResourcePackHost {
boolean useEnv = (boolean) arguments.getOrDefault("use-environment-variables", false);
String endpoint = (String) arguments.get("endpoint");
if (endpoint == null || endpoint.isEmpty()) {
throw new IllegalArgumentException("'endpoint' cannot be empty for S3 host");
throw new LocalizedException("warning.config.host.s3.lack_endpoint");
}
String protocol = (String) arguments.getOrDefault("protocol", "https");
boolean usePathStyle = (boolean) arguments.getOrDefault("path-style", false);
String bucket = (String) arguments.get("bucket");
if (bucket == null || bucket.isEmpty()) {
throw new IllegalArgumentException("'bucket' cannot be empty for S3 host");
throw new LocalizedException("warning.config.host.s3.lack_bucket");
}
String region = (String) arguments.getOrDefault("region", "auto");
String accessKeyId = useEnv ? System.getenv("CE_S3_ACCESS_KEY_ID") : (String) arguments.get("access-key-id");
if (accessKeyId == null || accessKeyId.isEmpty()) {
throw new IllegalArgumentException("'access-key-id' cannot be empty for S3 host");
throw new LocalizedException("warning.config.host.s3.lack_access_key_id");
}
String accessKeySecret = useEnv ? System.getenv("CE_S3_ACCESS_KEY_SECRET") : (String) arguments.get("access-key-secret");
if (accessKeySecret == null || accessKeySecret.isEmpty()) {
throw new IllegalArgumentException("'access-key-secret' cannot be empty for S3 host");
throw new LocalizedException("warning.config.host.s3.lack_access_key_secret");
}
String uploadPath = (String) arguments.getOrDefault("upload-path", "craftengine/resource_pack.zip");
if (uploadPath == null || uploadPath.isEmpty()) {
throw new IllegalArgumentException("'upload-path' cannot be empty for S3 host");
throw new LocalizedException("warning.config.host.s3.lack_upload_path");
}
boolean useLegacySignature = (boolean) arguments.getOrDefault("use-legacy-signature", true);
Duration validity = Duration.ofSeconds((int) arguments.getOrDefault("validity", 10));

View File

@@ -6,6 +6,7 @@ import net.momirealms.craftengine.core.pack.host.ResourcePackHostFactory;
import net.momirealms.craftengine.core.pack.host.ResourcePackHosts;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.locale.LocalizedException;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
@@ -61,11 +62,11 @@ public class SelfHost implements ResourcePackHost {
SelfHostHttpServer selfHostHttpServer = SelfHostHttpServer.instance();
String ip = (String) arguments.get("ip");
if (ip == null) {
throw new IllegalArgumentException("'ip' argument missing for self host");
throw new LocalizedException("warning.config.host.self.lack_ip");
}
int port = (int) arguments.get("port");
int port = MiscUtils.getAsInt(arguments.getOrDefault("port", 8163));
if (port < 0 || port > 65535) {
throw new IllegalArgumentException("Illegal port: '" + port + "' for self host");
throw new LocalizedException("warning.config.host.self.invalid_port", String.valueOf(port));
}
boolean oneTimeToken = (boolean) arguments.getOrDefault("one-time-token", true);
String protocol = (String) arguments.getOrDefault("protocol", "http");
@@ -74,8 +75,8 @@ public class SelfHost implements ResourcePackHost {
int maxRequests = 5;
int resetInterval = 20_000;
if (rateMap != null) {
maxRequests = (int) rateMap.getOrDefault("max-requests", 5);
resetInterval = (int) rateMap.getOrDefault("reset-interval", 20) * 1000;
maxRequests = MiscUtils.getAsInt(rateMap.getOrDefault("max-requests", 5));
resetInterval = MiscUtils.getAsInt(rateMap.getOrDefault("reset-interval", 20)) * 1000;
}
selfHostHttpServer.updateProperties(ip, port, denyNonMinecraftRequest, protocol, maxRequests, resetInterval, oneTimeToken);
return INSTANCE;

View File

@@ -1,5 +1,6 @@
package net.momirealms.craftengine.core.plugin;
import net.momirealms.craftengine.core.advancement.AdvancementManager;
import net.momirealms.craftengine.core.block.BlockManager;
import net.momirealms.craftengine.core.entity.furniture.FurnitureManager;
import net.momirealms.craftengine.core.font.FontManager;
@@ -66,6 +67,7 @@ public abstract class CraftEngine implements Plugin {
protected GuiManager guiManager;
protected SoundManager soundManager;
protected VanillaLootManager vanillaLootManager;
protected AdvancementManager advancementManager;
private final Consumer<CraftEngine> reloadEventDispatcher;
private boolean isReloading;
@@ -142,6 +144,7 @@ public abstract class CraftEngine implements Plugin {
this.vanillaLootManager.reload();
this.guiManager.reload();
this.packManager.reload();
this.advancementManager.reload();
if (reloadRecipe) {
this.recipeManager.reload();
}
@@ -161,6 +164,7 @@ public abstract class CraftEngine implements Plugin {
this.itemBrowserManager.delayedLoad();
// collect illegal characters from minecraft:default font
this.fontManager.delayedLoad();
this.advancementManager.delayedLoad();
if (reloadRecipe) {
// convert data pack recipes
this.recipeManager.delayedLoad();
@@ -210,6 +214,7 @@ public abstract class CraftEngine implements Plugin {
this.packManager.delayedInit();
this.fontManager.delayedInit();
this.vanillaLootManager.delayedInit();
this.advancementManager.delayedInit();
// reload the plugin
try {
this.reloadPlugin(Runnable::run, Runnable::run, true);
@@ -228,6 +233,7 @@ public abstract class CraftEngine implements Plugin {
public void onPluginDisable() {
if (this.networkManager != null) this.networkManager.disable();
if (this.fontManager != null) this.fontManager.disable();
if (this.advancementManager != null) this.advancementManager.disable();
if (this.packManager != null) this.packManager.disable();
if (this.itemManager != null) this.itemManager.disable();
if (this.blockManager != null) this.blockManager.disable();
@@ -268,6 +274,8 @@ public abstract class CraftEngine implements Plugin {
this.packManager.registerConfigSectionParsers(this.soundManager.parsers());
// register vanilla loot parser
this.packManager.registerConfigSectionParser(this.vanillaLootManager.parser());
// register advancement parser
this.packManager.registerConfigSectionParser(this.advancementManager.parser());
}
protected abstract void platformDelayedEnable();
@@ -391,6 +399,11 @@ public abstract class CraftEngine implements Plugin {
return fontManager;
}
@Override
public AdvancementManager advancementManager() {
return advancementManager;
}
@Override
public TranslationManager translationManager() {
return translationManager;

View File

@@ -1,5 +1,6 @@
package net.momirealms.craftengine.core.plugin;
import net.momirealms.craftengine.core.advancement.AdvancementManager;
import net.momirealms.craftengine.core.block.BlockManager;
import net.momirealms.craftengine.core.entity.furniture.FurnitureManager;
import net.momirealms.craftengine.core.entity.player.Player;
@@ -61,6 +62,8 @@ public interface Plugin {
FontManager fontManager();
AdvancementManager advancementManager();
Config config();
TranslationManager translationManager();

View File

@@ -12,6 +12,7 @@ import dev.dejvokep.boostedyaml.settings.loader.LoaderSettings;
import dev.dejvokep.boostedyaml.settings.updater.UpdaterSettings;
import dev.dejvokep.boostedyaml.utils.format.NodeRole;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.core.entity.furniture.ColliderType;
import net.momirealms.craftengine.core.pack.conflict.resolution.ConditionalResolution;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.PluginProperties;
@@ -32,6 +33,7 @@ import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@@ -100,6 +102,7 @@ public class Config {
protected boolean furniture$handle_invalid_furniture_on_chunk_load$enable;
protected Map<String, String> furniture$handle_invalid_furniture_on_chunk_load$mapping;
protected boolean furniture$hide_base_entity;
protected ColliderType furniture$collision_entity_type;
protected boolean block$sound_system$enable;
protected boolean block$simplify_adventure_break_check;
@@ -133,6 +136,11 @@ public class Config {
protected boolean image$intercept_packets$player_info;
protected boolean image$intercept_packets$set_score;
protected boolean emoji$chat;
protected boolean emoji$book;
protected boolean emoji$anvil;
protected boolean emoji$sign;
public Config(CraftEngine plugin) {
this.plugin = plugin;
this.configVersion = PluginProperties.getValue("config");
@@ -272,9 +280,10 @@ public class Config {
}
}
}
this.furniture$handle_invalid_furniture_on_chunk_load$mapping = builder.build();
furniture$handle_invalid_furniture_on_chunk_load$mapping = builder.build();
furniture$hide_base_entity = config.getBoolean("furniture.hide-base-entity", true);
furniture$collision_entity_type = ColliderType.valueOf(config.getString("furniture.collision-entity-type", "interaction").toUpperCase(Locale.ENGLISH));
// block
block$sound_system$enable = config.getBoolean("block.sound-system.enable", true);
@@ -309,6 +318,12 @@ public class Config {
image$intercept_packets$player_info = config.getBoolean("image.intercept-packets.player-info", true);
image$intercept_packets$set_score = config.getBoolean("image.intercept-packets.set-score", true);
// emoji
emoji$chat = config.getBoolean("emoji.chat", true);
emoji$anvil = config.getBoolean("emoji.anvil", true);
emoji$book = config.getBoolean("emoji.book", true);
emoji$sign = config.getBoolean("emoji.sign", true);
Class<?> modClazz = ReflectionUtils.getClazz(CraftEngine.MOD_CLASS);
if (modClazz != null) {
Method setMaxChainMethod = ReflectionUtils.getStaticMethod(modClazz, void.class, new String[] {"setMaxChainUpdate"}, int.class);
@@ -648,6 +663,26 @@ public class Config {
return instance.block$extended_interaction_range;
}
public static boolean allowEmojiSign() {
return instance.emoji$sign;
}
public static boolean allowEmojiChat() {
return instance.emoji$chat;
}
public static boolean allowEmojiAnvil() {
return instance.emoji$anvil;
}
public static boolean allowEmojiBook() {
return instance.emoji$book;
}
public static ColliderType colliderType() {
return instance.furniture$collision_entity_type;
}
public YamlDocument loadOrCreateYamlData(String fileName) {
File file = new File(this.plugin.dataFolderFile(), fileName);
if (!file.exists()) {

View File

@@ -0,0 +1,15 @@
package net.momirealms.craftengine.core.plugin.event;
import net.momirealms.craftengine.core.util.Key;
public class Trigger {
private final Key id;
public Trigger(Key id) {
this.id = id;
}
public Key id() {
return id;
}
}

View File

@@ -0,0 +1,21 @@
package net.momirealms.craftengine.core.plugin.event;
import net.momirealms.craftengine.core.util.Key;
import java.util.HashMap;
import java.util.Map;
public class Triggers {
public static final Map<Key, Trigger> TRIGGERS = new HashMap<>();
public static final Trigger USE_ITEM = create(Key.of("use_item"));
public static final Trigger INTERACT = create(Key.of("interact"));
public static final Trigger CONSUME = create(Key.of("consume"));
public static final Trigger BREAK = create(Key.of("break"));
private static Trigger create(Key id) {
Trigger trigger = new Trigger(id);
TRIGGERS.put(id, trigger);
return trigger;
}
}

View File

@@ -0,0 +1,20 @@
package net.momirealms.craftengine.core.plugin.locale;
public class LocalizedException extends RuntimeException {
private final String node;
private final String[] arguments;
public LocalizedException(String node, String... arguments) {
super(node);
this.node = node;
this.arguments = arguments;
}
public String[] arguments() {
return arguments;
}
public String node() {
return node;
}
}

View File

@@ -0,0 +1,63 @@
package net.momirealms.craftengine.core.plugin.script;
public abstract class AbstractTokenStringReader implements TokenStringReader {
protected final char[] chars;
protected int index;
public AbstractTokenStringReader(char[] chars) {
this.chars = chars;
}
@Override
public boolean hasNext() {
return this.index < this.chars.length;
}
@Override
public int index() {
return this.index;
}
@Override
public String nextToken() {
if (!hasNext()) {
throw new IndexOutOfBoundsException();
}
int start = this.index;
int end = this.chars.length - 1;
while (this.index < this.chars.length && !Character.isWhitespace(this.chars[this.index])) {
end++;
}
String token = new String(this.chars, start, end - start);
this.index = end;
return token;
}
@Override
public char peek() {
return this.chars[this.index];
}
@Override
public char peek(int n) {
return this.chars[this.index + n];
}
@Override
public void skip(int n) {
if (n < 0) {
throw new IllegalArgumentException("n < 0");
}
if (index() + n >= this.chars.length) {
throw new IndexOutOfBoundsException("index(" + index() + ") + (" + n + ") > chars.length(" + this.chars.length + ")");
}
this.index += n;
}
@Override
public void skipWhitespace() {
while (this.index < this.chars.length && !Character.isWhitespace(this.chars[this.index])) {
this.index++;
}
}
}

View File

@@ -0,0 +1,4 @@
package net.momirealms.craftengine.core.plugin.script;
public interface Action<T> {
}

View File

@@ -0,0 +1,72 @@
package net.momirealms.craftengine.core.plugin.script;
import net.momirealms.craftengine.core.util.Key;
public interface Block {
int size();
Action<?>[] actions();
Key id();
Action<?> byIndex(int index);
int indexOf(Action<?> action);
boolean contains(Action<?> action);
static Block create(Key id, Action<?>... actions) {
return new BlockImpl(id, actions);
}
class BlockImpl implements Block {
private final Key id;
private final Action<?>[] actions;
public BlockImpl(Key id, Action<?>[] actions) {
this.actions = actions;
this.id = id;
}
@Override
public int size() {
return actions.length;
}
@Override
public Action<?>[] actions() {
return this.actions;
}
@Override
public Key id() {
return this.id;
}
@Override
public boolean contains(Action<?> action) {
for (Action<?> value : this.actions) {
if (value.equals(action)) {
return true;
}
}
return false;
}
@Override
public Action<?> byIndex(int index) {
return this.actions[index];
}
@Override
public int indexOf(final Action<?> action) {
for (int i = 0; i < this.actions.length; i++) {
if (this.actions[i].equals(action)) {
return i;
}
}
return -1;
}
}
}

View File

@@ -0,0 +1,61 @@
package net.momirealms.craftengine.core.plugin.script;
import net.momirealms.craftengine.core.util.Key;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
public interface Task {
Map<Key, Block> blocks();
Key id();
@Nullable
Block byId(Key id);
@Nullable
Block byAction(Action<?> action);
static Task create(Key key, Map<Key, Block> blocks) {
return new TaskImpl(key, blocks);
}
class TaskImpl implements Task {
private final Key id;
private final Map<Key, Block> blocks = new LinkedHashMap<>();
public TaskImpl(Key id, Map<Key, Block> blocks) {
this.blocks.putAll(blocks);
this.id = id;
}
@Nullable
@Override
public Block byId(Key id) {
return this.blocks.get(id);
}
@Override
public Map<Key, Block> blocks() {
return Collections.unmodifiableMap(this.blocks);
}
@Override
public Key id() {
return this.id;
}
@Override
public @Nullable Block byAction(Action<?> action) {
for (Block block : blocks.values()) {
if (block.contains(action)) {
return block;
}
}
return null;
}
}
}

View File

@@ -0,0 +1,6 @@
package net.momirealms.craftengine.core.plugin.script;
public interface TaskActionParser {
<T> Action<T> parse(TokenStringReader reader);
}

View File

@@ -0,0 +1,4 @@
package net.momirealms.craftengine.core.plugin.script;
public class TaskActionParsers {
}

View File

@@ -0,0 +1,8 @@
package net.momirealms.craftengine.core.plugin.script;
public interface TaskContext {
Task task();
}

View File

@@ -0,0 +1,18 @@
package net.momirealms.craftengine.core.plugin.script;
public interface TokenStringReader {
char peek();
char peek(int n);
void skip(int n);
int index();
boolean hasNext();
String nextToken();
void skipWhitespace();
}

View File

@@ -0,0 +1,8 @@
package net.momirealms.craftengine.core.plugin.script.argument;
import net.momirealms.craftengine.core.plugin.script.TokenStringReader;
public interface ArgumentParser<T> {
T parse(TokenStringReader reader);
}

View File

@@ -0,0 +1,19 @@
package net.momirealms.craftengine.core.plugin.script.argument;
public class ArgumentParsers {
public static final ArgumentParser<Integer> INT_PARSER = new IntArgumentParser();
public static final ArgumentParser<Long> LONG_PARSER = new LongArgumentParser();
public static final ArgumentParser<Double> DOUBLE_PARSER = new DoubleArgumentParser();
public static ArgumentParser<Integer> intParser() {
return INT_PARSER;
}
public static ArgumentParser<Long> longParser() {
return LONG_PARSER;
}
public static ArgumentParser<Double> doubleParser() {
return DOUBLE_PARSER;
}
}

View File

@@ -0,0 +1,12 @@
package net.momirealms.craftengine.core.plugin.script.argument;
import net.momirealms.craftengine.core.plugin.script.TokenStringReader;
public class DoubleArgumentParser implements ArgumentParser<Double> {
@Override
public Double parse(TokenStringReader reader) {
String token = reader.nextToken();
return Double.parseDouble(token);
}
}

View File

@@ -0,0 +1,12 @@
package net.momirealms.craftengine.core.plugin.script.argument;
import net.momirealms.craftengine.core.plugin.script.TokenStringReader;
public class IntArgumentParser implements ArgumentParser<Integer> {
@Override
public Integer parse(TokenStringReader reader) {
String token = reader.nextToken();
return Integer.parseInt(token);
}
}

View File

@@ -0,0 +1,12 @@
package net.momirealms.craftengine.core.plugin.script.argument;
import net.momirealms.craftengine.core.plugin.script.TokenStringReader;
public class LongArgumentParser implements ArgumentParser<Long> {
@Override
public Long parse(TokenStringReader reader) {
String token = reader.nextToken();
return Long.parseLong(token);
}
}

View File

@@ -22,6 +22,7 @@ import net.momirealms.craftengine.core.pack.model.select.SelectPropertyFactory;
import net.momirealms.craftengine.core.pack.model.special.SpecialModelFactory;
import net.momirealms.craftengine.core.pack.model.tint.TintFactory;
import net.momirealms.craftengine.core.plugin.config.template.TemplateArgumentFactory;
import net.momirealms.craftengine.core.plugin.script.TaskActionParser;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.ResourceKey;
@@ -49,6 +50,7 @@ public class BuiltInRegistries {
public static final Registry<CustomSmithingTransformRecipe.ItemDataProcessor.Factory> SMITHING_RESULT_PROCESSOR_FACTORY = createRegistry(Registries.SMITHING_RESULT_PROCESSOR_FACTORY);
public static final Registry<HitBoxFactory> HITBOX_FACTORY = createRegistry(Registries.HITBOX_FACTORY);
public static final Registry<ResourcePackHostFactory> RESOURCE_PACK_HOST_FACTORY = createRegistry(Registries.RESOURCE_PACK_HOST_FACTORY);
public static final Registry<TaskActionParser> TASK_ACTION_PARSER = createRegistry(Registries.TASK_ACTION_PARSER);
private static <T> Registry<T> createRegistry(ResourceKey<? extends Registry<T>> key) {
return new MappedRegistry<>(key);

View File

@@ -22,6 +22,7 @@ import net.momirealms.craftengine.core.pack.model.select.SelectPropertyFactory;
import net.momirealms.craftengine.core.pack.model.special.SpecialModelFactory;
import net.momirealms.craftengine.core.pack.model.tint.TintFactory;
import net.momirealms.craftengine.core.plugin.config.template.TemplateArgumentFactory;
import net.momirealms.craftengine.core.plugin.script.TaskActionParser;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.ResourceKey;
@@ -50,4 +51,5 @@ public class Registries {
public static final ResourceKey<Registry<CustomSmithingTransformRecipe.ItemDataProcessor.Factory>> SMITHING_RESULT_PROCESSOR_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("smithing_result_processor_factory"));
public static final ResourceKey<Registry<HitBoxFactory>> HITBOX_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("hitbox_factory"));
public static final ResourceKey<Registry<ResourcePackHostFactory>> RESOURCE_PACK_HOST_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("resource_pack_host_factory"));
public static final ResourceKey<Registry<TaskActionParser>> TASK_ACTION_PARSER = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("task_action_parser"));
}

View File

@@ -28,6 +28,10 @@ public record Key(String namespace, String value) {
return of(decompose(namespacedId, "minecraft"));
}
public String[] decompose() {
return new String[] { namespace, value };
}
@Override
public int hashCode() {
return toString().hashCode();

View File

@@ -2,9 +2,9 @@ org.gradle.jvmargs=-Xmx1G
# Project settings
# Rule: [major update].[feature update].[bug fix]
project_version=0.0.49
config_version=29
lang_version=6
project_version=0.0.50
config_version=30
lang_version=7
project_group=net.momirealms
latest_supported_version=1.21.5
latest_minecraft_version=1.21.5
@@ -51,7 +51,7 @@ byte_buddy_version=1.17.5
ahocorasick_version=0.6.3
snake_yaml_version=2.4
anti_grief_version=0.15
nms_helper_version=0.60.11
nms_helper_version=0.61.7
reactive_streams_version=1.0.4
amazon_awssdk_version=2.31.23
amazon_awssdk_eventstream_version=1.0.1

View File

@@ -1,6 +1,5 @@
package net.momirealms.craftengine.mod;
import net.minecraft.server.level.ServerLevel;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.objectweb.asm.tree.ClassNode;