9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-28 03:19:14 +00:00

Merge pull request #341 from Xiao-MoMi/main

更新上游
This commit is contained in:
XiaoMoMi
2025-08-21 21:26:31 +08:00
committed by GitHub
173 changed files with 3127 additions and 934 deletions

View File

@@ -56,7 +56,7 @@ The code you contribute will be open-sourced under the GPLv3 license. If you pre
## Differences Between Versions
| Version | Official Support | Max Players | Dev Builds |
|-------------------|------------------|-------------|------------|
| Community Edition | ❌ No | 20 | ❌ No |
| Community Edition | ❌ No | 30 | ❌ No |
| Premium Edition | ✔️ Yes | Unlimited | ✔️ Yes |
### 💖 Support the Developer

View File

@@ -39,8 +39,6 @@ public class BukkitCompatibilityManager implements CompatibilityManager {
private final Map<String, ModelProvider> modelProviders;
private final Map<String, LevelerProvider> levelerProviders;
private boolean hasPlaceholderAPI;
private boolean hasViaVersion;
private MythicSkillHelper skillExecute;
public BukkitCompatibilityManager(BukkitCraftEngine plugin) {
this.plugin = plugin;
@@ -63,26 +61,12 @@ public class BukkitCompatibilityManager implements CompatibilityManager {
this.hasPlaceholderAPI = true;
logHook("PlaceholderAPI");
}
// skript
if (this.isPluginEnabled("Skript")) {
SkriptHook.register();
logHook("Skript");
Plugin skriptPlugin = getPlugin("Skript");
// This can cause bugs, needs to find a better way
// for (BukkitTask task : Bukkit.getScheduler().getPendingTasks()) {
// if (task.getOwner() == skriptPlugin) {
// task.cancel();
// if (VersionHelper.isFolia()) {
// Bukkit.getGlobalRegionScheduler().run(skriptPlugin, (t) -> {
// FastNMS.INSTANCE.getBukkitTaskRunnable(task).run();
// });
// } else {
// Bukkit.getScheduler().runTask(skriptPlugin, FastNMS.INSTANCE.getBukkitTaskRunnable(task));
// }
// }
// }
}
// WorldEdit
// FastAsyncWorldEdit
if (this.isPluginEnabled("FastAsyncWorldEdit")) {
try {
this.initFastAsyncWorldEditHook();

View File

@@ -9,7 +9,7 @@ import io.lumine.mythic.api.skills.SkillCaster;
import io.lumine.mythic.bukkit.BukkitAdapter;
import io.lumine.mythic.bukkit.adapters.BukkitItemStack;
import io.lumine.mythic.core.drops.droppables.ItemDrop;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.api.BukkitAdaptors;
import net.momirealms.craftengine.core.item.CustomItem;
import net.momirealms.craftengine.core.item.ItemBuildContext;
import net.momirealms.craftengine.core.plugin.CraftEngine;
@@ -38,7 +38,7 @@ public class MythicItemDrop extends ItemDrop implements IItemDrop {
if (caster != null && caster.getEntity() instanceof AbstractPlayer abstractPlayer) {
Entity bukkitEntity = abstractPlayer.getBukkitEntity();
if (bukkitEntity instanceof Player bukkitPlayer) {
var player = BukkitCraftEngine.instance().adapt(bukkitPlayer);
var player = BukkitAdaptors.adapt(bukkitPlayer);
context = ItemBuildContext.of(player);
}
}

View File

@@ -6,6 +6,7 @@ import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.core.util.Key;
import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
public class MythicItemDropListener implements Listener {
@@ -16,7 +17,7 @@ public class MythicItemDropListener implements Listener {
Bukkit.getPluginManager().registerEvents(this, plugin.javaPlugin());
}
@EventHandler
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH)
public void onMythicDropLoad(MythicDropLoadEvent event) {
if (!event.getDropName().equalsIgnoreCase("craftengine")) return;
String argument = event.getArgument();

View File

@@ -1,12 +1,14 @@
package net.momirealms.craftengine.bukkit.compatibility.skript;
import net.momirealms.craftengine.bukkit.compatibility.skript.clazz.CraftEngineClasses;
import net.momirealms.craftengine.bukkit.compatibility.skript.condition.CondIsCraftEngineHasBeenLoad;
import net.momirealms.craftengine.bukkit.compatibility.skript.condition.CondIsCustomBlock;
import net.momirealms.craftengine.bukkit.compatibility.skript.condition.CondIsCustomItem;
import net.momirealms.craftengine.bukkit.compatibility.skript.condition.CondIsFurniture;
import net.momirealms.craftengine.bukkit.compatibility.skript.effect.EffPlaceCustomBlock;
import net.momirealms.craftengine.bukkit.compatibility.skript.effect.EffPlaceFurniture;
import net.momirealms.craftengine.bukkit.compatibility.skript.effect.EffRemoveFurniture;
import net.momirealms.craftengine.bukkit.compatibility.skript.event.EvtCraftEngineReload;
import net.momirealms.craftengine.bukkit.compatibility.skript.event.EvtCustomBlock;
import net.momirealms.craftengine.bukkit.compatibility.skript.event.EvtCustomClick;
import net.momirealms.craftengine.bukkit.compatibility.skript.event.EvtCustomFurniture;
@@ -16,9 +18,11 @@ public class SkriptHook {
public static void register() {
CraftEngineClasses.register();
EvtCraftEngineReload.register();
EvtCustomBlock.register();
EvtCustomFurniture.register();
EvtCustomClick.register();
CondIsCraftEngineHasBeenLoad.register();
CondIsCustomBlock.register();
CondIsFurniture.register();
CondIsCustomItem.register();

View File

@@ -0,0 +1,46 @@
package net.momirealms.craftengine.bukkit.compatibility.skript.condition;
import ch.njol.skript.Skript;
import ch.njol.skript.doc.Description;
import ch.njol.skript.doc.Name;
import ch.njol.skript.doc.Since;
import ch.njol.skript.lang.Condition;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.SkriptParser;
import ch.njol.util.Kleenean;
import net.momirealms.craftengine.bukkit.compatibility.skript.event.EvtCraftEngineReload;
import org.bukkit.event.Event;
import org.jetbrains.annotations.Nullable;
@Name("CraftEngine has been load")
@Description({"Checks CraftEngine has been load."})
@Since("1.0")
public class CondIsCraftEngineHasBeenLoad extends Condition {
public static void register() {
Skript.registerCondition(CondIsCraftEngineHasBeenLoad.class,
"(ce|craft[-]engine) (has been|is) load[ed]",
"(ce|craft[-]engine) (has not been|is not) load[ed] [yet]",
"(ce|craft[-]engine) (hasn't been|isn't) load[ed] [yet]"
);
}
@Override
public boolean init(Expression<?>[] expressions, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult) {
setNegated(matchedPattern >= 1);
return true;
}
@Override
public boolean check(Event event) {
boolean beenLoad = EvtCraftEngineReload.hasBeenLoad();
return isNegated() ? !beenLoad : beenLoad;
}
@Override
public String toString(@Nullable Event event, boolean debug) {
return "craft-engine " + (isNegated() ? "is not" : "is") + " loaded";
}
}

View File

@@ -15,8 +15,8 @@ public class CondIsCustomBlock extends Condition {
public static void register() {
Skript.registerCondition(CondIsCustomBlock.class,
"%blocks% (is|are) custom block(s)",
"%blocks% (is|are)(n't| not) custom block(s)");
"%blocks% (is|are) [a[n]] (custom|ce|craft-engine) block[s]",
"%blocks% (is|are) (n't| not) [a[n]] (custom|ce|craft-engine) block[s]");
}
private Expression<Block> blocks;

View File

@@ -1,41 +1,65 @@
package net.momirealms.craftengine.bukkit.compatibility.skript.condition;
import ch.njol.skript.Skript;
import ch.njol.skript.aliases.ItemType;
import ch.njol.skript.conditions.base.PropertyCondition;
import ch.njol.skript.doc.Description;
import ch.njol.skript.doc.Name;
import ch.njol.skript.doc.Since;
import ch.njol.skript.lang.Condition;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.SkriptParser;
import ch.njol.skript.util.slot.Slot;
import ch.njol.util.Kleenean;
import net.momirealms.craftengine.bukkit.api.CraftEngineItems;
import org.bukkit.event.Event;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
@Name("Is CraftEngine Item")
@Description({"Checks if the Item is CraftEngine item."})
@Since("1.0")
public class CondIsCustomItem extends Condition {
public static void register() {
Skript.registerCondition(CondIsCustomItem.class,
"%itemstacks% (is|are) custom item(s)",
"%itemstacks% (is|are)(n't| not) custom item(s)");
"%itemstack/itemtype/slot% (is [a[n]]|are) (custom|ce|craft-engine) item[s]",
"%itemstack/itemtype/slot% (isn't|is not|aren't|are not) [a[n]] (custom|ce|craft-engine) item[s]"
);
}
private Expression<ItemStack> items;
@Override
public boolean check(Event event) {
return items.check(event, CraftEngineItems::isCustomItem, isNegated());
}
@Override
public String toString(@Nullable Event event, boolean debug) {
return PropertyCondition.toString(this, PropertyCondition.PropertyType.BE, event, debug, items, "itemstack");
}
private Expression<?> item;
@SuppressWarnings("unchecked")
@Override
public boolean init(Expression<?>[] expressions, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult) {
items = (Expression<ItemStack>) expressions[0];
setNegated(matchedPattern > 1);
item = expressions[0];
setNegated(matchedPattern == 1);
return true;
}
@Override
public boolean check(Event event) {
Object single = item.getSingle(event);
ItemStack checkItemStack = null;
if (single instanceof ItemType itemType) {
checkItemStack = itemType.getTypes().getFirst().getStack();
} else if (single instanceof ItemStack itemStack) {
checkItemStack = itemStack;
} else if (single instanceof Slot slot) {
checkItemStack = slot.getItem();
}
if (checkItemStack == null) return isNegated() ? true : false;
boolean exists = CraftEngineItems.isCustomItem(checkItemStack);
if (!exists) return isNegated() ? true : false;
return isNegated() ? false : true;
}
@Override
public String toString(@Nullable Event event, boolean debug) {
return PropertyCondition.toString(this, PropertyCondition.PropertyType.BE, event, debug, item, "itemtypes");
}
}

View File

@@ -7,6 +7,7 @@ import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.SkriptParser;
import ch.njol.util.Kleenean;
import net.momirealms.craftengine.bukkit.api.CraftEngineFurniture;
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture;
import org.bukkit.entity.Entity;
import org.bukkit.event.Event;
import org.jetbrains.annotations.Nullable;
@@ -15,15 +16,18 @@ public class CondIsFurniture extends Condition {
public static void register() {
Skript.registerCondition(CondIsFurniture.class,
"%entities% (is|are) furniture",
"%entities% (is|are)(n't| not) furniture");
"%entities% (is|are) [a[n]] [(custom|ce|craft-engine)] furniture[s]",
"%entities% (is|are) (n't| not) [a[n]] [(custom|ce|craft-engine)] furniture[s]");
}
private Expression<Entity> entities;
@Override
public boolean check(Event event) {
return entities.check(event, CraftEngineFurniture::isFurniture, isNegated());
return entities.check(event, entity -> {
BukkitFurniture baseEntity = CraftEngineFurniture.getLoadedFurnitureByBaseEntity(entity);
return baseEntity != null;
}, isNegated());
}
@Override

View File

@@ -15,7 +15,7 @@ import org.jetbrains.annotations.Nullable;
public class EffPlaceCustomBlock extends Effect {
public static void register() {
Skript.registerEffect(EffPlaceCustomBlock.class, "place custom block %customblockstates% [%directions% %locations%]");
Skript.registerEffect(EffPlaceCustomBlock.class, "place (custom|ce|craft-engine) block %customblockstates% [at] [%directions% %locations%]");
}
private Expression<ImmutableBlockState> blocks;

View File

@@ -15,7 +15,7 @@ import org.jetbrains.annotations.Nullable;
public class EffPlaceFurniture extends Effect {
public static void register() {
Skript.registerEffect(EffPlaceFurniture.class, "place furniture %strings% [%directions% %locations%]");
Skript.registerEffect(EffPlaceFurniture.class, "place [(custom|ce|craft-engine)] furniture[s] %strings% [at] [%directions% %locations%]");
}
private Expression<String> furniture;

View File

@@ -14,7 +14,7 @@ import org.jetbrains.annotations.Nullable;
public class EffRemoveFurniture extends Effect {
public static void register() {
Skript.registerEffect(EffRemoveFurniture.class, "remove furniture %entities%");
Skript.registerEffect(EffRemoveFurniture.class, "remove [(custom|ce|craft-engine)] furniture %entities%");
}
private Expression<Entity> entities;
@@ -22,11 +22,9 @@ public class EffRemoveFurniture extends Effect {
@Override
protected void execute(Event e) {
for (Entity entity : entities.getArray(e)) {
if (CraftEngineFurniture.isFurniture(entity)) {
Furniture bukkitFurniture = CraftEngineFurniture.getLoadedFurnitureByBaseEntity(entity);
if (bukkitFurniture != null) {
bukkitFurniture.destroy();
}
Furniture bukkitFurniture = CraftEngineFurniture.getLoadedFurnitureByBaseEntity(entity);
if (bukkitFurniture != null) {
bukkitFurniture.destroy();
}
}
}

View File

@@ -0,0 +1,57 @@
package net.momirealms.craftengine.bukkit.compatibility.skript.event;
import ch.njol.skript.Skript;
import ch.njol.skript.doc.Description;
import ch.njol.skript.doc.Name;
import ch.njol.skript.doc.Since;
import ch.njol.skript.lang.Literal;
import ch.njol.skript.lang.SkriptEvent;
import ch.njol.skript.lang.SkriptParser;
import net.momirealms.craftengine.bukkit.api.event.CraftEngineReloadEvent;
import org.bukkit.event.Event;
import org.jetbrains.annotations.Nullable;
@Name("On CraftEngine Reload")
@Description({"Fires when CraftEngine reload"})
@Since("1.0")
public class EvtCraftEngineReload extends SkriptEvent {
public static void register() {
Skript.registerEvent("CraftEngine Loaded", EvtCraftEngineReload.class, CraftEngineReloadEvent.class, "(ce|craft(engine|-engine)) [first] (load[ed]|reload)")
.description("Called when Craft-Engine resource loaded.");
}
private boolean onlyCheckFirstCall;
private static boolean hasBeenCalled = false;
@Override
public boolean init(Literal<?>[] args, int matchedPattern, SkriptParser.ParseResult parser) {
// 检查是否包含 "first" 关键词
String expr = parser.expr;
this.onlyCheckFirstCall = expr.contains("first");
return true;
}
@Override
public boolean check(Event event) {
if (!(event instanceof CraftEngineReloadEvent reloadEvent)) {
return false;
}
if (onlyCheckFirstCall) {
if (hasBeenCalled) return false; // 如果 hasBeenCalled 已经为 true代表已经调用过了, 故返回 false。
hasBeenCalled = true;
return true;
}
hasBeenCalled = true;
return true;
}
@Override
public String toString(@Nullable Event event, boolean debug) {
return onlyCheckFirstCall ? "craftengine first load" : "craftengine reload";
}
public static boolean hasBeenLoad() {
return hasBeenCalled;
}
}

View File

@@ -1,28 +1,48 @@
package net.momirealms.craftengine.bukkit.compatibility.skript.event;
import ch.njol.skript.Skript;
import ch.njol.skript.doc.Description;
import ch.njol.skript.doc.Name;
import ch.njol.skript.doc.Since;
import ch.njol.skript.lang.Literal;
import ch.njol.skript.lang.SkriptEvent;
import ch.njol.skript.lang.SkriptParser;
import ch.njol.skript.registrations.EventValues;
import net.momirealms.craftengine.bukkit.api.event.CustomBlockBreakEvent;
import net.momirealms.craftengine.bukkit.api.event.CustomBlockPlaceEvent;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.UnsafeBlockStateMatcher;
import net.momirealms.craftengine.core.entity.player.InteractionHand;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
@SuppressWarnings({"unchecked"})
@Name("On Custom Block Place And Break")
@Description({"Fires when a Custom block gets place and broken"})
@Since("1.0")
public class EvtCustomBlock extends SkriptEvent {
public static void register() {
Skript.registerEvent("Break Custom Block", EvtCustomBlock.class, CustomBlockBreakEvent.class, "(break[ing]|1¦min(e|ing)) [[of] %-unsafeblockstatematchers%]")
Skript.registerEvent("Break Custom Block", EvtCustomBlock.class, CustomBlockBreakEvent.class, "(break[ing]|1¦min(e|ing)) of (custom|ce|craft-engine) block[s] [[of] %-unsafeblockstatematchers%]")
.description("Called when a custom block is broken by a player. If you use 'on mine', only events where the broken block dropped something will call the trigger.");
Skript.registerEvent("Place Custom Block", EvtCustomBlock.class, CustomBlockPlaceEvent.class, "(plac(e|ing)|build[ing]) [[of] %-unsafeblockstatematchers%]")
EventValues.registerEventValue(CustomBlockBreakEvent.class, Location.class, CustomBlockBreakEvent::location, EventValues.TIME_NOW);
EventValues.registerEventValue(CustomBlockBreakEvent.class, Player.class, CustomBlockBreakEvent::getPlayer, EventValues.TIME_NOW);
EventValues.registerEventValue(CustomBlockBreakEvent.class, Block.class, CustomBlockBreakEvent::bukkitBlock, EventValues.TIME_NOW);
EventValues.registerEventValue(CustomBlockBreakEvent.class, World.class, event -> event.location().getWorld(), EventValues.TIME_NOW);
Skript.registerEvent("Place Custom Block", EvtCustomBlock.class, CustomBlockPlaceEvent.class, "(plac(e|ing)|build[ing]) of (custom|ce|craft-engine) block[s] [[of] %-unsafeblockstatematchers%]")
.description("Called when a player places a custom block.");
EventValues.registerEventValue(CustomBlockPlaceEvent.class, Location.class, CustomBlockPlaceEvent::location, EventValues.TIME_NOW);
EventValues.registerEventValue(CustomBlockPlaceEvent.class, Player.class, CustomBlockPlaceEvent::player, EventValues.TIME_NOW);
EventValues.registerEventValue(CustomBlockPlaceEvent.class, Block.class, CustomBlockPlaceEvent::bukkitBlock, EventValues.TIME_NOW);
EventValues.registerEventValue(CustomBlockPlaceEvent.class, World.class, event -> event.location().getWorld(), EventValues.TIME_NOW);
}
@Nullable

View File

@@ -3,20 +3,32 @@ package net.momirealms.craftengine.bukkit.compatibility.skript.event;
import ch.njol.skript.Skript;
import ch.njol.skript.aliases.ItemType;
import ch.njol.skript.bukkitutil.ClickEventTracker;
import ch.njol.skript.doc.Description;
import ch.njol.skript.doc.Name;
import ch.njol.skript.doc.Since;
import ch.njol.skript.lang.Literal;
import ch.njol.skript.lang.SkriptEvent;
import ch.njol.skript.lang.SkriptParser;
import ch.njol.skript.registrations.EventValues;
import net.momirealms.craftengine.bukkit.api.event.CustomBlockInteractEvent;
import net.momirealms.craftengine.bukkit.api.event.FurnitureInteractEvent;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.UnsafeBlockStateMatcher;
import net.momirealms.craftengine.core.entity.player.InteractionHand;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.inventory.EquipmentSlot;
import org.jetbrains.annotations.Nullable;
import java.util.function.Predicate;
@Name("On Click on Custom Block and Furniture")
@Description({"Fires when click on custom block and furniture"})
@Since("1.0")
public class EvtCustomClick extends SkriptEvent {
private final static int RIGHT = 1, LEFT = 2, ANY = RIGHT | LEFT;
@@ -25,8 +37,20 @@ public class EvtCustomClick extends SkriptEvent {
@SuppressWarnings("unchecked")
public static void register() {
Skript.registerEvent("Interact Custom Block Furniture", EvtCustomClick.class, new Class[]{CustomBlockInteractEvent.class, FurnitureInteractEvent.class},
"[(" + RIGHT + ":right|" + LEFT + ":left)(| |-)][mouse(| |-)]click[ing] [on %-unsafeblockstatematchers/strings%] [(with|using|holding) %-itemtype%]",
"[(" + RIGHT + ":right|" + LEFT + ":left)(| |-)][mouse(| |-)]click[ing] (with|using|holding) %itemtype% on %unsafeblockstatematchers/strings%");
"[(" + RIGHT + ":right|" + LEFT + ":left)(| |-)][mouse(| |-)]click[ing] of (ce|craft-engine) [on %-unsafeblockstatematchers/strings%] [(with|using|holding) %-itemtype%]",
"[(" + RIGHT + ":right|" + LEFT + ":left)(| |-)][mouse(| |-)]click[ing] of (ce|craft-engine) (with|using|holding) %itemtype% on %unsafeblockstatematchers/strings%");
EventValues.registerEventValue(CustomBlockInteractEvent.class, Location.class, CustomBlockInteractEvent::location, EventValues.TIME_NOW);
EventValues.registerEventValue(CustomBlockInteractEvent.class, Player.class, CustomBlockInteractEvent::player, EventValues.TIME_NOW);
EventValues.registerEventValue(CustomBlockInteractEvent.class, Block.class, CustomBlockInteractEvent::bukkitBlock, EventValues.TIME_NOW);
EventValues.registerEventValue(CustomBlockInteractEvent.class, Entity.class, event -> null, EventValues.TIME_NOW);
EventValues.registerEventValue(CustomBlockInteractEvent.class, World.class, event -> event.location().getWorld(), EventValues.TIME_NOW);
EventValues.registerEventValue(FurnitureInteractEvent.class, Location.class, FurnitureInteractEvent::location, EventValues.TIME_NOW);
EventValues.registerEventValue(FurnitureInteractEvent.class, Player.class, FurnitureInteractEvent::player, EventValues.TIME_NOW);
EventValues.registerEventValue(CustomBlockInteractEvent.class, Block.class, event -> null, EventValues.TIME_NOW);
EventValues.registerEventValue(FurnitureInteractEvent.class, Entity.class, event -> event.furniture().baseEntity(), EventValues.TIME_NOW);
EventValues.registerEventValue(FurnitureInteractEvent.class, World.class, event -> event.location().getWorld(), EventValues.TIME_NOW);
}
private @Nullable Literal<?> type;

View File

@@ -1,24 +1,44 @@
package net.momirealms.craftengine.bukkit.compatibility.skript.event;
import ch.njol.skript.Skript;
import ch.njol.skript.doc.Description;
import ch.njol.skript.doc.Name;
import ch.njol.skript.doc.Since;
import ch.njol.skript.lang.Literal;
import ch.njol.skript.lang.SkriptEvent;
import ch.njol.skript.lang.SkriptParser;
import ch.njol.skript.registrations.EventValues;
import net.momirealms.craftengine.bukkit.api.event.FurnitureBreakEvent;
import net.momirealms.craftengine.bukkit.api.event.FurniturePlaceEvent;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
@SuppressWarnings({"unchecked"})
@Name("On Custom Furniture Place And Break")
@Description({"Fires when a Custom furniture gets place and broken"})
@Since("1.0")
public class EvtCustomFurniture extends SkriptEvent {
public static void register() {
Skript.registerEvent("Break Furniture", EvtCustomFurniture.class, FurnitureBreakEvent.class, "(break[ing]) [[of] %-strings%]")
Skript.registerEvent("Break Furniture", EvtCustomFurniture.class, FurnitureBreakEvent.class, "(break[ing]) of [(custom|ce|craft-engine)] furniture[s] [[of] %-strings%]")
.description("Called when a furniture is broken by a player.");
Skript.registerEvent("Place Furniture", EvtCustomFurniture.class, FurniturePlaceEvent.class, "(plac(e|ing)|build[ing]) [[of] %-strings%]")
EventValues.registerEventValue(FurnitureBreakEvent.class, Location.class, FurnitureBreakEvent::location, EventValues.TIME_NOW);
EventValues.registerEventValue(FurnitureBreakEvent.class, Player.class, FurnitureBreakEvent::player, EventValues.TIME_NOW);
EventValues.registerEventValue(FurnitureBreakEvent.class, Entity.class, event -> event.furniture().baseEntity(), EventValues.TIME_NOW);
EventValues.registerEventValue(FurnitureBreakEvent.class, World.class, event -> event.location().getWorld(), EventValues.TIME_NOW);
Skript.registerEvent("Place Furniture", EvtCustomFurniture.class, FurniturePlaceEvent.class, "(plac(e|ing)|build[ing]) of [(custom|ce|craft-engine)] furniture[s] [[of] %-strings%]")
.description("Called when a player places a furniture.");
EventValues.registerEventValue(FurniturePlaceEvent.class, Location.class, FurniturePlaceEvent::location, EventValues.TIME_NOW);
EventValues.registerEventValue(FurniturePlaceEvent.class, Player.class, FurniturePlaceEvent::player, EventValues.TIME_NOW);
EventValues.registerEventValue(FurniturePlaceEvent.class, Entity.class, event -> event.furniture().baseEntity(), EventValues.TIME_NOW);
EventValues.registerEventValue(FurniturePlaceEvent.class, World.class, event -> event.location().getWorld(), EventValues.TIME_NOW);
}
@Nullable

View File

@@ -13,7 +13,7 @@ import java.util.Optional;
public class ExprBlockCustomBlockID extends SimplePropertyExpression<Object, String> {
public static void register() {
register(ExprBlockCustomBlockID.class, String.class, "custom block id", "blocks/blockdata/customblockstates");
register(ExprBlockCustomBlockID.class, String.class, "(custom|ce|craft-engine) block [namespace] id", "blocks/blockdata/customblockstates");
}
@Override

View File

@@ -10,7 +10,7 @@ import org.jetbrains.annotations.Nullable;
public class ExprBlockCustomBlockState extends SimplePropertyExpression<Object, ImmutableBlockState> {
public static void register() {
register(ExprBlockCustomBlockState.class, ImmutableBlockState.class, "custom block[ ]state", "blocks/blockdata");
register(ExprBlockCustomBlockState.class, ImmutableBlockState.class, "(custom|ce|craft-engine) block[ ]state", "blocks/blockdata");
}
@Override

View File

@@ -1,6 +1,10 @@
package net.momirealms.craftengine.bukkit.compatibility.skript.expression;
import ch.njol.skript.Skript;
import ch.njol.skript.aliases.ItemType;
import ch.njol.skript.doc.Description;
import ch.njol.skript.doc.Name;
import ch.njol.skript.doc.Since;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.ExpressionType;
import ch.njol.skript.lang.SkriptParser;
@@ -14,29 +18,44 @@ import org.bukkit.event.Event;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
public class ExprCustomItem extends SimpleExpression<ItemStack> {
import java.util.ArrayList;
import java.util.List;
@Name("CraftEngine Item")
@Description({"Get CraftEngine items."})
@Since("1.0")
public class ExprCustomItem extends SimpleExpression<ItemType> {
public static void register() {
Skript.registerExpression(ExprCustomItem.class, ItemStack.class, ExpressionType.SIMPLE, "[(the|a)] custom item [with id] %string%");
Skript.registerExpression(ExprCustomItem.class, ItemType.class, ExpressionType.SIMPLE, "[(the|a)] (custom|ce|craft-engine) item [with [namespace] id] %strings%");
}
private Expression<String> itemId;
private Expression<?> itemIds;
@Override
@SuppressWarnings("unchecked")
public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult) {
itemId = (Expression<String>) exprs[0];
itemIds = exprs[0];
return true;
}
@Override
@Nullable
protected ItemStack[] get(Event e) {
String itemId = this.itemId.getSingle(e);
if (itemId == null)
return null;
CustomItem<ItemStack> customItem = CraftEngineItems.byId(Key.of(itemId));
return customItem == null ? null : new ItemStack[] {customItem.buildItemStack(ItemBuildContext.EMPTY)};
protected ItemType[] get(Event event) {
Object[] objects = itemIds.getArray(event);
List<ItemType> items = new ArrayList<>();
for (Object object : objects) {
if (object instanceof String string) {
CustomItem<ItemStack> customItem = CraftEngineItems.byId(Key.of(string));
if (customItem != null) {
ItemType itemType = new ItemType(customItem.buildItemStack(ItemBuildContext.EMPTY));
items.add(itemType);
}
}
}
return items.toArray(new ItemType[0]);
}
@Override
@@ -45,12 +64,12 @@ public class ExprCustomItem extends SimpleExpression<ItemStack> {
}
@Override
public Class<ItemStack> getReturnType() {
return ItemStack.class;
public Class<ItemType> getReturnType() {
return ItemType.class;
}
@Override
public String toString(@Nullable Event e, boolean debug) {
return "the custom item with id " + itemId.toString(e, debug);
return "craft-engine item with id " + itemIds.toString(e, debug);
}
}

View File

@@ -10,12 +10,12 @@ import java.util.Optional;
public class ExprEntityFurnitureID extends SimplePropertyExpression<Object, String> {
public static void register() {
register(ExprEntityFurnitureID.class, String.class, "furniture id", "entities");
register(ExprEntityFurnitureID.class, String.class, "[(custom|ce|craft-engine)] furniture [namespace] id", "entities");
}
@Override
public @Nullable String convert(Object object) {
if (object instanceof Entity entity && CraftEngineFurniture.isFurniture(entity)) {
if (object instanceof Entity entity) {
return Optional.ofNullable(CraftEngineFurniture.getLoadedFurnitureByBaseEntity(entity))
.map(it -> it.id().toString())
.orElse(null);

View File

@@ -1,12 +1,18 @@
package net.momirealms.craftengine.bukkit.compatibility.skript.expression;
import ch.njol.skript.Skript;
import ch.njol.skript.aliases.ItemType;
import ch.njol.skript.classes.Changer;
import ch.njol.skript.expressions.base.SimplePropertyExpression;
import ch.njol.util.coll.CollectionUtils;
import ch.njol.skript.doc.Description;
import ch.njol.skript.doc.Name;
import ch.njol.skript.doc.Since;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.ExpressionType;
import ch.njol.skript.lang.SkriptParser;
import ch.njol.skript.lang.util.SimpleExpression;
import ch.njol.skript.util.slot.Slot;
import ch.njol.util.Kleenean;
import net.momirealms.craftengine.bukkit.api.CraftEngineItems;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.util.Key;
import org.bukkit.event.Event;
import org.bukkit.inventory.ItemStack;
@@ -14,27 +20,52 @@ import org.jetbrains.annotations.Nullable;
import java.util.Optional;
public class ExprItemCustomItemID extends SimplePropertyExpression<Object, String> {
@Name("CraftEngine Item ID")
@Description({"Get CraftEngine item id."})
@Since("1.0")
public class ExprItemCustomItemID extends SimpleExpression<String> {
public static void register() {
register(ExprItemCustomItemID.class, String.class, "custom item id", "itemstacks/itemtypes");
Skript.registerExpression(ExprItemCustomItemID.class, String.class, ExpressionType.PROPERTY,
"(custom|ce|craft-engine) item [namespace] id of %itemstack/itemtype/slot%",
"%itemstack/itemtype/slot%'[s] (custom|ce|craft-engine) item [namespace] id"
);
}
private Expression<?> itemStackExpr;
@Override
public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult) {
itemStackExpr = exprs[0];
return true;
}
@Override
public @Nullable String convert(Object object) {
if (object instanceof ItemStack itemStack)
return Optional.ofNullable(CraftEngineItems.byItemStack(itemStack)).map(it -> it.id().toString()).orElse(null);
if (object instanceof ItemType itemType) {
ItemStack itemStack = new ItemStack(itemType.getMaterial());
itemStack.setItemMeta(itemType.getItemMeta());
return Optional.ofNullable(CraftEngineItems.byItemStack(itemStack)).map(it -> it.id().toString()).orElse(null);
protected String[] get(Event event) {
Object single = itemStackExpr.getSingle(event);
String result = null;
if (single instanceof ItemStack itemStack) {
result = Optional.of(itemStack).map(this::getCraftEngineItemId).orElse(null);
} else if (single instanceof ItemType itemType) {
result = Optional.ofNullable(itemType.getTypes().getFirst().getStack()).map(this::getCraftEngineItemId).orElse(null);
} else if (single instanceof Slot slot) {
result = Optional.ofNullable(slot.getItem()).map(this::getCraftEngineItemId).orElse(null);
}
return null;
return new String[] {result};
}
private String getCraftEngineItemId(ItemStack itemStack) {
return Optional.ofNullable(CraftEngineItems.getCustomItemId(itemStack))
.map(Key::asString)
.orElse(null);
}
@Override
protected String getPropertyName() {
return "custom item id";
public boolean isSingle() {
return itemStackExpr.isSingle();
}
@Override
@@ -42,23 +73,14 @@ public class ExprItemCustomItemID extends SimplePropertyExpression<Object, Strin
return String.class;
}
// 不需要处理 add, delete 等修改操作
@Override
public Class<?>[] acceptChange(Changer.ChangeMode mode) {
return CollectionUtils.array(String.class);
return null;
}
@Override
public void change(Event e, @Nullable Object[] delta, Changer.ChangeMode mode) {
Key id = Key.of((String) delta[0]);
for (Object item : getExpr().getArray(e)) {
if (item instanceof ItemStack itemStack) {
Item<ItemStack> item1 = BukkitItemManager.instance().wrap(itemStack);
Item<ItemStack> item2 = BukkitItemManager.instance().createWrappedItem(id, null);
item1.merge(item2);
} else if (item instanceof ItemType itemType) {
Item<ItemStack> item2 = BukkitItemManager.instance().createWrappedItem(id, null);
itemType.setItemMeta(item2.getItem().getItemMeta());
}
}
public String toString(@Nullable Event event, boolean debug) {
return "craft-engine item ID of " + itemStackExpr.toString(event, debug);
}
}

View File

@@ -26,10 +26,11 @@ import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.injector.WorldStorageInjector;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.world.BukkitWorldManager;
import net.momirealms.craftengine.core.block.EmptyBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.util.ReflectionUtils;
import net.momirealms.craftengine.core.util.LazyReference;
import net.momirealms.craftengine.core.world.CEWorld;
import net.momirealms.craftengine.core.world.ChunkPos;
import net.momirealms.craftengine.core.world.SectionPos;
@@ -47,38 +48,45 @@ import static java.util.Objects.requireNonNull;
public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent {
private static int[] ordinalToIbdID;
private final Set<CEChunk> chunksToSave;
private final CEWorld ceWorld;
private final LazyReference<CEWorld> ceWorld;
private final Set<ChunkPos> brokenChunks = Collections.synchronizedSet(new HashSet<>());
protected FastAsyncWorldEditDelegate(EditSessionEvent event, Extent extent) {
super(extent);
this.chunksToSave = new HashSet<>();
World weWorld = event.getWorld();
org.bukkit.World world = Bukkit.getWorld(requireNonNull(weWorld).getName());
CEWorld ceWorld = CraftEngine.instance().worldManager().getWorld(requireNonNull(world).getUID());
this.ceWorld = requireNonNull(ceWorld);
this.ceWorld = LazyReference.lazyReference(() -> {
World weWorld = event.getWorld();
requireNonNull(weWorld, "WorldEdit world is null");
org.bukkit.World world = Bukkit.getWorld(weWorld.getName());
requireNonNull(world, () -> "WorldEdit world " + weWorld.getName() + " is not a Bukkit world");
CEWorld ceWorld = BukkitWorldManager.instance().getWorld(world);
requireNonNull(ceWorld, () -> "WorldEdit world " + world.getName() + " is not a CraftEngine world");
return ceWorld;
});
}
public static void init() {
Settings.settings().EXTENT.ALLOWED_PLUGINS.add(FastAsyncWorldEditDelegate.class.getCanonicalName());
FaweAdapter<?, ?> adapter = (FaweAdapter<?, ?>) WorldEditPlugin.getInstance().getBukkitImplAdapter();
Method ordinalToIbdIDMethod = ReflectionUtils.getDeclaredMethod(CachedBukkitAdapter.class, int.class.arrayType(), new String[]{"getOrdinalToIbdID"});
try {
assert ordinalToIbdIDMethod != null;
ordinalToIbdID = (int[]) ordinalToIbdIDMethod.invoke(adapter);
Method ordinalToIbdIDMethod = CachedBukkitAdapter.class.getDeclaredMethod("getOrdinalToIbdID"); // 这样获取有代码提示
ordinalToIbdIDMethod.setAccessible(true);
if (WorldEditPlugin.getInstance().getBukkitImplAdapter() instanceof FaweAdapter<?, ?> faweAdapter) { // 确保是 paper 服务器的才调用这个
ordinalToIbdID = (int[]) ordinalToIbdIDMethod.invoke(faweAdapter);
} else { // 应该在 spigot 的服务器上用不了这个
CraftEngine.instance().logger().warn("Failed to init FastAsyncWorldEdit compatibility, Please use the server of paper or its fork.");
return;
}
} catch (ReflectiveOperationException e) {
throw new RuntimeException("Failed to init FastAsyncWorldEdit compatibility", e);
CraftEngine.instance().logger().warn("Failed to init FastAsyncWorldEdit compatibility", e);
return;
}
WorldEdit.getInstance().getEventBus().register(new Object() {
@Subscribe
@SuppressWarnings("unused")
public void onEditSessionEvent(EditSessionEvent event) {
World weWorld = event.getWorld();
if (weWorld == null) return;
Extent currentExtent = event.getExtent();
if (event.getStage() == EditSession.Stage.BEFORE_CHANGE) {
event.setExtent(new FastAsyncWorldEditDelegate(event, currentExtent));
}
if (event.getWorld() == null || event.getStage() != EditSession.Stage.BEFORE_CHANGE) return;
if (!BukkitWorldManager.instance().initialized()) return;
event.setExtent(new FastAsyncWorldEditDelegate(event, event.getExtent()));
}
});
}
@@ -101,6 +109,10 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent {
}
}
private CEWorld ceWorld() {
return this.ceWorld.get();
}
@Override
public int setBlocks(final Set<BlockVector3> vset, final Pattern pattern) {
this.processBlocks(vset, pattern, getMask(), null);
@@ -180,10 +192,10 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent {
Operation operation = super.commit();
List<ChunkPos> chunks = new ArrayList<>(this.brokenChunks);
this.brokenChunks.clear();
Object worldServer = this.ceWorld.world().serverWorld();
Object worldServer = this.ceWorld().world().serverWorld();
Object chunkSource = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(worldServer);
for (ChunkPos chunk : chunks) {
CEChunk loaded = this.ceWorld.getChunkAtIfLoaded(chunk.longKey());
CEChunk loaded = this.ceWorld().getChunkAtIfLoaded(chunk.longKey());
// only inject loaded chunks
if (loaded == null) continue;
injectLevelChunk(chunkSource, loaded);
@@ -214,8 +226,8 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent {
this.brokenChunks.add(ChunkPos.of(chunkX, chunkZ));
if (BlockStateUtils.isVanillaBlock(newStateId) && BlockStateUtils.isVanillaBlock(oldStateId)) return;
try {
CEChunk ceChunk = Optional.ofNullable(this.ceWorld.getChunkAtIfLoaded(chunkX, chunkZ))
.orElse(this.ceWorld.worldDataStorage().readChunkAt(this.ceWorld, new ChunkPos(chunkX, chunkZ)));
CEChunk ceChunk = Optional.ofNullable(this.ceWorld().getChunkAtIfLoaded(chunkX, chunkZ))
.orElse(this.ceWorld().worldDataStorage().readChunkAt(this.ceWorld(), new ChunkPos(chunkX, chunkZ)));
CESection ceSection = ceChunk.sectionById(SectionPos.blockToSectionCoord(blockY));
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(newStateId);
if (immutableBlockState == null) {
@@ -232,7 +244,7 @@ public class FastAsyncWorldEditDelegate extends AbstractDelegateExtent {
private void saveAllChunks() {
try {
for (CEChunk ceChunk : this.chunksToSave) {
this.ceWorld.worldDataStorage().writeChunkAt(ceChunk.chunkPos(), ceChunk);
this.ceWorld().worldDataStorage().writeChunkAt(ceChunk.chunkPos(), ceChunk);
}
this.chunksToSave.clear();
} catch (IOException e) {

View File

@@ -56,6 +56,22 @@ public final class CraftEngineBlocks {
return place(location, block, UpdateOption.UPDATE_ALL, playSound);
}
/**
* Place a custom block
*
* @param location location
* @param blockId block owner id
* @param playSound whether to play place sounds
* @return success or not
*/
public static boolean place(@NotNull Location location,
@NotNull Key blockId,
boolean playSound) {
CustomBlock block = byId(blockId);
if (block == null) return false;
return place(location, block.defaultState(), UpdateOption.UPDATE_ALL, playSound);
}
/**
* Place a custom block with given properties
*

View File

@@ -1,6 +1,7 @@
package net.momirealms.craftengine.bukkit.block;
import io.papermc.paper.event.block.BlockBreakBlockEvent;
import net.momirealms.craftengine.bukkit.api.BukkitAdaptors;
import net.momirealms.craftengine.bukkit.api.event.CustomBlockBreakEvent;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
@@ -56,7 +57,7 @@ public final class BlockEventListener implements Listener {
public void onPlayerAttack(EntityDamageByEntityEvent event) {
if (!VersionHelper.isOrAbove1_20_5()) {
if (event.getDamager() instanceof Player player) {
BukkitServerPlayer serverPlayer = plugin.adapt(player);
BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player);
serverPlayer.setClientSideCanBreakBlock(true);
}
}
@@ -65,7 +66,7 @@ public final class BlockEventListener implements Listener {
@EventHandler(ignoreCancelled = true)
public void onPlaceBlock(BlockPlaceEvent event) {
Player player = event.getPlayer();
BukkitServerPlayer serverPlayer = plugin.adapt(player);
BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player);
// send swing if player is clicking a replaceable block
if (serverPlayer.shouldResendSwing()) {
player.swingHand(event.getHand());
@@ -112,7 +113,7 @@ public final class BlockEventListener implements Listener {
int stateId = BlockStateUtils.blockStateToId(blockState);
Player player = event.getPlayer();
Location location = block.getLocation();
BukkitServerPlayer serverPlayer = this.plugin.adapt(player);
BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player);
net.momirealms.craftengine.core.world.World world = new BukkitWorld(player.getWorld());
WorldPosition position = new WorldPosition(world, location.getBlockX() + 0.5, location.getBlockY() + 0.5, location.getBlockZ() + 0.5);
Item<ItemStack> itemInHand = serverPlayer.getItemInHand(InteractionHand.MAIN_HAND);
@@ -246,7 +247,7 @@ public final class BlockEventListener implements Listener {
Location location = player.getLocation();
ImmutableBlockState state = optionalCustomState.get();
Cancellable cancellable = Cancellable.of(event::isCancelled, event::setCancelled);
state.owner().value().execute(PlayerOptionalContext.of(this.plugin.adapt(player), ContextHolder.builder()
state.owner().value().execute(PlayerOptionalContext.of(BukkitAdaptors.adapt(player), ContextHolder.builder()
.withParameter(DirectContextParameters.EVENT, cancellable)
.withParameter(DirectContextParameters.POSITION, new WorldPosition(new BukkitWorld(event.getWorld()), LocationUtils.toVec3d(location)))
.withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block))

View File

@@ -3,8 +3,6 @@ package net.momirealms.craftengine.bukkit.block;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntArrayList;
@@ -26,16 +24,7 @@ import net.momirealms.craftengine.bukkit.util.TagUtils;
import net.momirealms.craftengine.core.block.*;
import net.momirealms.craftengine.core.block.behavior.EmptyBlockBehavior;
import net.momirealms.craftengine.core.block.parser.BlockStateParser;
import net.momirealms.craftengine.core.block.properties.Properties;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.loot.LootTable;
import net.momirealms.craftengine.core.pack.LoadingSequence;
import net.momirealms.craftengine.core.pack.Pack;
import net.momirealms.craftengine.core.pack.ResourceLocation;
import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration;
import net.momirealms.craftengine.core.plugin.config.ConfigParser;
import net.momirealms.craftengine.core.plugin.config.StringKeyConstructor;
import net.momirealms.craftengine.core.plugin.context.event.EventFunctions;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
import net.momirealms.craftengine.core.registry.Holder;
@@ -43,10 +32,10 @@ import net.momirealms.craftengine.core.registry.WritableRegistry;
import net.momirealms.craftengine.core.sound.SoundData;
import net.momirealms.craftengine.core.sound.Sounds;
import net.momirealms.craftengine.core.util.*;
import net.momirealms.craftengine.core.world.chunk.PalettedContainer;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.block.data.BlockData;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
@@ -68,20 +57,15 @@ import java.util.function.Predicate;
public final class BukkitBlockManager extends AbstractBlockManager {
private static BukkitBlockManager instance;
private final BukkitCraftEngine plugin;
private final BlockParser blockParser;
// The total amount of blocks registered
private int customBlockCount;
private ImmutableBlockState[] stateId2ImmutableBlockStates;
// Minecraft objects
// Cached new blocks $ holders
private Map<Key, Integer> internalId2StateId;
private Map<Key, DelegatingBlock> registeredBlocks;
private Map<Integer, Object> stateId2BlockHolder;
// This map is used to change the block states that are not necessarily needed into a certain block state
private Map<Integer, Integer> blockAppearanceMapper;
// Used to automatically arrange block states for strings such as minecraft:note_block:0
private Map<Key, List<Integer>> blockAppearanceArranger;
private Map<Key, List<Integer>> realBlockArranger;
// Record the amount of real blocks by block type
private Map<Key, Integer> registeredRealBlockSlots;
// A set of blocks that sounds have been removed
@@ -102,7 +86,6 @@ public final class BukkitBlockManager extends AbstractBlockManager {
super(plugin);
instance = this;
this.plugin = plugin;
this.blockParser = new BlockParser();
this.initVanillaRegistry();
this.loadMappingsAndAdditionalBlocks();
this.registerBlocks();
@@ -124,6 +107,11 @@ public final class BukkitBlockManager extends AbstractBlockManager {
this.resetPacketConsumers();
}
@Override
public String stateRegistryIdToStateSNBT(int id) {
return BlockStateUtils.idToBlockState(id).toString();
}
public static BukkitBlockManager instance() {
return instance;
}
@@ -227,12 +215,7 @@ public final class BukkitBlockManager extends AbstractBlockManager {
}
@Override
public ConfigParser parser() {
return this.blockParser;
}
@Override
public void addBlock(Key id, CustomBlock customBlock) {
public void addBlockInternal(Key id, CustomBlock customBlock) {
// bind appearance and real state
for (ImmutableBlockState state : customBlock.variantProvider().states()) {
ImmutableBlockState previous = this.stateId2ImmutableBlockStates[state.customBlockState().registryId() - BlockStateUtils.vanillaStateSize()];
@@ -243,7 +226,7 @@ public final class BukkitBlockManager extends AbstractBlockManager {
this.tempBlockAppearanceConvertor.put(state.customBlockState().registryId(), state.vanillaBlockState().registryId());
this.appearanceToRealState.computeIfAbsent(state.vanillaBlockState().registryId(), k -> new IntArrayList()).add(state.customBlockState().registryId());
}
super.addBlock(id, customBlock);
super.addBlockInternal(id, customBlock);
}
@Override
@@ -251,6 +234,11 @@ public final class BukkitBlockManager extends AbstractBlockManager {
return BlockStateUtils.getBlockOwnerIdFromState(state.handle());
}
@Override
protected Key getBlockOwnerId(int id) {
return BlockStateUtils.getBlockOwnerIdFromState(BlockStateUtils.idToBlockState(id));
}
@Override
public int availableAppearances(Key blockType) {
return Optional.ofNullable(this.registeredRealBlockSlots.get(blockType)).orElse(0);
@@ -297,6 +285,11 @@ public final class BukkitBlockManager extends AbstractBlockManager {
BlockStateUtils.init(vanillaStateCount);
}
@Override
protected CustomBlock.Builder platformBuilder(Key id) {
return BukkitCustomBlock.builder(id);
}
@SuppressWarnings("unchecked")
private void registerBlocks() {
this.plugin.logger().info("Registering blocks. Please wait...");
@@ -325,7 +318,9 @@ public final class BukkitBlockManager extends AbstractBlockManager {
this.realBlockArranger = builder3.build();
this.registeredBlocks = builder4.build();
this.blockRegisterOrder = ImmutableList.copyOf(order);
if (MCUtils.ceilLog2(BlockStateUtils.vanillaStateSize() + counter) == MCUtils.ceilLog2(BlockStateUtils.vanillaStateSize())) {
PalettedContainer.NEED_DOWNGRADE = false;
}
for (Object block : (Iterable<Object>) MBuiltInRegistries.BLOCK) {
Object soundType = CoreReflections.field$BlockBehaviour$soundType.get(block);
if (affectedBlockSounds.contains(soundType)) {
@@ -401,225 +396,6 @@ public final class BukkitBlockManager extends AbstractBlockManager {
return cachedUpdateTagsPacket;
}
public class BlockParser implements ConfigParser {
public static final String[] CONFIG_SECTION_NAME = new String[]{"blocks", "block"};
@Override
public String[] sectionId() {
return CONFIG_SECTION_NAME;
}
@Override
public int loadingSequence() {
return LoadingSequence.BLOCK;
}
@Override
public void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) {
if (id.namespace().equals("minecraft") && Registry.MATERIAL.get(KeyUtils.toNamespacedKey(id)) != null) {
parseVanillaBlock(pack, path, id, section);
} else {
// check duplicated config
if (BukkitBlockManager.this.byId.containsKey(id)) {
throw new LocalizedResourceConfigException("warning.config.block.duplicate");
}
parseCustomBlock(pack, path, id, section);
}
}
private void parseCustomBlock(Pack pack, Path path, Key id, Map<String, Object> section) {
// read block settings
BlockSettings settings = BlockSettings.fromMap(id, MiscUtils.castToMap(section.get("settings"), true));
// read states
Map<String, Property<?>> properties;
Map<String, Integer> appearances;
Map<String, BlockStateVariant> variants;
Map<String, Object> stateSection = MiscUtils.castToMap(ResourceConfigUtils.requireNonNullOrThrow(ResourceConfigUtils.get(section, "state", "states"), "warning.config.block.missing_state"), true);
boolean singleState = !stateSection.containsKey("properties");
// single state
if (singleState) {
properties = Map.of();
int internalId = ResourceConfigUtils.getAsInt(ResourceConfigUtils.requireNonNullOrThrow(stateSection.get("id"), "warning.config.block.state.missing_real_id"), "id");
VanillaBlockState vanillaBlock = getVanillaBlock(id, stateSection);
appearances = Map.of("", vanillaBlock.registryId());
Key internalBlockId = Key.of(Key.DEFAULT_NAMESPACE, vanillaBlock.type().value() + "_" + internalId);
int internalBlockRegistryId = Optional.ofNullable(internalId2StateId.get(internalBlockId)).orElse(-1);
if (internalBlockRegistryId == -1) {
throw new LocalizedResourceConfigException("warning.config.block.state.invalid_real_id", internalBlockId.toString(), String.valueOf(availableAppearances(vanillaBlock.type()) - 1));
}
variants = Map.of("", new BlockStateVariant("", settings, internalBlockRegistryId));
} else {
// properties
properties = getProperties(MiscUtils.castToMap(ResourceConfigUtils.requireNonNullOrThrow(stateSection.get("properties"), "warning.config.block.state.missing_properties"), true));
// appearance
appearances = new HashMap<>();
Map<String, Key> appearance2BlockType = new HashMap<>();
for (Map.Entry<String, Object> appearanceEntry : MiscUtils.castToMap(ResourceConfigUtils.requireNonNullOrThrow(stateSection.get("appearances"), "warning.config.block.state.missing_appearances"), false).entrySet()) {
if (appearanceEntry.getValue() instanceof Map<?, ?>) {
VanillaBlockState vanillaBlock = getVanillaBlock(id, MiscUtils.castToMap(appearanceEntry.getValue(), false));
appearances.put(appearanceEntry.getKey(), vanillaBlock.registryId());
appearance2BlockType.put(appearanceEntry.getKey(), vanillaBlock.type());
}
}
// variants
variants = new HashMap<>();
for (Map.Entry<String, Object> variantEntry : MiscUtils.castToMap(ResourceConfigUtils.requireNonNullOrThrow(stateSection.get("variants"), "warning.config.block.state.missing_variants"), false).entrySet()) {
if (variantEntry.getValue() instanceof Map<?, ?>) {
Map<String, Object> variantSection = MiscUtils.castToMap(variantEntry.getValue(), false);
String variantNBT = variantEntry.getKey();
String appearance = ResourceConfigUtils.requireNonEmptyStringOrThrow(variantSection.get("appearance"), "warning.config.block.state.variant.missing_appearance");
if (!appearances.containsKey(appearance)) {
throw new LocalizedResourceConfigException("warning.config.block.state.variant.invalid_appearance", variantNBT, appearance);
}
int internalId = ResourceConfigUtils.getAsInt(ResourceConfigUtils.requireNonNullOrThrow(variantSection.get("id"), "warning.config.block.state.missing_real_id"), "id");
Key baseBlock = appearance2BlockType.get(appearance);
Key internalBlockId = Key.of(Key.DEFAULT_NAMESPACE, baseBlock.value() + "_" + internalId);
int internalBlockRegistryId = Optional.ofNullable(internalId2StateId.get(internalBlockId)).orElse(-1);
if (internalBlockRegistryId == -1) {
throw new LocalizedResourceConfigException("warning.config.block.state.invalid_real_id", internalBlockId.toString(), String.valueOf(availableAppearances(baseBlock) - 1));
}
Map<String, Object> anotherSetting = MiscUtils.castToMap(variantSection.get("settings"), true);
variants.put(variantNBT, new BlockStateVariant(appearance, anotherSetting == null ? settings : BlockSettings.ofFullCopy(settings, anotherSetting), internalBlockRegistryId));
}
}
}
CustomBlock block = BukkitCustomBlock.builder(id)
.appearances(appearances)
.variantMapper(variants)
.properties(properties)
.settings(settings)
.lootTable(LootTable.fromMap(MiscUtils.castToMap(section.get("loot"), true)))
.behavior(MiscUtils.getAsMapList(ResourceConfigUtils.get(section, "behavior", "behaviors")))
.events(EventFunctions.parseEvents(ResourceConfigUtils.get(section, "events", "event")))
.build();
addBlock(id, block);
}
private void parseVanillaBlock(Pack pack, Path path, Key id, Map<String, Object> section) {
Map<String, Object> settings = MiscUtils.castToMap(section.get("settings"), true);
if (settings != null) {
Object clientBoundTags = settings.get("client-bound-tags");
if (clientBoundTags instanceof List<?> list) {
List<String> clientSideTags = MiscUtils.getAsStringList(list).stream().filter(ResourceLocation::isValid).toList();
Object nmsBlock = FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.BLOCK, KeyUtils.toResourceLocation(id));
FastNMS.INSTANCE.method$IdMap$getId(MBuiltInRegistries.BLOCK, nmsBlock).ifPresent(i ->
BukkitBlockManager.this.clientBoundTags.put(i, clientSideTags));
}
}
}
}
@NotNull
private Map<String, Property<?>> getProperties(Map<String, Object> propertiesSection) {
Map<String, Property<?>> properties = new HashMap<>();
for (Map.Entry<String, Object> entry : propertiesSection.entrySet()) {
Property<?> property = Properties.fromMap(entry.getKey(), MiscUtils.castToMap(entry.getValue(), false));
properties.put(entry.getKey(), property);
}
return properties;
}
@NotNull
private VanillaBlockState getVanillaBlock(Key id, Map<String, Object> section) {
// require state non null
String vanillaBlockStateTag = ResourceConfigUtils.requireNonEmptyStringOrThrow(section.get("state"), "warning.config.block.state.missing_state");
// get its registry id
int vanillaBlockStateRegistryId = getVanillaBlockStateRegistryId(vanillaBlockStateTag);
// check if another block has occupied the appearance
// TODO blocks share the same look
Key ifAny = this.tempRegistryIdConflictMap.get(vanillaBlockStateRegistryId);
if (ifAny != null && !ifAny.equals(id)) {
throw new LocalizedResourceConfigException("warning.config.block.state.conflict", BlockStateUtils.fromBlockData(BlockStateUtils.idToBlockState(vanillaBlockStateRegistryId)).getAsString(), ifAny.toString());
}
// require models not to be null
Object models = ResourceConfigUtils.requireNonNullOrThrow(ResourceConfigUtils.get(section, "models", "model"), "warning.config.block.state.missing_model");
List<JsonObject> variants = ResourceConfigUtils.parseConfigAsList(models, this::getVariantModel);
if (variants.isEmpty()) {
throw new LocalizedResourceConfigException("warning.config.block.state.missing_model");
}
// TODO blocks share the same look
this.tempRegistryIdConflictMap.put(vanillaBlockStateRegistryId, id);
// gets the full block state
String blockState = BlockStateUtils.idToBlockState(vanillaBlockStateRegistryId).toString();
Key blockId = Key.of(blockState.substring(blockState.indexOf('{') + 1, blockState.lastIndexOf('}')));
String propertyNBT = blockState.substring(blockState.indexOf('[') + 1, blockState.lastIndexOf(']'));
// for generating assets
JsonElement combinedVariant = GsonHelper.combine(variants);
this.blockStateOverrides.computeIfAbsent(blockId, k -> new HashMap<>()).put(propertyNBT, combinedVariant);
this.tempVanillaBlockStateModels.put(vanillaBlockStateRegistryId, combinedVariant);
return new VanillaBlockState(blockId, propertyNBT, vanillaBlockStateRegistryId);
}
public record VanillaBlockState(Key type, String properties, int registryId) {
}
private JsonObject getVariantModel(Map<String, Object> singleModelMap) {
JsonObject json = new JsonObject();
String modelPath = ResourceConfigUtils.requireNonEmptyStringOrThrow(singleModelMap.get("path"), "warning.config.block.state.model.missing_path");
if (!ResourceLocation.isValid(modelPath)) {
throw new LocalizedResourceConfigException("warning.config.block.state.model.invalid_path", modelPath);
}
json.addProperty("model", modelPath);
if (singleModelMap.containsKey("x"))
json.addProperty("x", ResourceConfigUtils.getAsInt(singleModelMap.get("x"), "x"));
if (singleModelMap.containsKey("y"))
json.addProperty("y", ResourceConfigUtils.getAsInt(singleModelMap.get("y"), "y"));
if (singleModelMap.containsKey("uvlock")) json.addProperty("uvlock", ResourceConfigUtils.getAsBoolean(singleModelMap.get("uvlock"), "uvlock"));
if (singleModelMap.containsKey("weight"))
json.addProperty("weight", ResourceConfigUtils.getAsInt(singleModelMap.get("weight"), "weight"));
Map<String, Object> generationMap = MiscUtils.castToMap(singleModelMap.get("generation"), true);
if (generationMap != null) {
prepareModelGeneration(ModelGeneration.of(Key.of(modelPath), generationMap));
}
return json;
}
private int getVanillaBlockStateRegistryId(String blockState) {
String[] split = blockState.split(":", 3);
if (split.length >= 4) {
throw new LocalizedResourceConfigException("warning.config.block.state.invalid_vanilla", blockState);
}
int registryId;
String stateOrId = split[split.length - 1];
boolean isId = !stateOrId.contains("[") && !stateOrId.contains("]");
if (isId) {
if (split.length == 1) {
throw new LocalizedResourceConfigException("warning.config.block.state.invalid_vanilla", blockState);
}
Key block = split.length == 2 ? Key.of(split[0]) : Key.of(split[0], split[1]);
try {
int id = split.length == 2 ? Integer.parseInt(split[1]) : Integer.parseInt(split[2]);
if (id < 0) {
throw new LocalizedResourceConfigException("warning.config.block.state.invalid_vanilla", blockState);
}
List<Integer> arranger = this.blockAppearanceArranger.get(block);
if (arranger == null) {
throw new LocalizedResourceConfigException("warning.config.block.state.unavailable_vanilla", blockState);
}
if (id >= arranger.size()) {
throw new LocalizedResourceConfigException("warning.config.block.state.invalid_vanilla_id", blockState, String.valueOf(arranger.size() - 1));
}
registryId = arranger.get(id);
} catch (NumberFormatException e) {
throw new LocalizedResourceConfigException("warning.config.block.state.invalid_vanilla", e, blockState);
}
} else {
try {
BlockData blockData = Bukkit.createBlockData(blockState);
registryId = BlockStateUtils.blockDataToId(blockData);
if (!this.blockAppearanceMapper.containsKey(registryId)) {
throw new LocalizedResourceConfigException("warning.config.block.state.unavailable_vanilla", blockState);
}
} catch (IllegalArgumentException e) {
throw new LocalizedResourceConfigException("warning.config.block.state.invalid_vanilla", e, blockState);
}
}
return registryId;
}
private void loadMappingsAndAdditionalBlocks() {
this.plugin.logger().info("Loading mappings.yml.");
Path mappingsFile = this.plugin.dataFolderPath().resolve("mappings.yml");
@@ -757,13 +533,19 @@ public final class BukkitBlockManager extends AbstractBlockManager {
}
private LinkedHashMap<Key, Integer> buildRegisteredRealBlockSlots(Map<Key, Integer> counter, Map<String, Object> additionalYaml) {
LinkedHashMap<Key, Integer> map = new LinkedHashMap<>();
for (Map.Entry<Key, Integer> entry : counter.entrySet()) {
String id = entry.getKey().toString();
int additionalStates = (int) additionalYaml.getOrDefault(id, 0);
int internalIds = entry.getValue() + additionalStates;
plugin.logger().info("Loaded " + id + " with " + entry.getValue() + " appearances and " + internalIds + " real block states");
map.put(entry.getKey(), internalIds);
LinkedHashMap<Key, Integer> map = new LinkedHashMap<>(counter);
for (Map.Entry<String, Object> entry : additionalYaml.entrySet()) {
Key blockType = Key.of(entry.getKey());
if (entry.getValue() instanceof Integer i) {
int previous = map.getOrDefault(blockType, 0);
if (previous == 0) {
map.put(blockType, i);
this.plugin.logger().info("Loaded " + blockType + " with " + i + " real block states");
} else {
map.put(blockType, i + previous);
this.plugin.logger().info("Loaded " + blockType + " with " + previous + " appearances and " + (i + previous) + " real block states");
}
}
}
return map;
}
@@ -885,4 +667,21 @@ public final class BukkitBlockManager extends AbstractBlockManager {
}
}
}
@Override
protected int getBlockRegistryId(Key id) {
Object block = FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.BLOCK, KeyUtils.toResourceLocation(id));
return FastNMS.INSTANCE.method$IdMap$getId(MBuiltInRegistries.BLOCK, block).orElseThrow(() -> new IllegalStateException("Block " + id + " not found"));
}
@Override
protected boolean isVanillaBlock(Key id) {
if (!id.namespace().equals("minecraft")) {
return false;
}
if (id.value().equals("air")) {
return true;
}
return FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.BLOCK, KeyUtils.toResourceLocation(id)) != MBlocks.AIR;
}
}

View File

@@ -68,7 +68,7 @@ public class StackableBlockBehavior extends BukkitBlockBehavior {
private void updateStackableBlock(ImmutableBlockState state, BlockPos pos, World world, Item<ItemStack> item, Player player, InteractionHand hand) {
ImmutableBlockState nextStage = state.cycle(this.amountProperty);
Location location = new Location((org.bukkit.World) world.platformWorld(), pos.x(), pos.y(), pos.z());
FastNMS.INSTANCE.method$LevelWriter$setBlock(world.serverWorld(), LocationUtils.toBlockPos(pos), nextStage.customBlockState().handle(), UpdateOption.UPDATE_NONE.flags());
FastNMS.INSTANCE.method$LevelWriter$setBlock(world.serverWorld(), LocationUtils.toBlockPos(pos), nextStage.customBlockState().handle(), UpdateOption.UPDATE_ALL.flags());
if (this.stackSound != null) {
world.playBlockSound(new Vec3d(location.getX(), location.getY(), location.getZ()), this.stackSound);
}

View File

@@ -0,0 +1,26 @@
package net.momirealms.craftengine.bukkit.entity.data;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
import java.util.Optional;
public class AbstractMinecartData<T> extends VehicleEntityData<T> {
// 1.20~1.21.2
public static final AbstractMinecartData<Integer> DisplayBlock = of(AbstractMinecartData.class, EntityDataValue.Serializers$INT, BlockStateUtils.blockStateToId(MBlocks.AIR$defaultState), !VersionHelper.isOrAbove1_21_3());
// 1.21.3+
public static final AbstractMinecartData<Optional<Object>> CustomDisplayBlock = of(AbstractMinecartData.class, EntityDataValue.Serializers$OPTIONAL_BLOCK_STATE, Optional.empty(), VersionHelper.isOrAbove1_21_3());
public static final AbstractMinecartData<Integer> DisplayOffset = of(AbstractMinecartData.class, EntityDataValue.Serializers$INT, 6, true);
// 1.20~1.21.2
public static final AbstractMinecartData<Boolean> CustomDisplay = of(AbstractMinecartData.class, EntityDataValue.Serializers$BOOLEAN, false, !VersionHelper.isOrAbove1_21_3());
public static <T> AbstractMinecartData<T> of(final Class<?> clazz, final Object serializer, T defaultValue, boolean condition) {
if (!condition) return null;
return new AbstractMinecartData<>(clazz, serializer, defaultValue);
}
public AbstractMinecartData(Class<?> clazz, Object serializer, T defaultValue) {
super(clazz, serializer, defaultValue);
}
}

View File

@@ -0,0 +1,8 @@
package net.momirealms.craftengine.bukkit.entity.data;
public class BlockAttachedEntityData<T> extends BaseEntityData<T> {
public BlockAttachedEntityData(Class<?> clazz, Object serializer, T defaultValue) {
super(clazz, serializer, defaultValue);
}
}

View File

@@ -4,7 +4,7 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks;
public class BlockDisplayEntityData<T> extends DisplayEntityData<T> {
// Block display only
public static final DisplayEntityData<Object> DisplayedBlock = new BlockDisplayEntityData<>(BlockDisplayEntityData.class, EntityDataValue.Serializers$BLOCK_STATE, MBlocks.AIR$defaultState);
public static final BlockDisplayEntityData<Object> DisplayedBlock = new BlockDisplayEntityData<>(BlockDisplayEntityData.class, EntityDataValue.Serializers$BLOCK_STATE, MBlocks.AIR$defaultState);
public BlockDisplayEntityData(Class<?> clazz, Object serializer, T defaultValue) {
super(clazz, serializer, defaultValue);

View File

@@ -0,0 +1,13 @@
package net.momirealms.craftengine.bukkit.entity.data;
import java.util.Optional;
public class EnderManData<T> extends MonsterData<T> {
public static final EnderManData<Optional<Object>> CarryState = new EnderManData<>(EnderManData.class, EntityDataValue.Serializers$OPTIONAL_BLOCK_STATE, Optional.empty());
public static final EnderManData<Boolean> Creepy = new EnderManData<>(EnderManData.class, EntityDataValue.Serializers$BOOLEAN, false);
public static final EnderManData<Boolean> StaredAt = new EnderManData<>(EnderManData.class, EntityDataValue.Serializers$BOOLEAN, false);
public EnderManData(Class<?> clazz, Object serializer, T defaultValue) {
super(clazz, serializer, defaultValue);
}
}

View File

@@ -0,0 +1,8 @@
package net.momirealms.craftengine.bukkit.entity.data;
public class GlowItemFrameData<T> extends ItemFrameData<T> {
public GlowItemFrameData(Class<?> clazz, Object serializer, T defaultValue) {
super(clazz, serializer, defaultValue);
}
}

View File

@@ -0,0 +1,18 @@
package net.momirealms.craftengine.bukkit.entity.data;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.core.util.VersionHelper;
public class HangingEntityData<T> extends BlockAttachedEntityData<T> {
// 1.21.6+
public static final HangingEntityData<Object> Direction = of(HangingEntityData.class, EntityDataValue.Serializers$DIRECTION, CoreReflections.instance$Direction$SOUTH, VersionHelper.isOrAbove1_21_6());
public static <T> HangingEntityData<T> of(final Class<?> clazz, final Object serializer, T defaultValue, boolean condition) {
if (!condition) return null;
return new HangingEntityData<>(clazz, serializer, defaultValue);
}
public HangingEntityData(Class<?> clazz, Object serializer, T defaultValue) {
super(clazz, serializer, defaultValue);
}
}

View File

@@ -4,7 +4,7 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflect
public class ItemDisplayEntityData<T> extends DisplayEntityData<T> {
// Item display only
public static final DisplayEntityData<Object> DisplayedItem = new ItemDisplayEntityData<>(ItemDisplayEntityData.class, EntityDataValue.Serializers$ITEM_STACK, CoreReflections.instance$ItemStack$EMPTY);
public static final ItemDisplayEntityData<Object> DisplayedItem = new ItemDisplayEntityData<>(ItemDisplayEntityData.class, EntityDataValue.Serializers$ITEM_STACK, CoreReflections.instance$ItemStack$EMPTY);
/**
* Display type:
* 0 = NONE
@@ -17,7 +17,7 @@ public class ItemDisplayEntityData<T> extends DisplayEntityData<T> {
* 7 = GROUND
* 8 = FIXED
*/
public static final DisplayEntityData<Byte> DisplayType = new ItemDisplayEntityData<>(ItemDisplayEntityData.class, EntityDataValue.Serializers$BYTE, (byte) 0);
public static final ItemDisplayEntityData<Byte> DisplayType = new ItemDisplayEntityData<>(ItemDisplayEntityData.class, EntityDataValue.Serializers$BYTE, (byte) 0);
public ItemDisplayEntityData(Class<?> clazz, Object serializer, T defaultValue) {
super(clazz, serializer, defaultValue);

View File

@@ -0,0 +1,12 @@
package net.momirealms.craftengine.bukkit.entity.data;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
public class ItemFrameData<T> extends HangingEntityData<T> {
public static final ItemFrameData<Object> Item = new ItemFrameData<>(ItemFrameData.class, EntityDataValue.Serializers$ITEM_STACK, CoreReflections.instance$ItemStack$EMPTY);
public static final ItemFrameData<Integer> Rotation = new ItemFrameData<>(ItemFrameData.class, EntityDataValue.Serializers$INT, 0);
public ItemFrameData(Class<?> clazz, Object serializer, T defaultValue) {
super(clazz, serializer, defaultValue);
}
}

View File

@@ -0,0 +1,8 @@
package net.momirealms.craftengine.bukkit.entity.data;
public class MonsterData<T> extends PathfinderMobData<T> {
public MonsterData(Class<?> clazz, Object serializer, T defaultValue) {
super(clazz, serializer, defaultValue);
}
}

View File

@@ -0,0 +1,19 @@
package net.momirealms.craftengine.bukkit.entity.data;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks;
import net.momirealms.craftengine.core.util.VersionHelper;
public class PrimedTntData<T> extends BaseEntityData<T> {
public static final PrimedTntData<Integer> Fuse = of(PrimedTntData.class, EntityDataValue.Serializers$INT, 80, true);
// 1.20.3+
public static final PrimedTntData<Object> BlockState = of(PrimedTntData.class, EntityDataValue.Serializers$BLOCK_STATE, MBlocks.TNT$defaultState, VersionHelper.isOrAbove1_20_3());
public static <T> PrimedTntData<T> of(final Class<?> clazz, final Object serializer, T defaultValue, boolean condition) {
if (!condition) return null;
return new PrimedTntData<>(clazz, serializer, defaultValue);
}
public PrimedTntData(Class<?> clazz, Object serializer, T defaultValue) {
super(clazz, serializer, defaultValue);
}
}

View File

@@ -3,11 +3,11 @@ package net.momirealms.craftengine.bukkit.entity.data;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
public class TextDisplayEntityData<T> extends DisplayEntityData<T> {
public static final DisplayEntityData<Object> Text = new TextDisplayEntityData<>(TextDisplayEntityData.class, EntityDataValue.Serializers$COMPONENT, CoreReflections.instance$Component$empty);
public static final DisplayEntityData<Integer> LineWidth = new TextDisplayEntityData<>(TextDisplayEntityData.class, EntityDataValue.Serializers$INT, 200);
public static final DisplayEntityData<Integer> BackgroundColor = new TextDisplayEntityData<>(TextDisplayEntityData.class, EntityDataValue.Serializers$INT, 0x40000000);
public static final DisplayEntityData<Byte> TextOpacity = new TextDisplayEntityData<>(TextDisplayEntityData.class, EntityDataValue.Serializers$BYTE, (byte) -1);
public static final DisplayEntityData<Byte> TextDisplayMasks = new TextDisplayEntityData<>(TextDisplayEntityData.class, EntityDataValue.Serializers$BYTE, (byte) 0);
public static final TextDisplayEntityData<Object> Text = new TextDisplayEntityData<>(TextDisplayEntityData.class, EntityDataValue.Serializers$COMPONENT, CoreReflections.instance$Component$empty);
public static final TextDisplayEntityData<Integer> LineWidth = new TextDisplayEntityData<>(TextDisplayEntityData.class, EntityDataValue.Serializers$INT, 200);
public static final TextDisplayEntityData<Integer> BackgroundColor = new TextDisplayEntityData<>(TextDisplayEntityData.class, EntityDataValue.Serializers$INT, 0x40000000);
public static final TextDisplayEntityData<Byte> TextOpacity = new TextDisplayEntityData<>(TextDisplayEntityData.class, EntityDataValue.Serializers$BYTE, (byte) -1);
public static final TextDisplayEntityData<Byte> TextDisplayMasks = new TextDisplayEntityData<>(TextDisplayEntityData.class, EntityDataValue.Serializers$BYTE, (byte) 0);
public TextDisplayEntityData(Class<?> clazz, Object serializer, T defaultValue) {
super(clazz, serializer, defaultValue);

View File

@@ -0,0 +1,11 @@
package net.momirealms.craftengine.bukkit.entity.data;
public class VehicleEntityData<T> extends BaseEntityData<T> {
public static final VehicleEntityData<Integer> Hurt = new VehicleEntityData<>(VehicleEntityData.class, EntityDataValue.Serializers$INT, 0);
public static final VehicleEntityData<Integer> HurtDir = new VehicleEntityData<>(VehicleEntityData.class, EntityDataValue.Serializers$INT, 1);
public static final VehicleEntityData<Float> Damage = new VehicleEntityData<>(VehicleEntityData.class, EntityDataValue.Serializers$FLOAT, 0.0F);
public VehicleEntityData(Class<?> clazz, Object serializer, T defaultValue) {
super(clazz, serializer, defaultValue);
}
}

View File

@@ -1,11 +1,13 @@
package net.momirealms.craftengine.bukkit.entity.furniture;
import net.momirealms.craftengine.bukkit.api.BukkitAdaptors;
import net.momirealms.craftengine.bukkit.entity.furniture.hitbox.InteractionHitBox;
import net.momirealms.craftengine.bukkit.nms.CollisionEntity;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.network.handler.FurniturePacketHandler;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes;
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
import net.momirealms.craftengine.bukkit.util.EntityUtils;
import net.momirealms.craftengine.bukkit.util.KeyUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
@@ -207,15 +209,16 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
BukkitFurniture furniture = addNewFurniture(display, customFurniture);
furniture.initializeColliders();
for (Player player : display.getTrackedPlayers()) {
this.plugin.adapt(player).entityPacketHandlers().computeIfAbsent(furniture.baseEntityId(), k -> new FurniturePacketHandler(furniture.fakeEntityIds()));
this.plugin.networkManager().sendPacket(this.plugin.adapt(player), furniture.spawnPacket(player));
BukkitAdaptors.adapt(player).entityPacketHandlers().computeIfAbsent(furniture.baseEntityId(), k -> new FurniturePacketHandler(furniture.fakeEntityIds()));
this.plugin.networkManager().sendPacket(BukkitAdaptors.adapt(player), furniture.spawnPacket(player));
}
}
} else {
BukkitFurniture furniture = addNewFurniture(display, customFurniture);
for (Player player : display.getTrackedPlayers()) {
this.plugin.adapt(player).entityPacketHandlers().computeIfAbsent(furniture.baseEntityId(), k -> new FurniturePacketHandler(furniture.fakeEntityIds()));
this.plugin.networkManager().sendPacket(this.plugin.adapt(player), furniture.spawnPacket(player));
BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player);
serverPlayer.entityPacketHandlers().computeIfAbsent(furniture.baseEntityId(), k -> new FurniturePacketHandler(furniture.fakeEntityIds()));
this.plugin.networkManager().sendPacket(serverPlayer, furniture.spawnPacket(player));
}
if (preventChange) {
this.plugin.scheduler().sync().runLater(furniture::initializeColliders, 1, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4);

View File

@@ -5,6 +5,7 @@ import com.google.gson.JsonObject;
import io.papermc.paper.event.player.AsyncChatCommandDecorateEvent;
import io.papermc.paper.event.player.AsyncChatDecorateEvent;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.bukkit.api.BukkitAdaptors;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.reflection.paper.PaperReflections;
import net.momirealms.craftengine.bukkit.util.ComponentUtils;
@@ -143,7 +144,7 @@ public class BukkitFontManager extends AbstractFontManager implements Listener {
if (renameText == null || renameText.isEmpty()) return;
Component itemName = Component.text(renameText);
EmojiComponentProcessResult replaceProcessResult = replaceComponentEmoji(itemName, this.plugin.adapt(player), renameText);
EmojiComponentProcessResult replaceProcessResult = replaceComponentEmoji(itemName, BukkitAdaptors.adapt(player), renameText);
if (replaceProcessResult.changed()) {
Item<ItemStack> wrapped = this.plugin.itemManager().wrap(result);
wrapped.customNameJson(AdventureHelper.componentToJson(replaceProcessResult.newText()));
@@ -160,7 +161,7 @@ public class BukkitFontManager extends AbstractFontManager implements Listener {
JsonElement json = ComponentUtils.paperAdventureToJsonElement(lines.get(i));
if (json == null) continue;
Component line = AdventureHelper.jsonElementToComponent(json);
EmojiComponentProcessResult result = replaceComponentEmoji(line, plugin.adapt(player));
EmojiComponentProcessResult result = replaceComponentEmoji(line, BukkitAdaptors.adapt(player));
if (result.changed()) {
try {
PaperReflections.method$SignChangeEvent$line.invoke(event, i, ComponentUtils.jsonElementToPaperAdventure(AdventureHelper.componentToJsonElement(result.newText())));
@@ -191,7 +192,7 @@ public class BukkitFontManager extends AbstractFontManager implements Listener {
for (int i = 0; i < pages.size(); i++) {
JsonElement json = ComponentUtils.paperAdventureToJsonElement(pages.get(i));
Component page = AdventureHelper.jsonElementToComponent(json);
EmojiComponentProcessResult result = replaceComponentEmoji(page, plugin.adapt(player));
EmojiComponentProcessResult result = replaceComponentEmoji(page, BukkitAdaptors.adapt(player));
if (result.changed()) {
changed = true;
try {
@@ -214,7 +215,7 @@ public class BukkitFontManager extends AbstractFontManager implements Listener {
Object originalMessage = PaperReflections.field$AsyncChatDecorateEvent$originalMessage.get(event);
String rawJsonMessage = ComponentUtils.paperAdventureToJson(originalMessage);
if (Config.allowEmojiChat()) {
EmojiTextProcessResult processResult = replaceJsonEmoji(rawJsonMessage, this.plugin.adapt(player));
EmojiTextProcessResult processResult = replaceJsonEmoji(rawJsonMessage, BukkitAdaptors.adapt(player));
boolean hasChanged = processResult.replaced();
if (!player.hasPermission(FontManager.BYPASS_CHAT)) {
IllegalCharacterProcessResult result = processIllegalCharacters(processResult.text());

View File

@@ -5,6 +5,7 @@ import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.core.item.*;
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
import net.momirealms.craftengine.core.item.modifier.ItemDataModifier;
import net.momirealms.craftengine.core.item.updater.ItemUpdateConfig;
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
import net.momirealms.craftengine.core.plugin.context.event.EventTrigger;
import net.momirealms.craftengine.core.plugin.context.function.Function;
@@ -25,8 +26,9 @@ public class BukkitCustomItem extends AbstractCustomItem<ItemStack> {
List<ItemBehavior> behaviors,
List<ItemDataModifier<ItemStack>> modifiers, List<ItemDataModifier<ItemStack>> clientBoundModifiers,
ItemSettings settings,
Map<EventTrigger, List<Function<PlayerOptionalContext>>> events) {
super(isVanillaItem, id, materialKey, clientBoundMaterialKey, behaviors, modifiers, clientBoundModifiers, settings, events);
Map<EventTrigger, List<Function<PlayerOptionalContext>>> events,
ItemUpdateConfig updater) {
super(isVanillaItem, id, materialKey, clientBoundMaterialKey, behaviors, modifiers, clientBoundModifiers, settings, events, updater);
this.item = item;
this.clientItem = clientItem;
}
@@ -75,6 +77,7 @@ public class BukkitCustomItem extends AbstractCustomItem<ItemStack> {
private final List<ItemDataModifier<ItemStack>> modifiers = new ArrayList<>(4);
private final List<ItemDataModifier<ItemStack>> clientBoundModifiers = new ArrayList<>(4);
private ItemSettings settings;
private ItemUpdateConfig updater;
public BuilderImpl(Object item, Object clientBoundItem) {
this.item = item;
@@ -153,12 +156,18 @@ public class BukkitCustomItem extends AbstractCustomItem<ItemStack> {
return this;
}
@Override
public Builder<ItemStack> updater(ItemUpdateConfig updater) {
this.updater = updater;
return this;
}
@Override
public CustomItem<ItemStack> build() {
this.modifiers.addAll(this.settings.modifiers());
this.clientBoundModifiers.addAll(this.settings.clientBoundModifiers());
return new BukkitCustomItem(this.isVanillaItem, this.id, this.item, this.clientBoundItem, this.itemKey, this.clientBoundItemKey, List.copyOf(this.behaviors),
List.copyOf(this.modifiers), List.copyOf(this.clientBoundModifiers), this.settings, this.events);
List.copyOf(this.modifiers), List.copyOf(this.clientBoundModifiers), this.settings, this.events, updater);
}
}
}

View File

@@ -62,7 +62,7 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
instance = this;
this.plugin = plugin;
this.factory = BukkitItemFactory.create(plugin);
this.itemEventListener = new ItemEventListener(plugin);
this.itemEventListener = new ItemEventListener(plugin, this);
this.debugStickListener = new DebugStickListener(plugin);
this.armorEventListener = new ArmorEventListener();
this.networkItemHandler = VersionHelper.isOrAbove1_20_5() ? new ModernNetworkItemHandler() : new LegacyNetworkItemHandler();
@@ -335,7 +335,7 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
@Override
public ItemStack buildCustomItemStack(Key id, Player player) {
return Optional.ofNullable(this.customItems.get(id)).map(it -> it.buildItemStack(new ItemBuildContext(player, ContextHolder.EMPTY), 1)).orElse(null);
return Optional.ofNullable(this.customItemsById.get(id)).map(it -> it.buildItemStack(new ItemBuildContext(player, ContextHolder.EMPTY), 1)).orElse(null);
}
@Override
@@ -349,12 +349,12 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
@Override
public Item<ItemStack> createCustomWrappedItem(Key id, Player player) {
return Optional.ofNullable(customItems.get(id)).map(it -> it.buildItem(player)).orElse(null);
return Optional.ofNullable(customItemsById.get(id)).map(it -> it.buildItem(player)).orElse(null);
}
@Override
public Item<ItemStack> createWrappedItem(Key id, @Nullable Player player) {
CustomItem<ItemStack> customItem = this.customItems.get(id);
CustomItem<ItemStack> customItem = this.customItemsById.get(id);
if (customItem != null) {
return customItem.buildItem(player);
}
@@ -365,9 +365,10 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
return null;
}
@Nullable
private ItemStack createVanillaItemStack(Key id) {
Object item = FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.ITEM, KeyUtils.toResourceLocation(id));
if (item == null) {
if (item == MItems.AIR && !id.equals(ItemKeys.AIR)) {
return null;
}
return FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(FastNMS.INSTANCE.constructor$ItemStack(item, 1));

View File

@@ -12,9 +12,11 @@ import net.momirealms.craftengine.bukkit.util.ItemStackUtils;
import net.momirealms.craftengine.bukkit.util.KeyUtils;
import net.momirealms.craftengine.core.item.ItemWrapper;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.sparrow.nbt.Tag;
import org.bukkit.inventory.ItemStack;
import java.util.Objects;
import java.util.Optional;
public class ComponentItemWrapper implements ItemWrapper<ItemStack> {
@@ -104,6 +106,18 @@ public class ComponentItemWrapper implements ItemWrapper<ItemStack> {
return FastNMS.INSTANCE.method$ItemStack$hasComponent(getLiteralObject(), ensureDataComponentType(type));
}
public boolean hasNonDefaultComponent(Object type) {
if (VersionHelper.isOrAbove1_21_4()) {
return FastNMS.INSTANCE.method$ItemStack$hasNonDefaultComponent(getLiteralObject(), ensureDataComponentType(type));
} else {
Object item = FastNMS.INSTANCE.method$ItemStack$getItem(this.getLiteralObject());
Object componentMap = FastNMS.INSTANCE.method$Item$components(item);
Object componentType = ensureDataComponentType(type);
Object defaultComponent = FastNMS.INSTANCE.method$DataComponentMap$get(componentMap, componentType);
return !Objects.equals(defaultComponent, getComponentExact(componentType));
}
}
public void setComponentExact(Object type, final Object value) {
FastNMS.INSTANCE.method$ItemStack$setComponent(this.getLiteralObject(), ensureDataComponentType(type), value);
}

View File

@@ -165,6 +165,11 @@ public abstract class BukkitItemFactory<W extends ItemWrapper<ItemStack>> extend
throw new UnsupportedOperationException("This feature is only available on 1.20.5+");
}
@Override
protected boolean hasNonDefaultComponent(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+");

View File

@@ -267,6 +267,11 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
return item.hasComponent(type);
}
@Override
protected boolean hasNonDefaultComponent(ComponentItemWrapper item, Object type) {
return item.hasNonDefaultComponent(type);
}
@Override
protected void removeComponent(ComponentItemWrapper item, Object type) {
item.removeComponent(type);
@@ -346,9 +351,9 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
@Override
protected void unbreakable(ComponentItemWrapper item, boolean unbreakable) {
if (unbreakable) {
item.resetComponent(ComponentTypes.UNBREAKABLE);
item.setJavaComponent(ComponentTypes.UNBREAKABLE, Map.of());
} else {
item.setJavaComponent(ComponentTypes.UNBREAKABLE, true);
item.resetComponent(ComponentTypes.UNBREAKABLE);
}
}

View File

@@ -1,6 +1,7 @@
package net.momirealms.craftengine.bukkit.item.listener;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.bukkit.api.BukkitAdaptors;
import net.momirealms.craftengine.bukkit.api.CraftEngineBlocks;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
@@ -49,7 +50,7 @@ public class DebugStickListener implements Listener {
Material material = itemInHand.getType();
if (material != Material.DEBUG_STICK) return;
Player bukkitPlayer = event.getPlayer();
BukkitServerPlayer player = this.plugin.adapt(bukkitPlayer);
BukkitServerPlayer player = BukkitAdaptors.adapt(bukkitPlayer);
if (!(player.canInstabuild() && player.hasPermission("minecraft.debugstick")) && !player.hasPermission("minecraft.debugstick.always")) {
return;
}

View File

@@ -1,8 +1,10 @@
package net.momirealms.craftengine.bukkit.item.listener;
import io.papermc.paper.event.block.CompostItemEvent;
import net.momirealms.craftengine.bukkit.api.BukkitAdaptors;
import net.momirealms.craftengine.bukkit.api.event.CustomBlockInteractEvent;
import net.momirealms.craftengine.bukkit.item.BukkitCustomItem;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
@@ -16,9 +18,11 @@ import net.momirealms.craftengine.core.entity.player.InteractionHand;
import net.momirealms.craftengine.core.entity.player.InteractionResult;
import net.momirealms.craftengine.core.item.CustomItem;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.item.ItemBuildContext;
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
import net.momirealms.craftengine.core.item.context.UseOnContext;
import net.momirealms.craftengine.core.item.setting.FoodData;
import net.momirealms.craftengine.core.item.updater.ItemUpdateResult;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
@@ -44,13 +48,16 @@ import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.enchantment.PrepareItemEnchantEvent;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.EntityPickupItemEvent;
import org.bukkit.event.entity.FoodLevelChangeEvent;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.player.PlayerDropItemEvent;
import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerItemConsumeEvent;
import org.bukkit.inventory.EnchantingInventory;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import java.util.ArrayList;
@@ -60,16 +67,18 @@ import java.util.Optional;
public class ItemEventListener implements Listener {
private final BukkitCraftEngine plugin;
private final BukkitItemManager itemManager;
public ItemEventListener(BukkitCraftEngine plugin) {
public ItemEventListener(BukkitCraftEngine plugin, BukkitItemManager itemManager) {
this.plugin = plugin;
this.itemManager = itemManager;
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onInteractEntity(PlayerInteractEntityEvent event) {
Player player = event.getPlayer();
Entity entity = event.getRightClicked();
BukkitServerPlayer serverPlayer = this.plugin.adapt(player);
BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player);
if (serverPlayer == null) return;
InteractionHand hand = event.getHand() == EquipmentSlot.HAND ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND;
@@ -107,7 +116,7 @@ public class ItemEventListener implements Listener {
return;
}
BukkitServerPlayer serverPlayer = this.plugin.adapt(player);
BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player);
if (serverPlayer == null) return;
InteractionHand hand = event.getHand() == EquipmentSlot.HAND ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND;
// 如果本tick内主手已被处理则不处理副手
@@ -349,7 +358,7 @@ public class ItemEventListener implements Listener {
if (action != Action.RIGHT_CLICK_AIR && action != Action.LEFT_CLICK_AIR)
return;
Player player = event.getPlayer();
BukkitServerPlayer serverPlayer = this.plugin.adapt(player);
BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player);
if (serverPlayer.isSpectatorMode())
return;
// Gets the item in hand
@@ -411,7 +420,7 @@ public class ItemEventListener implements Listener {
}
Cancellable cancellable = Cancellable.of(event::isCancelled, event::setCancelled);
CustomItem<ItemStack> customItem = optionalCustomItem.get();
PlayerOptionalContext context = PlayerOptionalContext.of(this.plugin.adapt(event.getPlayer()), ContextHolder.builder()
PlayerOptionalContext context = PlayerOptionalContext.of(BukkitAdaptors.adapt(event.getPlayer()), ContextHolder.builder()
.withParameter(DirectContextParameters.ITEM_IN_HAND, wrapped)
.withParameter(DirectContextParameters.EVENT, cancellable)
.withParameter(DirectContextParameters.HAND, event.getHand() == EquipmentSlot.HAND ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND)
@@ -425,7 +434,7 @@ public class ItemEventListener implements Listener {
if (replacement == null) {
event.setReplacement(null);
} else {
ItemStack replacementItem = this.plugin.itemManager().buildItemStack(replacement, this.plugin.adapt(event.getPlayer()));
ItemStack replacementItem = this.plugin.itemManager().buildItemStack(replacement, BukkitAdaptors.adapt(event.getPlayer()));
event.setReplacement(replacementItem);
}
}
@@ -516,7 +525,7 @@ public class ItemEventListener implements Listener {
if (optionalCustomItem.isEmpty()) return;
BukkitCustomItem customItem = (BukkitCustomItem) optionalCustomItem.get();
if (customItem.clientItem() == FastNMS.INSTANCE.method$ItemStack$getItem(wrapped.getLiteralObject())) return;
BukkitServerPlayer serverPlayer = this.plugin.adapt(player);
BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player);
if (serverPlayer == null) return;
this.plugin.scheduler().sync().runDelayed(() -> {
Object container = FastNMS.INSTANCE.field$Player$containerMenu(serverPlayer.serverPlayer());
@@ -533,4 +542,50 @@ public class ItemEventListener implements Listener {
serverPlayer.sendPackets(packets, false);
});
}
/*
关于物品更新器
*/
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH)
public void onDropItem(PlayerDropItemEvent event) {
if (!Config.triggerUpdateDrop()) return;
org.bukkit.entity.Item itemDrop = event.getItemDrop();
ItemStack itemStack = itemDrop.getItemStack();
Item<ItemStack> wrapped = this.itemManager.wrap(itemStack);
ItemUpdateResult result = this.itemManager.updateItem(wrapped, () -> ItemBuildContext.of(BukkitAdaptors.adapt(event.getPlayer())));
if (result.updated()) {
itemDrop.setItemStack((ItemStack) result.finalItem().getItem());
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH)
public void onPickUpItem(EntityPickupItemEvent event) {
if (!Config.triggerUpdatePickUp()) return;
if (!(event.getEntity() instanceof Player player)) return;
org.bukkit.entity.Item itemDrop = event.getItem();
ItemStack itemStack = itemDrop.getItemStack();
Item<ItemStack> wrapped = this.itemManager.wrap(itemStack);
ItemUpdateResult result = this.itemManager.updateItem(wrapped, () -> ItemBuildContext.of(BukkitAdaptors.adapt(player)));
if (result.updated()) {
itemDrop.setItemStack((ItemStack) result.finalItem().getItem());
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOW)
public void onInventoryClickItem(InventoryClickEvent event) {
if (!Config.triggerUpdateClick()) return;
if (!(event.getWhoClicked() instanceof Player player)) return;
Inventory clickedInventory = event.getClickedInventory();
// 点击自己物品栏里的物品
if (clickedInventory == null || clickedInventory != player.getInventory()) return;
ItemStack currentItem = event.getCurrentItem();
Item<ItemStack> wrapped = this.itemManager.wrap(currentItem);
ItemUpdateResult result = this.itemManager.updateItem(wrapped, () -> ItemBuildContext.of(BukkitAdaptors.adapt(player)));
if (!result.updated() || !result.replaced()) {
return;
}
event.setCurrentItem((ItemStack) result.finalItem().getItem());
}
}

View File

@@ -1,10 +0,0 @@
package net.momirealms.craftengine.bukkit.item.recipe;
import net.momirealms.craftengine.core.item.recipe.Recipe;
import net.momirealms.craftengine.core.util.Key;
import org.bukkit.inventory.ItemStack;
public interface BukkitRecipeConvertor<T extends Recipe<ItemStack>> {
Runnable convert(Key id, T recipe);
}

View File

@@ -3,6 +3,7 @@ package net.momirealms.craftengine.bukkit.item.recipe;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import io.papermc.paper.potion.PotionMix;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
@@ -144,11 +145,11 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
.map(Optional::get)
.toList();
if (VersionHelper.isOrAbove1_21_2()) {
CoreReflections.field$ShapedRecipe$placementInfo.set(shapedRecipe, null);
CoreReflections.methodHandle$ShapedRecipe$placementInfoSetter.invokeExact(shapedRecipe, (Object) null);
}
List<Object> ingredients = getIngredientsFromShapedRecipe(shapedRecipe);
modifyIngredients(ingredients, actualIngredients);
} catch (ReflectiveOperationException e) {
} catch (Throwable e) {
CraftEngine.instance().logger().warn("Failed to inject shaped recipe", e);
}
}
@@ -158,16 +159,16 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
List<Object> ingredients = new ArrayList<>();
try {
if (VersionHelper.isOrAbove1_20_3()) {
Object pattern = CoreReflections.field$1_20_3$ShapedRecipe$pattern.get(recipe);
Object pattern = CoreReflections.methodHandle$1_20_3$ShapedRecipe$patternGetter.invokeExact(recipe);
if (VersionHelper.isOrAbove1_21_2()) {
List<Optional<Object>> optionals = (List<Optional<Object>>) CoreReflections.field$ShapedRecipePattern$ingredients1_21_2.get(pattern);
List<Optional<Object>> optionals = (List<Optional<Object>>) CoreReflections.methodHandle$ShapedRecipePattern$ingredients1_21_2Getter.invokeExact(pattern);
for (Optional<Object> optional : optionals) {
optional.ifPresent(ingredients::add);
}
} else {
List<Object> objectList = (List<Object>) CoreReflections.field$ShapedRecipePattern$ingredients1_20_3.get(pattern);
List<Object> objectList = (List<Object>) CoreReflections.methodHandle$ShapedRecipePattern$ingredients1_20_3Getter.invokeExact(pattern);
for (Object object : objectList) {
Object[] values = (Object[]) CoreReflections.field$Ingredient$values.get(object);
Object[] values = (Object[]) CoreReflections.methodHandle$Ingredient$valuesGetter.invokeExact(object);
// is empty or not
if (values.length != 0) {
ingredients.add(object);
@@ -175,15 +176,15 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
}
}
} else {
List<Object> objectList = (List<Object>) CoreReflections.field$1_20_1$ShapedRecipe$recipeItems.get(recipe);
List<Object> objectList = (List<Object>) CoreReflections.methodHandle$1_20_1$ShapedRecipe$recipeItemsGetter.invokeExact(recipe);
for (Object object : objectList) {
Object[] values = (Object[]) CoreReflections.field$Ingredient$values.get(object);
Object[] values = (Object[]) CoreReflections.methodHandle$Ingredient$valuesGetter.invokeExact(object);
if (values.length != 0) {
ingredients.add(object);
}
}
}
} catch (ReflectiveOperationException e) {
} catch (Throwable e) {
CraftEngine.instance().logger().warn("Failed to get ingredients from shaped recipe", e);
}
return ingredients;
@@ -193,12 +194,12 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
try {
List<Ingredient<ItemStack>> actualIngredients = recipe.ingredientsInUse();
if (VersionHelper.isOrAbove1_21_2()) {
CoreReflections.field$ShapelessRecipe$placementInfo.set(shapelessRecipe, null);
CoreReflections.methodHandle$ShapelessRecipe$placementInfoSetter.invokeExact(shapelessRecipe, (Object) null);
}
@SuppressWarnings("unchecked")
List<Object> ingredients = (List<Object>) CoreReflections.field$ShapelessRecipe$ingredients.get(shapelessRecipe);
List<Object> ingredients = (List<Object>) CoreReflections.methodHandle$ShapelessRecipe$ingredientsGetter.invokeExact(shapelessRecipe);
modifyIngredients(ingredients, actualIngredients);
} catch (ReflectiveOperationException e) {
} catch (Throwable e) {
CraftEngine.instance().logger().warn("Failed to inject shapeless recipe", e);
}
}
@@ -208,12 +209,12 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
Ingredient<ItemStack> actualIngredient = recipe.ingredient();
Object ingredient;
if (VersionHelper.isOrAbove1_21_2()) {
ingredient = CoreReflections.field$SingleItemRecipe$input.get(cookingRecipe);
ingredient = CoreReflections.methodHandle$SingleItemRecipe$inputGetter.invokeExact(cookingRecipe);
} else {
ingredient = CoreReflections.field$AbstractCookingRecipe$input.get(cookingRecipe);
ingredient = CoreReflections.methodHandle$AbstractCookingRecipe$inputGetter.invokeExact(cookingRecipe);
}
modifyIngredients(List.of(ingredient), List.of(actualIngredient));
} catch (ReflectiveOperationException e) {
} catch (Throwable e) {
CraftEngine.instance().logger().warn("Failed to inject cooking recipe", e);
}
}
@@ -235,7 +236,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
return itemStacks;
}
private static void modifyIngredients(List<Object> fakeIngredients, List<Ingredient<ItemStack>> actualIngredients) throws ReflectiveOperationException {
private static void modifyIngredients(List<Object> fakeIngredients, List<Ingredient<ItemStack>> actualIngredients) throws Throwable {
if (fakeIngredients.size() != actualIngredients.size()) {
throw new IllegalArgumentException("Ingredient count mismatch");
}
@@ -244,15 +245,15 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
Ingredient<ItemStack> actualIngredient = actualIngredients.get(i);
List<Object> items = getIngredientLooks(actualIngredient.items());
if (VersionHelper.isOrAbove1_21_4()) {
CoreReflections.field$Ingredient$itemStacks1_21_4.set(ingredient, new HashSet<>(items));
CoreReflections.methodHandle$Ingredient$itemStacksSetter.invokeExact(ingredient, (Set<Object>) new ObjectOpenHashSet<>(items));
} else if (VersionHelper.isOrAbove1_21_2()) {
CoreReflections.field$Ingredient$itemStacks1_21_2.set(ingredient, items);
CoreReflections.methodHandle$Ingredient$itemStacksSetter.invokeExact(ingredient, (List<Object>) items);
} else {
Object itemStackArray = Array.newInstance(CoreReflections.clazz$ItemStack, items.size());
for (int j = 0; j < items.size(); j++) {
Array.set(itemStackArray, j, items.get(j));
}
CoreReflections.field$Ingredient$itemStacks1_20_1.set(ingredient, itemStackArray);
CoreReflections.methodHandle$Ingredient$itemStacksSetter.invokeExact(ingredient, (Object) itemStackArray);
}
MODIFIED_INGREDIENTS.add(ingredient);
}
@@ -308,9 +309,9 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
if (!Config.enableRecipeSystem()) return;
if (VersionHelper.isOrAbove1_21_2()) {
try {
this.stolenFeatureFlagSet = CoreReflections.field$RecipeManager$featureflagset.get(minecraftRecipeManager());
CoreReflections.field$RecipeManager$featureflagset.set(minecraftRecipeManager(), null);
} catch (ReflectiveOperationException e) {
this.stolenFeatureFlagSet = CoreReflections.methodHandle$RecipeManager$featureflagsetGetter.invokeExact(minecraftRecipeManager());
CoreReflections.methodHandle$RecipeManager$featureflagsetSetter.invokeExact(minecraftRecipeManager(), (Object) null);
} catch (Throwable e) {
this.plugin.logger().warn("Failed to steal feature flag set", e);
}
}
@@ -398,41 +399,44 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
this.replacedDatapackRecipes.clear();
try {
this.lastDatapackRecipes = scanResources();
} catch (ReflectiveOperationException e) {
} catch (Throwable e) {
this.plugin.logger().warn("Failed to load datapack recipes", e);
}
}
if (Config.disableAllVanillaRecipes()) {
this.recipesToUnregister.addAll(this.lastDatapackRecipes.keySet().stream().map(it -> Pair.of(it, false)).toList());
return;
}
boolean hasDisabledAny = !Config.disabledVanillaRecipes().isEmpty();
for (Map.Entry<Key, Recipe<ItemStack>> entry : this.lastDatapackRecipes.entrySet()) {
if (hasDisabledAny && Config.disabledVanillaRecipes().contains(entry.getKey())) {
this.recipesToUnregister.add(Pair.of(entry.getKey(), false));
continue;
}
markAsDataPackRecipe(entry.getKey());
registerInternalRecipe(entry.getKey(), entry.getValue());
}
}
@SuppressWarnings("unchecked")
private Map<Key, Recipe<ItemStack>> scanResources() throws ReflectiveOperationException {
Object fileToIdConverter = CoreReflections.method$FileToIdConverter$json.invoke(null, VersionHelper.isOrAbove1_21() ? "recipe" : "recipes");
private Map<Key, Recipe<ItemStack>> scanResources() throws Throwable {
Object fileToIdConverter = CoreReflections.methodHandle$FileToIdConverter$json.invokeExact((String) (VersionHelper.isOrAbove1_21() ? "recipe" : "recipes"));
Object minecraftServer = FastNMS.INSTANCE.method$MinecraftServer$getServer();
Object packRepository = CoreReflections.method$MinecraftServer$getPackRepository.invoke(minecraftServer);
List<Object> selected = (List<Object>) CoreReflections.field$PackRepository$selected.get(packRepository);
Object packRepository = CoreReflections.methodHandle$MinecraftServer$getPackRepository.invokeExact(minecraftServer);
List<Object> selected = (List<Object>) CoreReflections.methodHandle$PackRepository$selectedGetter.invokeExact(packRepository);
List<Object> packResources = new ArrayList<>();
for (Object pack : selected) {
packResources.add(CoreReflections.method$Pack$open.invoke(pack));
packResources.add(CoreReflections.methodHandle$Pack$open.invokeExact(pack));
}
Map<Key, Recipe<ItemStack>> recipes = new HashMap<>();
boolean hasDisabledAny = !Config.disabledVanillaRecipes().isEmpty();
try (AutoCloseable resourceManager = (AutoCloseable) CoreReflections.constructor$MultiPackResourceManager.newInstance(CoreReflections.instance$PackType$SERVER_DATA, packResources)) {
Map<Object, Object> scannedResources = (Map<Object, Object>) CoreReflections.method$FileToIdConverter$listMatchingResources.invoke(fileToIdConverter, resourceManager);
try (AutoCloseable resourceManager = (AutoCloseable) CoreReflections.methodHandle$MultiPackResourceManagerConstructor.invokeExact(CoreReflections.instance$PackType$SERVER_DATA, packResources)) {
Map<Object, Object> scannedResources = (Map<Object, Object>) CoreReflections.methodHandle$FileToIdConverter$listMatchingResources.invokeExact(fileToIdConverter, resourceManager);
for (Map.Entry<Object, Object> entry : scannedResources.entrySet()) {
Key id = extractKeyFromResourceLocation(entry.getKey().toString());
if (Config.disableAllVanillaRecipes()) {
this.recipesToUnregister.add(new Pair<>(id, false));
continue;
}
if (hasDisabledAny && Config.disabledVanillaRecipes().contains(id)) {
this.recipesToUnregister.add(new Pair<>(id, false));
continue;
}
Reader reader = (Reader) CoreReflections.method$Resource$openAsReader.invoke(entry.getValue());
Reader reader = (Reader) CoreReflections.methodHandle$Resource$openAsReader.invokeExact(entry.getValue());
JsonObject jsonObject = JsonParser.parseReader(reader).getAsJsonObject();
Key serializerType = Key.of(jsonObject.get("type").getAsString());
RecipeSerializer<ItemStack, ? extends Recipe<ItemStack>> serializer = (RecipeSerializer<ItemStack, ? extends Recipe<ItemStack>>) BuiltInRegistries.RECIPE_SERIALIZER.getValue(serializerType);
@@ -446,7 +450,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
this.plugin.logger().warn("Failed to load data pack recipe " + id + ". Json: " + jsonObject, e);
}
}
} catch (Exception e) {
} catch (Throwable e) {
this.plugin.logger().warn("Unknown error occurred when loading data pack recipes", e);
}
return recipes;
@@ -469,6 +473,9 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
for (Pair<Key, Boolean> pair : this.recipesToUnregister) {
unregisterPlatformRecipeMainThread(pair.left(), pair.right());
}
this.recipesToUnregister.clear();
// 注册新的配方
for (Recipe<ItemStack> recipe : this.byId.values()) {
try {
@@ -480,32 +487,32 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
try {
// give flags back on 1.21.2+
if (VersionHelper.isOrAbove1_21_2() && this.stolenFeatureFlagSet != null) {
CoreReflections.field$RecipeManager$featureflagset.set(minecraftRecipeManager(), this.stolenFeatureFlagSet);
CoreReflections.methodHandle$RecipeManager$featureflagsetSetter.invokeExact(minecraftRecipeManager(), (Object) this.stolenFeatureFlagSet);
this.stolenFeatureFlagSet = null;
}
// refresh recipes
if (VersionHelper.isOrAbove1_21_2()) {
CoreReflections.method$RecipeManager$finalizeRecipeLoading.invoke(minecraftRecipeManager());
CoreReflections.methodHandle$RecipeManager$finalizeRecipeLoading.invokeExact(minecraftRecipeManager());
}
// send to players
CoreReflections.method$DedicatedPlayerList$reloadRecipes.invoke(CraftBukkitReflections.field$CraftServer$playerList.get(Bukkit.getServer()));
CoreReflections.methodHandle$DedicatedPlayerList$reloadRecipes.invokeExact(CraftBukkitReflections.methodHandle$CraftServer$playerListGetter.invokeExact(Bukkit.getServer()));
// now we need to remove the fake `exact` choices
if (VersionHelper.isOrAbove1_21_4()) {
for (Object ingredient : MODIFIED_INGREDIENTS) {
CoreReflections.field$Ingredient$itemStacks1_21_4.set(ingredient, null);
CoreReflections.methodHandle$Ingredient$itemStacksSetter.invokeExact(ingredient, (Set<Object>) null);
}
} else if (VersionHelper.isOrAbove1_21_2()) {
for (Object ingredient : MODIFIED_INGREDIENTS) {
CoreReflections.field$Ingredient$itemStacks1_21_2.set(ingredient, null);
CoreReflections.methodHandle$Ingredient$itemStacksSetter.invokeExact(ingredient, (List<Object>) null);
}
}
// clear cache
MODIFIED_INGREDIENTS.clear();
} catch (Exception e) {
} catch (Throwable e) {
this.plugin.logger().warn("Failed to run delayed recipe tasks", e);
}
}

View File

@@ -2,6 +2,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.api.BukkitAdaptors;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.item.ComponentTypes;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
@@ -455,7 +456,7 @@ public class RecipeEventListener implements Listener {
Player player = InventoryUtils.getPlayerFromInventoryEvent(event);
if (finalCost >= maxRepairCost && !plugin.adapt(player).canInstabuild()) {
if (finalCost >= maxRepairCost && !BukkitAdaptors.adapt(player).canInstabuild()) {
hasResult = false;
}
@@ -548,7 +549,7 @@ public class RecipeEventListener implements Listener {
return;
}
Player player = InventoryUtils.getPlayerFromInventoryEvent(event);
Item<ItemStack> newItem = customItemOptional.get().buildItem(plugin.adapt(player));
Item<ItemStack> newItem = customItemOptional.get().buildItem(BukkitAdaptors.adapt(player));
newItem.maxDamage(max);
newItem.damage(Math.max(max - finalDurability, 0));
inventory.setResult(newItem.getItem());
@@ -597,7 +598,7 @@ public class RecipeEventListener implements Listener {
CraftingInput<ItemStack> input = getCraftingInput(inventory);
if (input == null) return;
Player player = InventoryUtils.getPlayerFromInventoryEvent(event);
BukkitServerPlayer serverPlayer = this.plugin.adapt(player);
BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player);
inventory.setResult(craftingTableRecipe.assemble(input, new ItemBuildContext(serverPlayer, ContextHolder.EMPTY)));
}
@@ -650,7 +651,7 @@ public class RecipeEventListener implements Listener {
SmithingInput<ItemStack> input = getSmithingInput(inventory);
if (smithingTrimRecipe.matches(input)) {
Player player = InventoryUtils.getPlayerFromInventoryEvent(event);
ItemStack result = smithingTrimRecipe.assemble(getSmithingInput(inventory), new ItemBuildContext(this.plugin.adapt(player), ContextHolder.EMPTY));
ItemStack result = smithingTrimRecipe.assemble(getSmithingInput(inventory), new ItemBuildContext(BukkitAdaptors.adapt(player), ContextHolder.EMPTY));
event.setResult(result);
} else {
event.setResult(null);
@@ -674,7 +675,7 @@ public class RecipeEventListener implements Listener {
SmithingInput<ItemStack> input = getSmithingInput(inventory);
if (smithingTransformRecipe.matches(input)) {
Player player = InventoryUtils.getPlayerFromInventoryEvent(event);
ItemStack processed = smithingTransformRecipe.assemble(input, new ItemBuildContext(this.plugin.adapt(player), ContextHolder.EMPTY));
ItemStack processed = smithingTransformRecipe.assemble(input, new ItemBuildContext(BukkitAdaptors.adapt(player), ContextHolder.EMPTY));
event.setResult(processed);
} else {
event.setResult(null);

View File

@@ -1,5 +1,6 @@
package net.momirealms.craftengine.bukkit.loot;
import net.momirealms.craftengine.bukkit.api.BukkitAdaptors;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks;
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
@@ -72,7 +73,7 @@ public class BukkitVanillaLootManager extends AbstractVanillaLootManager impleme
BukkitServerPlayer optionalPlayer = null;
if (VersionHelper.isOrAbove1_20_5()) {
if (event.getDamageSource().getCausingEntity() instanceof Player player) {
optionalPlayer = this.plugin.adapt(player);
optionalPlayer = BukkitAdaptors.adapt(player);
builder.withParameter(DirectContextParameters.PLAYER, optionalPlayer);
}
}

View File

@@ -1,5 +1,6 @@
package net.momirealms.craftengine.bukkit.pack;
import net.momirealms.craftengine.bukkit.api.BukkitAdaptors;
import net.momirealms.craftengine.bukkit.api.event.AsyncResourcePackGenerateEvent;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.command.feature.ReloadCommand;
@@ -43,7 +44,7 @@ public class BukkitPackManager extends AbstractPackManager implements Listener {
@EventHandler(priority = EventPriority.LOW)
public void onPlayerJoin(PlayerJoinEvent event) {
if (Config.sendPackOnJoin() && !VersionHelper.isOrAbove1_20_2()) {
Player player = plugin.adapt(event.getPlayer());
Player player = BukkitAdaptors.adapt(event.getPlayer());
this.sendResourcePack(player);
}
}

View File

@@ -50,6 +50,7 @@ import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import org.jspecify.annotations.Nullable;
import java.io.*;
@@ -58,7 +59,6 @@ import java.net.URLConnection;
import java.nio.file.Path;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@SuppressWarnings("unchecked")
public class BukkitCraftEngine extends CraftEngine {
@@ -123,7 +123,7 @@ public class BukkitCraftEngine extends CraftEngine {
@Override
public void onPluginLoad() {
if (super.blockManager == null) {
injectRegistries();
this.injectRegistries();
}
try {
WorldStorageInjector.init();
@@ -363,11 +363,9 @@ public class BukkitCraftEngine extends CraftEngine {
}
}
public BukkitServerPlayer adapt(org.bukkit.entity.Player player) {
if (player == null) return null;
return Optional.ofNullable((BukkitServerPlayer) networkManager().getOnlineUser(player)).orElseGet(
() -> (BukkitServerPlayer) networkManager().getUser(player)
);
public BukkitServerPlayer adapt(@NotNull org.bukkit.entity.Player player) {
Objects.requireNonNull(player, "player cannot be null");
return (BukkitServerPlayer) networkManager().getOnlineUser(player);
}
public AntiGriefLib antiGriefProvider() {

View File

@@ -1,8 +1,11 @@
package net.momirealms.craftengine.bukkit.plugin.command.feature;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.bukkit.api.CraftEngineItems;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
import net.momirealms.craftengine.bukkit.util.PlayerUtils;
import net.momirealms.craftengine.core.item.CustomItem;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
import net.momirealms.craftengine.core.plugin.command.FlagKeys;
@@ -47,12 +50,18 @@ public class GetItemCommand extends BukkitCommandFeature<CommandSender> {
int amount = context.getOrDefault("amount", 1);
boolean toInv = context.flags().hasFlag(FlagKeys.TO_INVENTORY);
NamespacedKey namespacedKey = context.get("id");
Key key = Key.of(namespacedKey.namespace(), namespacedKey.value());
ItemStack builtItem = plugin().itemManager().buildCustomItemStack(key, plugin().adapt(context.sender()));
if (builtItem == null) {
handleFeedback(context, MessageConstants.COMMAND_ITEM_GET_FAILURE_NOT_EXIST, Component.text(key.toString()));
return;
Key itemId = Key.of(namespacedKey.namespace(), namespacedKey.value());
CustomItem<ItemStack> customItem = CraftEngineItems.byId(itemId);
if (customItem == null) {
customItem = BukkitItemManager.instance().getCustomItemByPathOnly(itemId.value()).orElse(null);
if (customItem == null) {
handleFeedback(context, MessageConstants.COMMAND_ITEM_GET_FAILURE_NOT_EXIST, Component.text(itemId.toString()));
return;
} else {
itemId = customItem.id();
}
}
ItemStack builtItem = customItem.buildItemStack(plugin().adapt(context.sender()));
int amountToGive = amount;
int maxStack = builtItem.getMaxStackSize();
while (amountToGive > 0) {
@@ -66,7 +75,7 @@ public class GetItemCommand extends BukkitCommandFeature<CommandSender> {
PlayerUtils.dropItem(player, more, false, true, false);
}
}
handleFeedback(context, MessageConstants.COMMAND_ITEM_GET_SUCCESS, Component.text(amount), Component.text(key.toString()));
handleFeedback(context, MessageConstants.COMMAND_ITEM_GET_SUCCESS, Component.text(amount), Component.text(itemId.toString()));
});
}

View File

@@ -1,6 +1,7 @@
package net.momirealms.craftengine.bukkit.plugin.command.feature;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.bukkit.api.CraftEngineItems;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
import net.momirealms.craftengine.bukkit.util.PlayerUtils;
@@ -27,7 +28,6 @@ import org.incendo.cloud.suggestion.Suggestion;
import org.incendo.cloud.suggestion.SuggestionProvider;
import java.util.Collection;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
public class GiveItemCommand extends BukkitCommandFeature<CommandSender> {
@@ -54,16 +54,20 @@ public class GiveItemCommand extends BukkitCommandFeature<CommandSender> {
int amount = context.getOrDefault("amount", 1);
boolean toInv = context.flags().hasFlag(FlagKeys.TO_INVENTORY);
NamespacedKey namespacedKey = context.get("id");
Key key = Key.of(namespacedKey.namespace(), namespacedKey.value());
Optional<CustomItem<ItemStack>> optionalItem = BukkitItemManager.instance().getCustomItem(key);
if (optionalItem.isEmpty()) {
handleFeedback(context, MessageConstants.COMMAND_ITEM_GIVE_FAILURE_NOT_EXIST, Component.text(key.toString()));
return;
Key itemId = Key.of(namespacedKey.namespace(), namespacedKey.value());
CustomItem<ItemStack> customItem = CraftEngineItems.byId(itemId);
if (customItem == null) {
customItem = BukkitItemManager.instance().getCustomItemByPathOnly(itemId.value()).orElse(null);
if (customItem == null) {
handleFeedback(context, MessageConstants.COMMAND_ITEM_GIVE_FAILURE_NOT_EXIST, Component.text(itemId.toString()));
return;
} else {
itemId = customItem.id();
}
}
Collection<Player> players = selector.values();
for (Player player : players) {
ItemStack builtItem = optionalItem.get().buildItemStack(plugin().adapt(player));
ItemStack builtItem = customItem.buildItemStack(plugin().adapt(player));
if (builtItem == null) {
return;
}
@@ -90,9 +94,9 @@ public class GiveItemCommand extends BukkitCommandFeature<CommandSender> {
}
}
if (players.size() == 1) {
handleFeedback(context, MessageConstants.COMMAND_ITEM_GIVE_SUCCESS_SINGLE, Component.text(amount), Component.text(key.toString()), Component.text(players.iterator().next().getName()));
handleFeedback(context, MessageConstants.COMMAND_ITEM_GIVE_SUCCESS_SINGLE, Component.text(amount), Component.text(itemId.toString()), Component.text(players.iterator().next().getName()));
} else if (players.size() > 1) {
handleFeedback(context, MessageConstants.COMMAND_ITEM_GIVE_SUCCESS_MULTIPLE, Component.text(amount), Component.text(key.toString()), Component.text(players.size()));
handleFeedback(context, MessageConstants.COMMAND_ITEM_GIVE_SUCCESS_MULTIPLE, Component.text(amount), Component.text(itemId.toString()), Component.text(players.size()));
}
});
}

View File

@@ -1,12 +1,15 @@
package net.momirealms.craftengine.bukkit.plugin.command.feature;
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
import net.momirealms.craftengine.bukkit.plugin.network.BukkitNetworkManager;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
import org.bukkit.Location;
import org.bukkit.block.data.BlockData;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.incendo.cloud.Command;
import org.incendo.cloud.parser.standard.IntegerParser;
public class TestCommand extends BukkitCommandFeature<CommandSender> {
@@ -17,10 +20,22 @@ public class TestCommand extends BukkitCommandFeature<CommandSender> {
@Override
public Command.Builder<? extends CommandSender> assembleCommand(org.incendo.cloud.CommandManager<CommandSender> manager, Command.Builder<CommandSender> builder) {
return builder
.required("start", IntegerParser.integerParser(0))
.senderType(Player.class)
.handler(context -> {
Player player = context.sender();
player.sendMessage("客户端模组状态: " + BukkitNetworkManager.instance().getUser(player).clientModEnabled());
Player sender = context.sender();
int start = context.get("start");
int x = sender.getChunk().getX() * 16;
int z = sender.getChunk().getZ() * 16;
int y = (sender.getLocation().getBlockY() / 16) * 16;
for (int a = 0; a < 16; a++) {
for (int b = 0; b < 16; b++) {
for (int c = 0; c < 16; c++) {
BlockData blockData = BlockStateUtils.fromBlockData(BlockStateUtils.idToBlockState(start + a + b * 16 + c * 256));
sender.getWorld().setBlockData(new Location(sender.getWorld(), x + a, y + b, z + c), blockData);
}
}
}
});
}

View File

@@ -3,6 +3,7 @@ package net.momirealms.craftengine.bukkit.plugin.gui;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.reflection.bukkit.CraftBukkitReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections;
import net.momirealms.craftengine.bukkit.util.ComponentUtils;
@@ -91,6 +92,9 @@ public class BukkitGuiManager implements GuiManager, Listener {
@EventHandler(ignoreCancelled = true)
public void onInventoryClick(InventoryClickEvent event) {
org.bukkit.inventory.Inventory inventory = event.getInventory();
if (!CraftBukkitReflections.clazz$MinecraftInventory.isInstance(FastNMS.INSTANCE.method$CraftInventory$getInventory(inventory))) {
return;
}
if (!(inventory.getHolder() instanceof CraftEngineInventoryHolder craftEngineInventoryHolder)) {
return;
}
@@ -106,6 +110,9 @@ public class BukkitGuiManager implements GuiManager, Listener {
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOW)
public void onInventoryDrag(InventoryDragEvent event) {
org.bukkit.inventory.Inventory inventory = event.getInventory();
if (!CraftBukkitReflections.clazz$MinecraftInventory.isInstance(FastNMS.INSTANCE.method$CraftInventory$getInventory(inventory))) {
return;
}
if (!(inventory.getHolder() instanceof CraftEngineInventoryHolder)) {
return;
}

View File

@@ -177,7 +177,6 @@ public final class WorldStorageInjector {
return section;
}
public static class SetBlockStateInterceptor {
public static final SetBlockStateInterceptor INSTANCE = new SetBlockStateInterceptor();
@@ -215,7 +214,7 @@ public final class WorldStorageInjector {
}
}
protected static void compareAndUpdateBlockState(int x, int y, int z, Object newState, Object previousState, InjectedHolder holder) {
private static void compareAndUpdateBlockState(int x, int y, int z, Object newState, Object previousState, InjectedHolder holder) {
try {
Optional<ImmutableBlockState> optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(newState);
CESection section = holder.ceSection();

View File

@@ -12,9 +12,11 @@ import net.momirealms.craftengine.bukkit.plugin.network.id.PacketIdFinder;
import net.momirealms.craftengine.bukkit.plugin.network.id.PacketIds1_20;
import net.momirealms.craftengine.bukkit.plugin.network.id.PacketIds1_20_5;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.LeavesReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.LibraryReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections;
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
import net.momirealms.craftengine.bukkit.plugin.user.FakeBukkitServerPlayer;
import net.momirealms.craftengine.bukkit.util.KeyUtils;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.context.CooldownData;
@@ -142,6 +144,47 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
} catch (ReflectiveOperationException e) {
throw new RuntimeException("Failed to init server connection", e);
}
// Inject Leaves bot list
if (VersionHelper.isLeaves()) {
this.injectLeavesBotList();
}
}
public static BukkitNetworkManager instance() {
return instance;
}
public void addFakePlayer(Player player) {
FakeBukkitServerPlayer fakePlayer = new FakeBukkitServerPlayer(this.plugin);
fakePlayer.setPlayer(player);
this.onlineUsers.put(player.getUniqueId(), fakePlayer);
this.resetUserArray();
}
public boolean removeFakePlayer(Player player) {
BukkitServerPlayer fakePlayer = this.onlineUsers.get(player.getUniqueId());
if (!(fakePlayer instanceof FakeBukkitServerPlayer)) {
return false;
}
this.onlineUsers.remove(player.getUniqueId());
this.resetUserArray();
this.saveCooldown(player, fakePlayer.cooldown());
return true;
}
@SuppressWarnings("unchecked")
private void injectLeavesBotList() {
try {
Object botList = LeavesReflections.field$BotList$INSTANCE.get(null);
List<Object> bots = (List<Object>) LeavesReflections.field$BotList$bots.get(botList);
ListMonitor<Object> monitor = new ListMonitor<>(bots,
(bot) -> addFakePlayer(FastNMS.INSTANCE.method$ServerPlayer$getBukkitEntity(bot)),
(bot) -> removeFakePlayer(FastNMS.INSTANCE.method$ServerPlayer$getBukkitEntity(bot))
);
LeavesReflections.field$BotList$bots.set(botList, monitor);
} catch (ReflectiveOperationException e) {
this.plugin.logger().severe("Failed to inject leaves bot list");
}
}
private void registerPacketHandlers() {
@@ -203,10 +246,6 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
registerC2SByteBufPacketConsumer(PacketConsumers.INTERACT_ENTITY, this.packetIds.serverboundInteractPacket());
}
public static BukkitNetworkManager instance() {
return instance;
}
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();
@@ -215,10 +254,10 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
user.setPlayer(player);
this.onlineUsers.put(player.getUniqueId(), user);
this.resetUserArray();
// folia在此tick每个玩家
if (VersionHelper.isFolia()) {
player.getScheduler().runAtFixedRate(plugin.javaPlugin(), (t) -> user.tick(),
() -> {
}, 1, 1);
() -> {}, 1, 1);
}
}
}
@@ -282,7 +321,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
}
@Override
public NetWorkUser getUser(Channel channel) {
public NetWorkUser getUser(@NotNull Channel channel) {
ChannelPipeline pipeline = channel.pipeline();
return this.users.get(pipeline);
}
@@ -298,14 +337,18 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
return getChannel((Player) player.platformPlayer());
}
@Nullable
public NetWorkUser getUser(Player player) {
return getUser(getChannel(player));
}
@Nullable
public NetWorkUser getOnlineUser(Player player) {
return this.onlineUsers.get(player.getUniqueId());
}
// 当假人的时候channel为null
@NotNull
public Channel getChannel(Player player) {
return FastNMS.INSTANCE.field$Connection$channel(
FastNMS.INSTANCE.field$ServerGamePacketListenerImpl$connection(
@@ -318,6 +361,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
@Override
public void sendPacket(@NotNull NetWorkUser player, Object packet, boolean immediately, Runnable sendListener) {
if (player.isFakePlayer()) return;
if (immediately) {
this.immediatePacketConsumer.accept(player.nettyChannel(), packet, sendListener);
} else {
@@ -327,6 +371,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
@Override
public void sendPackets(@NotNull NetWorkUser player, List<Object> packet, boolean immediately, Runnable sendListener) {
if (player.isFakePlayer()) return;
if (immediately) {
this.immediatePacketsConsumer.accept(player.nettyChannel(), packet, sendListener);
} else {

View File

@@ -18,6 +18,7 @@ import net.momirealms.craftengine.bukkit.api.event.FurnitureAttemptBreakEvent;
import net.momirealms.craftengine.bukkit.api.event.FurnitureBreakEvent;
import net.momirealms.craftengine.bukkit.api.event.FurnitureInteractEvent;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData;
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture;
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager;
import net.momirealms.craftengine.bukkit.entity.projectile.BukkitProjectileManager;
@@ -86,9 +87,10 @@ import java.util.function.BiConsumer;
public class PacketConsumers {
private static BukkitNetworkManager.Handlers[] ADD_ENTITY_HANDLERS;
private static int[] mappings;
private static int[] mappingsMOD;
private static IntIdentityList BLOCK_LIST;
private static int[] BLOCK_STATE_MAPPINGS;
private static int[] MOD_BLOCK_STATE_MAPPINGS;
private static IntIdentityList SERVER_BLOCK_LIST;
private static IntIdentityList CLIENT_BLOCK_LIST;
private static IntIdentityList BIOME_LIST;
public static void initEntities(int registrySize) {
@@ -137,6 +139,14 @@ public class PacketConsumers {
ADD_ENTITY_HANDLERS[MEntityTypes.ITEM$registryId] = simpleAddEntityHandler(CommonItemPacketHandler.INSTANCE);
ADD_ENTITY_HANDLERS[MEntityTypes.ITEM_FRAME$registryId] = simpleAddEntityHandler(ItemFramePacketHandler.INSTANCE);
ADD_ENTITY_HANDLERS[MEntityTypes.GLOW_ITEM_FRAME$registryId] = simpleAddEntityHandler(ItemFramePacketHandler.INSTANCE);
ADD_ENTITY_HANDLERS[MEntityTypes.ENDERMAN$registryId] = simpleAddEntityHandler(EndermanPacketHandler.INSTANCE);
ADD_ENTITY_HANDLERS[MEntityTypes.CHEST_MINECART$registryId] = simpleAddEntityHandler(AbstractMinecartPacketHandler.INSTANCE);
ADD_ENTITY_HANDLERS[MEntityTypes.COMMAND_BLOCK_MINECART$registryId] = simpleAddEntityHandler(AbstractMinecartPacketHandler.INSTANCE);
ADD_ENTITY_HANDLERS[MEntityTypes.FURNACE_MINECART$registryId] = simpleAddEntityHandler(AbstractMinecartPacketHandler.INSTANCE);
ADD_ENTITY_HANDLERS[MEntityTypes.HOPPER_MINECART$registryId] = simpleAddEntityHandler(AbstractMinecartPacketHandler.INSTANCE);
ADD_ENTITY_HANDLERS[MEntityTypes.MINECART$registryId] = simpleAddEntityHandler(AbstractMinecartPacketHandler.INSTANCE);
ADD_ENTITY_HANDLERS[MEntityTypes.SPAWNER_MINECART$registryId] = simpleAddEntityHandler(AbstractMinecartPacketHandler.INSTANCE);
ADD_ENTITY_HANDLERS[MEntityTypes.TNT_MINECART$registryId] = simpleAddEntityHandler(AbstractMinecartPacketHandler.INSTANCE);
ADD_ENTITY_HANDLERS[MEntityTypes.FIREBALL$registryId] = createOptionalCustomProjectileEntityHandler(true);
ADD_ENTITY_HANDLERS[MEntityTypes.EYE_OF_ENDER$registryId] = createOptionalCustomProjectileEntityHandler(true);
ADD_ENTITY_HANDLERS[MEntityTypes.FIREWORK_ROCKET$registryId] = createOptionalCustomProjectileEntityHandler(true);
@@ -149,6 +159,9 @@ public class PacketConsumers {
ADD_ENTITY_HANDLERS[MEntityTypes.TRIDENT$registryId] = createOptionalCustomProjectileEntityHandler(false);
ADD_ENTITY_HANDLERS[MEntityTypes.ARROW$registryId] = createOptionalCustomProjectileEntityHandler(false);
ADD_ENTITY_HANDLERS[MEntityTypes.SPECTRAL_ARROW$registryId] = createOptionalCustomProjectileEntityHandler(false);
if (VersionHelper.isOrAbove1_20_3()) {
ADD_ENTITY_HANDLERS[MEntityTypes.TNT$registryId] = simpleAddEntityHandler(PrimedTNTPacketHandler.INSTANCE);
}
if (VersionHelper.isOrAbove1_20_5()) {
ADD_ENTITY_HANDLERS[MEntityTypes.OMINOUS_ITEM_SPAWNER$registryId] = simpleAddEntityHandler(CommonItemPacketHandler.INSTANCE);
}
@@ -215,32 +228,35 @@ public class PacketConsumers {
}
public static void initBlocks(Map<Integer, Integer> map, int registrySize) {
mappings = new int[registrySize];
int[] newMappings = new int[registrySize];
for (int i = 0; i < registrySize; i++) {
mappings[i] = i;
newMappings[i] = i;
}
mappingsMOD = Arrays.copyOf(mappings, registrySize);
int[] newMappingsMOD = Arrays.copyOf(newMappings, registrySize);
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
mappings[entry.getKey()] = entry.getValue();
newMappings[entry.getKey()] = entry.getValue();
if (BlockStateUtils.isVanillaBlock(entry.getKey())) {
mappingsMOD[entry.getKey()] = entry.getValue();
newMappingsMOD[entry.getKey()] = entry.getValue();
}
}
for (int i = 0; i < mappingsMOD.length; i++) {
for (int i = 0; i < newMappingsMOD.length; i++) {
if (BlockStateUtils.isVanillaBlock(i)) {
mappingsMOD[i] = remap(i);
newMappingsMOD[i] = newMappings[i];
}
}
BLOCK_LIST = new IntIdentityList(registrySize);
BLOCK_STATE_MAPPINGS = newMappings;
MOD_BLOCK_STATE_MAPPINGS = newMappingsMOD;
SERVER_BLOCK_LIST = new IntIdentityList(registrySize);
CLIENT_BLOCK_LIST = new IntIdentityList(BlockStateUtils.vanillaStateSize());
BIOME_LIST = new IntIdentityList(RegistryUtils.currentBiomeRegistrySize());
}
public static int remap(int stateId) {
return mappings[stateId];
return BLOCK_STATE_MAPPINGS[stateId];
}
public static int remapMOD(int stateId) {
return mappingsMOD[stateId];
return MOD_BLOCK_STATE_MAPPINGS[stateId];
}
public static final BiConsumer<NetWorkUser, ByteBufPacketEvent> LEVEL_CHUNK_WITH_LIGHT = (user, event) -> {
@@ -292,26 +308,22 @@ public class PacketConsumers {
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(byteBuf);
FriendlyByteBuf newBuf = new FriendlyByteBuf(Unpooled.buffer());
for (int i = 0, count = player.clientSideSectionCount(); i < count; i++) {
try {
MCSection mcSection = new MCSection(BLOCK_LIST, BIOME_LIST);
mcSection.readPacket(friendlyByteBuf);
PalettedContainer<Integer> container = mcSection.blockStateContainer();
Palette<Integer> palette = container.data().palette();
if (palette.canRemap()) {
palette.remap(PacketConsumers::remapMOD);
} else {
for (int j = 0; j < 4096; j++) {
int state = container.get(j);
int newState = remapMOD(state);
if (newState != state) {
container.set(j, newState);
}
MCSection mcSection = new MCSection(SERVER_BLOCK_LIST, SERVER_BLOCK_LIST, BIOME_LIST);
mcSection.readPacket(friendlyByteBuf);
PalettedContainer<Integer> container = mcSection.blockStateContainer();
Palette<Integer> palette = container.data().palette();
if (palette.canRemap()) {
palette.remap(PacketConsumers::remapMOD);
} else {
for (int j = 0; j < 4096; j++) {
int state = container.get(j);
int newState = remapMOD(state);
if (newState != state) {
container.set(j, newState);
}
}
mcSection.writePacket(newBuf);
} catch (Exception e) {
break;
}
mcSection.writePacket(newBuf);
}
buffer = newBuf.array();
} else {
@@ -319,26 +331,22 @@ public class PacketConsumers {
FriendlyByteBuf friendlyByteBuf = new FriendlyByteBuf(byteBuf);
FriendlyByteBuf newBuf = new FriendlyByteBuf(Unpooled.buffer());
for (int i = 0, count = player.clientSideSectionCount(); i < count; i++) {
try {
MCSection mcSection = new MCSection(BLOCK_LIST, BIOME_LIST);
mcSection.readPacket(friendlyByteBuf);
PalettedContainer<Integer> container = mcSection.blockStateContainer();
Palette<Integer> palette = container.data().palette();
if (palette.canRemap()) {
palette.remap(PacketConsumers::remap);
} else {
for (int j = 0; j < 4096; j++) {
int state = container.get(j);
int newState = remap(state);
if (newState != state) {
container.set(j, newState);
}
MCSection mcSection = new MCSection(CLIENT_BLOCK_LIST, SERVER_BLOCK_LIST, BIOME_LIST);
mcSection.readPacket(friendlyByteBuf);
PalettedContainer<Integer> container = mcSection.blockStateContainer();
Palette<Integer> palette = container.data().palette();
if (palette.canRemap()) {
palette.remap(PacketConsumers::remap);
} else {
for (int j = 0; j < 4096; j++) {
int state = container.get(j);
int newState = remap(state);
if (newState != state) {
container.set(j, newState);
}
}
mcSection.writePacket(newBuf);
} catch (Exception e) {
break;
}
mcSection.writePacket(newBuf);
}
buffer = newBuf.array();
}
@@ -1945,7 +1953,7 @@ public class PacketConsumers {
for (int i = 0; i < packedItems.size(); i++) {
Object packedItem = packedItems.get(i);
int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem);
if (entityDataId != EntityDataUtils.CUSTOM_NAME_DATA_ID) continue;
if (entityDataId != BaseEntityData.CustomName.id()) continue;
Optional<Object> optionalTextComponent = (Optional<Object>) FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem);
if (optionalTextComponent.isEmpty()) continue;
Object textComponent = optionalTextComponent.get();
@@ -2241,7 +2249,7 @@ public class PacketConsumers {
// 因为不能走编码器只能替换对象
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> CONTAINER_CLICK_1_21_5 = (user, event, packet) -> {
try {
var player = (net.momirealms.craftengine.core.entity.player.Player) user;
BukkitServerPlayer player = (BukkitServerPlayer) user;
int containerId = FastNMS.INSTANCE.field$ServerboundContainerClickPacket$containerId(packet);
int stateId = FastNMS.INSTANCE.field$ServerboundContainerClickPacket$stateId(packet);
short slotNum = FastNMS.INSTANCE.field$ServerboundContainerClickPacket$slotNum(packet);

View File

@@ -0,0 +1,114 @@
package net.momirealms.craftengine.bukkit.plugin.network.handler;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.bukkit.entity.data.AbstractMinecartData;
import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.network.PacketConsumers;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.ComponentUtils;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent;
import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler;
import net.momirealms.craftengine.core.plugin.network.NetWorkUser;
import net.momirealms.craftengine.core.util.AdventureHelper;
import net.momirealms.craftengine.core.util.FriendlyByteBuf;
import net.momirealms.craftengine.core.util.VersionHelper;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public class AbstractMinecartPacketHandler implements EntityPacketHandler {
public static final AbstractMinecartPacketHandler INSTANCE = new AbstractMinecartPacketHandler();
private static final BlockStateHandler HANDLER = VersionHelper.isOrAbove1_21_3() ? BlockStateHandler_1_21_3.INSTANCE : BlockStateHandler_1_20.INSTANCE;
@Override
public void handleSetEntityData(NetWorkUser user, ByteBufPacketEvent event) {
FriendlyByteBuf buf = event.getBuffer();
int id = buf.readVarInt();
boolean isChanged = false;
List<Object> packedItems = FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$unpack(buf);
for (int i = 0; i < packedItems.size(); i++) {
Object packedItem = packedItems.get(i);
int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem);
Object blockState = HANDLER.handle(user, packedItem, entityDataId);
if (blockState != null) {
packedItems.set(i, blockState);
isChanged = true;
} else if (Config.interceptEntityName() && entityDataId == BaseEntityData.CustomName.id()) {
@SuppressWarnings("unchecked")
Optional<Object> optionalTextComponent = (Optional<Object>) FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem);
if (optionalTextComponent.isEmpty()) continue;
Object textComponent = optionalTextComponent.get();
String json = ComponentUtils.minecraftToJson(textComponent);
Map<String, Component> tokens = CraftEngine.instance().fontManager().matchTags(json);
if (tokens.isEmpty()) continue;
Component component = AdventureHelper.jsonToComponent(json);
for (Map.Entry<String, Component> token : tokens.entrySet()) {
component = component.replaceText(b -> b.matchLiteral(token.getKey()).replacement(token.getValue()));
}
Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem);
packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue(
entityDataId, serializer, Optional.of(ComponentUtils.adventureToMinecraft(component))
));
isChanged = true;
}
}
if (isChanged) {
event.setChanged(true);
buf.clear();
buf.writeVarInt(event.packetID());
buf.writeVarInt(id);
FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$pack(packedItems, buf);
}
}
interface BlockStateHandler {
Object handle(NetWorkUser user, Object packedItem, int entityDataId);
}
static class BlockStateHandler_1_21_3 implements BlockStateHandler {
protected static final BlockStateHandler INSTANCE = new BlockStateHandler_1_21_3();
@Override
public Object handle(NetWorkUser user, Object packedItem, int entityDataId) {
if (entityDataId != AbstractMinecartData.CustomDisplayBlock.id()) return null;
@SuppressWarnings("unchecked")
Optional<Object> blockState = (Optional<Object>) FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem);
if (blockState.isEmpty()) return null;
int stateId = BlockStateUtils.blockStateToId(blockState.get());
int newStateId;
if (!user.clientModEnabled()) {
newStateId = PacketConsumers.remap(stateId);
} else {
newStateId = PacketConsumers.remapMOD(stateId);
}
if (newStateId == stateId) return null;
Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem);
return FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue(
entityDataId, serializer, Optional.of(BlockStateUtils.idToBlockState(newStateId))
);
}
}
static class BlockStateHandler_1_20 implements BlockStateHandler {
protected static final BlockStateHandler INSTANCE = new BlockStateHandler_1_20();
@Override
public Object handle(NetWorkUser user, Object packedItem, int entityDataId) {
if (entityDataId != AbstractMinecartData.DisplayBlock.id()) return null;
int stateId = (int) FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem);
int newStateId;
if (!user.clientModEnabled()) {
newStateId = PacketConsumers.remap(stateId);
} else {
newStateId = PacketConsumers.remapMOD(stateId);
}
if (newStateId == stateId) return null;
Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem);
return FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue(entityDataId, serializer, newStateId);
}
}
}

View File

@@ -1,9 +1,9 @@
package net.momirealms.craftengine.bukkit.plugin.network.handler;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.util.ComponentUtils;
import net.momirealms.craftengine.bukkit.util.EntityDataUtils;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent;
@@ -32,7 +32,7 @@ public class ArmorStandPacketHandler implements EntityPacketHandler {
for (int i = 0; i < packedItems.size(); i++) {
Object packedItem = packedItems.get(i);
int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem);
if (entityDataId != EntityDataUtils.CUSTOM_NAME_DATA_ID) continue;
if (entityDataId != BaseEntityData.CustomName.id()) continue;
@SuppressWarnings("unchecked")
Optional<Object> optionalTextComponent = (Optional<Object>) FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem);
if (optionalTextComponent.isEmpty()) continue;

View File

@@ -1,11 +1,12 @@
package net.momirealms.craftengine.bukkit.plugin.network.handler;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData;
import net.momirealms.craftengine.bukkit.entity.data.BlockDisplayEntityData;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.network.PacketConsumers;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.ComponentUtils;
import net.momirealms.craftengine.bukkit.util.EntityDataUtils;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent;
@@ -30,7 +31,7 @@ public class BlockDisplayPacketHandler implements EntityPacketHandler {
for (int i = 0; i < packedItems.size(); i++) {
Object packedItem = packedItems.get(i);
int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem);
if (entityDataId == EntityDataUtils.BLOCK_STATE_DATA_ID) {
if (entityDataId == BlockDisplayEntityData.DisplayedBlock.id()) {
Object blockState = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem);
int stateId = BlockStateUtils.blockStateToId(blockState);
int newStateId;
@@ -39,12 +40,13 @@ public class BlockDisplayPacketHandler implements EntityPacketHandler {
} else {
newStateId = PacketConsumers.remapMOD(stateId);
}
if (newStateId == stateId) continue;
Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem);
packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue(
entityDataId, serializer, BlockStateUtils.idToBlockState(newStateId)
));
isChanged = true;
} else if (Config.interceptEntityName() && entityDataId == EntityDataUtils.CUSTOM_NAME_DATA_ID) {
} else if (Config.interceptEntityName() && entityDataId == BaseEntityData.CustomName.id()) {
@SuppressWarnings("unchecked")
Optional<Object> optionalTextComponent = (Optional<Object>) FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem);
if (optionalTextComponent.isEmpty()) continue;

View File

@@ -28,7 +28,7 @@ public class CommonItemPacketHandler implements EntityPacketHandler {
for (int i = 0; i < packedItems.size(); i++) {
Object packedItem = packedItems.get(i);
int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem);
if (entityDataId != EntityDataUtils.ITEM_DATA_ID) continue;
if (entityDataId != EntityDataUtils.UNSAFE_ITEM_DATA_ID) continue;
Object nmsItemStack = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem);
if (!CoreReflections.clazz$ItemStack.isInstance(nmsItemStack)) {
long time = System.currentTimeMillis();

View File

@@ -0,0 +1,79 @@
package net.momirealms.craftengine.bukkit.plugin.network.handler;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData;
import net.momirealms.craftengine.bukkit.entity.data.EnderManData;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.network.PacketConsumers;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.ComponentUtils;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent;
import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler;
import net.momirealms.craftengine.core.plugin.network.NetWorkUser;
import net.momirealms.craftengine.core.util.AdventureHelper;
import net.momirealms.craftengine.core.util.FriendlyByteBuf;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public class EndermanPacketHandler implements EntityPacketHandler {
public static final EndermanPacketHandler INSTANCE = new EndermanPacketHandler();
@Override
public void handleSetEntityData(NetWorkUser user, ByteBufPacketEvent event) {
FriendlyByteBuf buf = event.getBuffer();
int id = buf.readVarInt();
boolean isChanged = false;
List<Object> packedItems = FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$unpack(buf);
for (int i = 0; i < packedItems.size(); i++) {
Object packedItem = packedItems.get(i);
int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem);
if (entityDataId == EnderManData.CarryState.id()) {
@SuppressWarnings("unchecked")
Optional<Object> blockState = (Optional<Object>) FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem);
if (blockState.isEmpty()) continue;
int stateId = BlockStateUtils.blockStateToId(blockState.get());
int newStateId;
if (!user.clientModEnabled()) {
newStateId = PacketConsumers.remap(stateId);
} else {
newStateId = PacketConsumers.remapMOD(stateId);
}
if (newStateId == stateId) continue;
Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem);
packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue(
entityDataId, serializer, Optional.of(BlockStateUtils.idToBlockState(newStateId))
));
isChanged = true;
} else if (Config.interceptEntityName() && entityDataId == BaseEntityData.CustomName.id()) {
@SuppressWarnings("unchecked")
Optional<Object> optionalTextComponent = (Optional<Object>) FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem);
if (optionalTextComponent.isEmpty()) continue;
Object textComponent = optionalTextComponent.get();
String json = ComponentUtils.minecraftToJson(textComponent);
Map<String, Component> tokens = CraftEngine.instance().fontManager().matchTags(json);
if (tokens.isEmpty()) continue;
Component component = AdventureHelper.jsonToComponent(json);
for (Map.Entry<String, Component> token : tokens.entrySet()) {
component = component.replaceText(b -> b.matchLiteral(token.getKey()).replacement(token.getValue()));
}
Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem);
packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue(
entityDataId, serializer, Optional.of(ComponentUtils.adventureToMinecraft(component))
));
isChanged = true;
}
}
if (isChanged) {
event.setChanged(true);
buf.clear();
buf.writeVarInt(event.packetID());
buf.writeVarInt(id);
FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$pack(packedItems, buf);
}
}
}

View File

@@ -1,9 +1,9 @@
package net.momirealms.craftengine.bukkit.plugin.network.handler;
import net.momirealms.craftengine.bukkit.entity.data.ItemDisplayEntityData;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
import net.momirealms.craftengine.bukkit.util.EntityDataUtils;
import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent;
import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler;
import net.momirealms.craftengine.core.plugin.network.NetWorkUser;
@@ -25,7 +25,7 @@ public class ItemDisplayPacketHandler implements EntityPacketHandler {
for (int i = 0; i < packedItems.size(); i++) {
Object packedItem = packedItems.get(i);
int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem);
if (entityDataId != EntityDataUtils.DISPLAYED_ITEM_DATA_ID) continue;
if (entityDataId != ItemDisplayEntityData.DisplayedItem.id()) continue;
Object nmsItemStack = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem);
ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(nmsItemStack);
Optional<ItemStack> optional = BukkitItemManager.instance().s2c(itemStack, (BukkitServerPlayer) user);

View File

@@ -1,10 +1,10 @@
package net.momirealms.craftengine.bukkit.plugin.network.handler;
import net.momirealms.craftengine.bukkit.entity.data.ItemFrameData;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
import net.momirealms.craftengine.bukkit.util.EntityDataUtils;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent;
import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler;
@@ -28,7 +28,7 @@ public class ItemFramePacketHandler implements EntityPacketHandler {
for (int i = 0; i < packedItems.size(); i++) {
Object packedItem = packedItems.get(i);
int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem);
if (entityDataId != EntityDataUtils.ITEM_FRAME_DATA_ID) continue;
if (entityDataId != ItemFrameData.Item.id()) continue;
Object nmsItemStack = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem);
if (!CoreReflections.clazz$ItemStack.isInstance(nmsItemStack)) {
long time = System.currentTimeMillis();

View File

@@ -0,0 +1,76 @@
package net.momirealms.craftengine.bukkit.plugin.network.handler;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData;
import net.momirealms.craftengine.bukkit.entity.data.PrimedTntData;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.network.PacketConsumers;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.ComponentUtils;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent;
import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler;
import net.momirealms.craftengine.core.plugin.network.NetWorkUser;
import net.momirealms.craftengine.core.util.AdventureHelper;
import net.momirealms.craftengine.core.util.FriendlyByteBuf;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public class PrimedTNTPacketHandler implements EntityPacketHandler {
public static final PrimedTNTPacketHandler INSTANCE = new PrimedTNTPacketHandler();
@Override
public void handleSetEntityData(NetWorkUser user, ByteBufPacketEvent event) {
FriendlyByteBuf buf = event.getBuffer();
int id = buf.readVarInt();
boolean isChanged = false;
List<Object> packedItems = FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$unpack(buf);
for (int i = 0; i < packedItems.size(); i++) {
Object packedItem = packedItems.get(i);
int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem);
if (entityDataId == PrimedTntData.BlockState.id()) {
Object blockState = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem);
int stateId = BlockStateUtils.blockStateToId(blockState);
int newStateId;
if (!user.clientModEnabled()) {
newStateId = PacketConsumers.remap(stateId);
} else {
newStateId = PacketConsumers.remapMOD(stateId);
}
if (newStateId == stateId) continue;
Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem);
packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue(
entityDataId, serializer, BlockStateUtils.idToBlockState(newStateId)
));
isChanged = true;
} else if (Config.interceptEntityName() && entityDataId == BaseEntityData.CustomName.id()) {
@SuppressWarnings("unchecked")
Optional<Object> optionalTextComponent = (Optional<Object>) FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem);
if (optionalTextComponent.isEmpty()) continue;
Object textComponent = optionalTextComponent.get();
String json = ComponentUtils.minecraftToJson(textComponent);
Map<String, Component> tokens = CraftEngine.instance().fontManager().matchTags(json);
if (tokens.isEmpty()) continue;
Component component = AdventureHelper.jsonToComponent(json);
for (Map.Entry<String, Component> token : tokens.entrySet()) {
component = component.replaceText(b -> b.matchLiteral(token.getKey()).replacement(token.getValue()));
}
Object serializer = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$serializer(packedItem);
packedItems.set(i, FastNMS.INSTANCE.constructor$SynchedEntityData$DataValue(
entityDataId, serializer, Optional.of(ComponentUtils.adventureToMinecraft(component))
));
isChanged = true;
}
}
if (isChanged) {
event.setChanged(true);
buf.clear();
buf.writeVarInt(event.packetID());
buf.writeVarInt(id);
FastNMS.INSTANCE.method$ClientboundSetEntityDataPacket$pack(packedItems, buf);
}
}
}

View File

@@ -1,10 +1,10 @@
package net.momirealms.craftengine.bukkit.plugin.network.handler;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.bukkit.entity.data.TextDisplayEntityData;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.util.ComponentUtils;
import net.momirealms.craftengine.bukkit.util.EntityDataUtils;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.network.ByteBufPacketEvent;
@@ -31,7 +31,7 @@ public class TextDisplayPacketHandler implements EntityPacketHandler {
for (int i = 0; i < packedItems.size(); i++) {
Object packedItem = packedItems.get(i);
int entityDataId = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$id(packedItem);
if (entityDataId != EntityDataUtils.TEXT_DATA_ID) continue;
if (entityDataId != TextDisplayEntityData.Text.id()) continue;
Object textComponent = FastNMS.INSTANCE.field$SynchedEntityData$DataValue$value(packedItem);
if (textComponent == CoreReflections.instance$Component$empty) break;
String json = ComponentUtils.minecraftToJson(textComponent);

View File

@@ -1,10 +1,12 @@
package net.momirealms.craftengine.bukkit.plugin.reflection.bukkit;
import net.momirealms.craftengine.bukkit.plugin.reflection.ReflectionInitException;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.util.BukkitReflectionUtils;
import net.momirealms.craftengine.core.util.ReflectionUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
import org.bukkit.NamespacedKey;
import org.bukkit.Server;
import org.bukkit.block.BlockState;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.HumanEntity;
@@ -12,6 +14,8 @@ import org.bukkit.event.block.BlockPhysicsEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.inventory.*;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@@ -201,6 +205,17 @@ public final class CraftBukkitReflections {
ReflectionUtils.getDeclaredField(clazz$CraftServer, CoreReflections.clazz$DedicatedPlayerList, 0)
);
public static final MethodHandle methodHandle$CraftServer$playerListGetter;
static {
try {
methodHandle$CraftServer$playerListGetter = ReflectionUtils.unreflectGetter(field$CraftServer$playerList)
.asType(MethodType.methodType(Object.class, Server.class));
} catch (Exception e) {
throw new ReflectionInitException("Failed to initialize methodHandle$CraftServer$playerList", e);
}
}
public static final Class<?> clazz$CraftInventoryCrafting = requireNonNull(
ReflectionUtils.getClazz(BukkitReflectionUtils.assembleCBClass("inventory.CraftInventoryCrafting"))
);
@@ -353,4 +368,8 @@ public final class CraftBukkitReflections {
public static final Method method$Level$getCraftWorld = requireNonNull(
ReflectionUtils.getMethod(CoreReflections.clazz$Level, clazz$CraftWorld)
);
public static final Class<?> clazz$MinecraftInventory = requireNonNull(
ReflectionUtils.getClazz(BukkitReflectionUtils.assembleCBClass("inventory.CraftInventoryCustom$MinecraftInventory"))
);
}

View File

@@ -14,6 +14,7 @@ import net.momirealms.craftengine.core.util.ReflectionUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
import java.io.BufferedReader;
import java.io.Reader;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.lang.invoke.VarHandle;
@@ -97,6 +98,17 @@ public final class CoreReflections {
public static final Method method$FileToIdConverter$json = requireNonNull(
ReflectionUtils.getStaticMethod(clazz$FileToIdConverter, clazz$FileToIdConverter, String.class)
);
public static final MethodHandle methodHandle$FileToIdConverter$json;
static {
try {
methodHandle$FileToIdConverter$json = ReflectionUtils.unreflectMethod(method$FileToIdConverter$json)
.asType(MethodType.methodType(Object.class, String.class));
} catch (Throwable t) {
throw new ReflectionInitException("Failed to initialize methodHandle$FileToIdConverter$json", t);
}
}
public static final Class<?> clazz$RegistryOps = requireNonNull(
BukkitReflectionUtils.findReobfOrMojmapClass(
@@ -1900,17 +1912,42 @@ public final class CoreReflections {
)
);
// 1.20.1-1.21.1
public static final Field field$Ingredient$itemStacks1_20_1 =
ReflectionUtils.getDeclaredField(clazz$Ingredient, clazz$ItemStack.arrayType(), 0);
public static final Field field$Ingredient$itemStacks = requireNonNull(
ReflectionUtils.getDeclaredField(
clazz$Ingredient,
VersionHelper.isOrAbove1_21_4() ? Set.class : VersionHelper.isOrAbove1_21_2() ? List.class : clazz$ItemStack.arrayType(),
VersionHelper.isOrAbove1_21_4() ? 0 : VersionHelper.isOrAbove1_21_2() ? 1 : 0
)
);
// 1.21.2-1.21.3
public static final Field field$Ingredient$itemStacks1_21_2 =
ReflectionUtils.getDeclaredField(clazz$Ingredient, List.class, 1);
public static final MethodHandle methodHandle$RecipeManager$finalizeRecipeLoading;
public static final MethodHandle methodHandle$RecipeManager$featureflagsetGetter;
public static final MethodHandle methodHandle$RecipeManager$featureflagsetSetter;
public static final MethodHandle methodHandle$Ingredient$itemStacksSetter;
// 1.21.4 paper
public static final Field field$Ingredient$itemStacks1_21_4 =
ReflectionUtils.getDeclaredField(clazz$Ingredient, Set.class, 0);
static {
try {
if (method$RecipeManager$finalizeRecipeLoading != null) {
methodHandle$RecipeManager$finalizeRecipeLoading = ReflectionUtils.unreflectMethod(method$RecipeManager$finalizeRecipeLoading)
.asType(MethodType.methodType(void.class, Object.class));
} else {
methodHandle$RecipeManager$finalizeRecipeLoading = null;
}
if (field$RecipeManager$featureflagset != null) {
methodHandle$RecipeManager$featureflagsetGetter = ReflectionUtils.unreflectGetter(field$RecipeManager$featureflagset)
.asType(MethodType.methodType(Object.class, Object.class));
methodHandle$RecipeManager$featureflagsetSetter = ReflectionUtils.unreflectSetter(field$RecipeManager$featureflagset)
.asType(MethodType.methodType(void.class, Object.class, Object.class));
} else {
methodHandle$RecipeManager$featureflagsetGetter = null;
methodHandle$RecipeManager$featureflagsetSetter = null;
}
methodHandle$Ingredient$itemStacksSetter = ReflectionUtils.unreflectSetter(field$Ingredient$itemStacks)
.asType(MethodType.methodType(void.class, Object.class, VersionHelper.isOrAbove1_21_4() ? Set.class : VersionHelper.isOrAbove1_21_2() ? List.class : Object.class));
} catch (Exception e) {
throw new ReflectionInitException("Failed to initialize CoreReflections", e);
}
}
// Since 1.21.2, exact has been removed
public static final Field field$Ingredient$exact =
@@ -1928,7 +1965,7 @@ public final class CoreReflections {
ReflectionUtils.getClazz(BukkitReflectionUtils.assembleMCClass("world.item.crafting.ShapedRecipePattern"));
// 1.20.1-1.20.2
public static final Field field$1_20_1$ShapedRecipe$recipeItems=
public static final Field field$1_20_1$ShapedRecipe$recipeItems =
ReflectionUtils.getDeclaredField(clazz$ShapedRecipe, clazz$NonNullList, 0);
// 1.20.3+
@@ -1992,6 +2029,70 @@ public final class CoreReflections {
Optional.ofNullable(ReflectionUtils.getDeclaredField(clazz$ShapelessRecipe, List.class, 0))
.orElse(ReflectionUtils.getDeclaredField(clazz$ShapelessRecipe, clazz$NonNullList, 0));
public static final MethodHandle methodHandle$1_20_1$ShapedRecipe$recipeItemsGetter;
public static final MethodHandle methodHandle$1_20_3$ShapedRecipe$patternGetter;
public static final MethodHandle methodHandle$ShapedRecipePattern$ingredients1_20_3Getter;
public static final MethodHandle methodHandle$ShapedRecipePattern$ingredients1_21_2Getter;
public static final MethodHandle methodHandle$Ingredient$valuesGetter;
public static final MethodHandle methodHandle$ShapelessRecipe$placementInfoSetter;
public static final MethodHandle methodHandle$ShapedRecipe$placementInfoSetter;
public static final MethodHandle methodHandle$ShapelessRecipe$ingredientsGetter;
static {
try {
if (field$1_20_1$ShapedRecipe$recipeItems != null) {
methodHandle$1_20_1$ShapedRecipe$recipeItemsGetter = ReflectionUtils.unreflectGetter(field$1_20_1$ShapedRecipe$recipeItems)
.asType(MethodType.methodType(List.class, Object.class));
} else {
methodHandle$1_20_1$ShapedRecipe$recipeItemsGetter = null;
}
if (field$1_20_3$ShapedRecipe$pattern != null) {
methodHandle$1_20_3$ShapedRecipe$patternGetter = ReflectionUtils.unreflectGetter(field$1_20_3$ShapedRecipe$pattern)
.asType(MethodType.methodType(Object.class, Object.class));
} else {
methodHandle$1_20_3$ShapedRecipe$patternGetter = null;
}
if (field$ShapedRecipePattern$ingredients1_20_3 != null) {
methodHandle$ShapedRecipePattern$ingredients1_20_3Getter = ReflectionUtils.unreflectGetter(field$ShapedRecipePattern$ingredients1_20_3)
.asType(MethodType.methodType(List.class, Object.class));
} else {
methodHandle$ShapedRecipePattern$ingredients1_20_3Getter = null;
}
if (field$ShapedRecipePattern$ingredients1_21_2 != null) {
methodHandle$ShapedRecipePattern$ingredients1_21_2Getter = ReflectionUtils.unreflectGetter(field$ShapedRecipePattern$ingredients1_21_2)
.asType(MethodType.methodType(List.class, Object.class));
} else {
methodHandle$ShapedRecipePattern$ingredients1_21_2Getter = null;
}
if (field$Ingredient$values != null) {
methodHandle$Ingredient$valuesGetter = ReflectionUtils.unreflectGetter(field$Ingredient$values)
.asType(MethodType.methodType(Object[].class, Object.class));
} else {
methodHandle$Ingredient$valuesGetter = null;
}
if (field$ShapelessRecipe$placementInfo != null) {
methodHandle$ShapelessRecipe$placementInfoSetter = ReflectionUtils.unreflectSetter(field$ShapelessRecipe$placementInfo)
.asType(MethodType.methodType(void.class, Object.class, Object.class));
} else {
methodHandle$ShapelessRecipe$placementInfoSetter = null;
}
if (field$ShapedRecipe$placementInfo != null) {
methodHandle$ShapedRecipe$placementInfoSetter = ReflectionUtils.unreflectSetter(field$ShapedRecipe$placementInfo)
.asType(MethodType.methodType(void.class, Object.class, Object.class));
} else {
methodHandle$ShapedRecipe$placementInfoSetter = null;
}
if (field$ShapelessRecipe$ingredients != null) {
methodHandle$ShapelessRecipe$ingredientsGetter = ReflectionUtils.unreflectGetter(field$ShapelessRecipe$ingredients)
.asType(MethodType.methodType(List.class, Object.class));
} else {
methodHandle$ShapelessRecipe$ingredientsGetter = null;
}
} catch (Exception e) {
throw new ReflectionInitException("Failed to initialize CoreReflections", e);
}
}
// require ResourceLocation for 1.20.1-1.21.1
// require ResourceKey for 1.21.2+
public static final Method method$RecipeManager$byKey;
@@ -2093,6 +2194,28 @@ public final class CoreReflections {
.map(it -> ReflectionUtils.getDeclaredField(it, clazz$Ingredient, 0))
.orElse(null);
public static final MethodHandle methodHandle$AbstractCookingRecipe$inputGetter;
public static final MethodHandle methodHandle$SingleItemRecipe$inputGetter;
static {
try {
if (field$AbstractCookingRecipe$input != null) {
methodHandle$AbstractCookingRecipe$inputGetter = ReflectionUtils.unreflectGetter(field$AbstractCookingRecipe$input)
.asType(MethodType.methodType(Object.class, Object.class));
} else {
methodHandle$AbstractCookingRecipe$inputGetter = null;
}
if (field$SingleItemRecipe$input != null) {
methodHandle$SingleItemRecipe$inputGetter = ReflectionUtils.unreflectGetter(field$SingleItemRecipe$input)
.asType(MethodType.methodType(Object.class, Object.class));
} else {
methodHandle$SingleItemRecipe$inputGetter = null;
}
} catch (Exception e) {
throw new ReflectionInitException("Failed to initialize methodHandle$SingleItemRecipe$inputGetter", e);
}
}
public static final Field field$AbstractFurnaceBlockEntity$quickCheck = requireNonNull(
ReflectionUtils.getDeclaredField(clazz$AbstractFurnaceBlockEntity, clazz$RecipeManager$CachedCheck, 0)
);
@@ -3019,6 +3142,17 @@ public final class CoreReflections {
ReflectionUtils.getMethod(clazz$Resource, BufferedReader.class)
);
public static final MethodHandle methodHandle$Resource$openAsReader;
static {
try {
methodHandle$Resource$openAsReader = ReflectionUtils.unreflectMethod(method$Resource$openAsReader)
.asType(MethodType.methodType(Reader.class, Object.class));
} catch (Exception e) {
throw new ReflectionInitException("Failed to init methodHandle$Resource$openAsReader", e);
}
}
public static final Class<?> clazz$MultiPackResourceManager = requireNonNull(
BukkitReflectionUtils.findReobfOrMojmapClass(
"server.packs.resources.ResourceManager",
@@ -3063,6 +3197,20 @@ public final class CoreReflections {
ReflectionUtils.getInstanceDeclaredField(clazz$PackRepository, List.class, 0)
);
public static final MethodHandle methodHandle$MinecraftServer$getPackRepository;
public static final MethodHandle methodHandle$PackRepository$selectedGetter;
static {
try {
methodHandle$MinecraftServer$getPackRepository = ReflectionUtils.unreflectMethod(method$MinecraftServer$getPackRepository)
.asType(MethodType.methodType(Object.class, Object.class));
methodHandle$PackRepository$selectedGetter = ReflectionUtils.unreflectGetter(field$PackRepository$selected)
.asType(MethodType.methodType(List.class, Object.class));
} catch (Exception e) {
throw new ReflectionInitException("Failed to initialize reflection for methodHandle$MinecraftServer$getPackRepository", e);
}
}
public static final Class<?> clazz$Pack = requireNonNull(
BukkitReflectionUtils.findReobfOrMojmapClass(
"server.packs.repository.ResourcePackLoader",
@@ -3109,6 +3257,23 @@ public final class CoreReflections {
ReflectionUtils.getMethod(clazz$DedicatedPlayerList, new String[] {"reloadRecipeData", "reloadRecipes"})
);
public static final MethodHandle methodHandle$DedicatedPlayerList$reloadRecipes;
public static final MethodHandle methodHandle$Pack$open;
public static final MethodHandle methodHandle$MultiPackResourceManagerConstructor;
static {
try {
methodHandle$DedicatedPlayerList$reloadRecipes = ReflectionUtils.unreflectMethod(method$DedicatedPlayerList$reloadRecipes)
.asType(MethodType.methodType(void.class, Object.class));
methodHandle$Pack$open = ReflectionUtils.unreflectMethod(method$Pack$open)
.asType(MethodType.methodType(Object.class, Object.class));
methodHandle$MultiPackResourceManagerConstructor = ReflectionUtils.unreflectConstructor(constructor$MultiPackResourceManager)
.asType(MethodType.methodType(AutoCloseable.class, Object.class, List.class));
} catch (Exception e) {
throw new ReflectionInitException("Failed to init methodHandle$DedicatedPlayerList$reloadRecipes", e);
}
}
public static final Method method$ServerChunkCache$getGenerator = requireNonNull(
ReflectionUtils.getMethod(clazz$ServerChunkCache, clazz$ChunkGenerator)
);
@@ -3315,6 +3480,17 @@ public final class CoreReflections {
ReflectionUtils.getMethod(clazz$FileToIdConverter, Map.class, new String[]{"listMatchingResources", "a"}, clazz$ResourceManager)
);
public static final MethodHandle methodHandle$FileToIdConverter$listMatchingResources;
static {
try {
methodHandle$FileToIdConverter$listMatchingResources = ReflectionUtils.unreflectMethod(method$FileToIdConverter$listMatchingResources)
.asType(MethodType.methodType(Map.class, Object.class, AutoCloseable.class));
} catch (Exception e) {
throw new ReflectionInitException("Failed to initialize methodHandle$FileToIdConverter$listMatchingResources", e);
}
}
public static final Method method$RegistryOps$create = requireNonNull(
ReflectionUtils.getStaticMethod(clazz$RegistryOps, clazz$RegistryOps, DynamicOps.class, clazz$HolderLookup$Provider)
);

View File

@@ -0,0 +1,49 @@
package net.momirealms.craftengine.bukkit.plugin.reflection.minecraft;
//import net.momirealms.craftengine.core.util.MiscUtils;
//import net.momirealms.craftengine.core.util.ReflectionUtils;
//import net.momirealms.craftengine.core.util.VersionHelper;
//import org.bukkit.event.HandlerList;
//
//import java.lang.reflect.Field;
//import java.util.Optional;
import net.momirealms.craftengine.core.util.ReflectionUtils;
import java.lang.reflect.Field;
import java.util.List;
// TODO API 太新了需要1.21.8,目前先采用其他方式解决假人问题
public final class LeavesReflections {
private LeavesReflections() {}
// public static final Class<?> clazz$BotJoinEvent = MiscUtils.requireNonNullIf(ReflectionUtils.getClazz("org.leavesmc.leaves.event.bot.BotJoinEvent"), VersionHelper.isLeaves());
//
// public static final Class<?> clazz$BotRemoveEvent = MiscUtils.requireNonNullIf(ReflectionUtils.getClazz("org.leavesmc.leaves.event.bot.BotRemoveEvent"), VersionHelper.isLeaves());
//
// public static final Class<?> clazz$BotEvent = MiscUtils.requireNonNullIf(ReflectionUtils.getClazz("org.leavesmc.leaves.event.bot.BotEvent"), VersionHelper.isLeaves());
//
// public static final Class<?> clazz$Bot = MiscUtils.requireNonNullIf(ReflectionUtils.getClazz("org.leavesmc.leaves.entity.bot.Bot"), VersionHelper.isLeaves());
//
// public static final Field field$BotEvent$bot = Optional.ofNullable(clazz$BotEvent)
// .map(it -> ReflectionUtils.getDeclaredField(it, clazz$Bot, 0))
// .orElse(null);
//
// public static final Field field$BotJoinEvent$handlers = Optional.ofNullable(clazz$BotJoinEvent)
// .map(it -> ReflectionUtils.getDeclaredField(it, HandlerList.class, 0))
// .orElse(null);
//
// public static final Field field$BotRemoveEvent$handlers = Optional.ofNullable(clazz$BotRemoveEvent)
// .map(it -> ReflectionUtils.getDeclaredField(it, HandlerList.class, 0))
// .orElse(null);
public static final Class<?> clazz$ServerBot = ReflectionUtils.getClazz("org.leavesmc.leaves.bot.ServerBot");
// 注入BotList来实现全版本的监听
public static final Class<?> clazz$BotList = ReflectionUtils.getClazz("org.leavesmc.leaves.bot.BotList");
public static final Field field$BotList$INSTANCE = ReflectionUtils.getDeclaredField(clazz$BotList, clazz$BotList, 0);
public static final Field field$BotList$bots = ReflectionUtils.getDeclaredField(clazz$BotList, List.class, 0);
}

View File

@@ -20,6 +20,8 @@ public final class MBlocks {
public static final Object SNOW;
public static final Object WATER;
public static final Object WATER$defaultState;
public static final Object TNT;
public static final Object TNT$defaultState;
private static Object getById(String id) {
Object rl = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", id);
@@ -41,5 +43,7 @@ public final class MBlocks {
SNOW = getById("snow");
WATER = getById("water");
WATER$defaultState = FastNMS.INSTANCE.method$Block$defaultState(WATER);
TNT = getById("tnt");
TNT$defaultState = FastNMS.INSTANCE.method$Block$defaultState(TNT);
}
}

View File

@@ -58,6 +58,24 @@ public final class MEntityTypes {
public static final int HAPPY_GHAST$registryId;
public static final Object PLAYER;
public static final int PLAYER$registryId;
public static final Object ENDERMAN;
public static final int ENDERMAN$registryId;
public static final Object TNT;
public static final int TNT$registryId;
public static final Object CHEST_MINECART;
public static final int CHEST_MINECART$registryId;
public static final Object COMMAND_BLOCK_MINECART;
public static final int COMMAND_BLOCK_MINECART$registryId;
public static final Object FURNACE_MINECART;
public static final int FURNACE_MINECART$registryId;
public static final Object HOPPER_MINECART;
public static final int HOPPER_MINECART$registryId;
public static final Object MINECART;
public static final int MINECART$registryId;
public static final Object SPAWNER_MINECART;
public static final int SPAWNER_MINECART$registryId;
public static final Object TNT_MINECART;
public static final int TNT_MINECART$registryId;
private static Object getById(String id) {
Object rl = FastNMS.INSTANCE.method$ResourceLocation$fromNamespaceAndPath("minecraft", id);
@@ -122,5 +140,23 @@ public final class MEntityTypes {
ARROW$registryId = getRegistryId(ARROW);
SPECTRAL_ARROW = getById("spectral_arrow");
SPECTRAL_ARROW$registryId = getRegistryId(SPECTRAL_ARROW);
ENDERMAN = getById("enderman");
ENDERMAN$registryId = getRegistryId(ENDERMAN);
TNT = getById("tnt");
TNT$registryId = getRegistryId(TNT);
CHEST_MINECART = getById("chest_minecart");
CHEST_MINECART$registryId = getRegistryId(CHEST_MINECART);
COMMAND_BLOCK_MINECART = getById("command_block_minecart");
COMMAND_BLOCK_MINECART$registryId = getRegistryId(COMMAND_BLOCK_MINECART);
FURNACE_MINECART = getById("furnace_minecart");
FURNACE_MINECART$registryId = getRegistryId(FURNACE_MINECART);
HOPPER_MINECART = getById("hopper_minecart");
HOPPER_MINECART$registryId = getRegistryId(HOPPER_MINECART);
MINECART = getById("minecart");
MINECART$registryId = getRegistryId(MINECART);
SPAWNER_MINECART = getById("spawner_minecart");
SPAWNER_MINECART$registryId = getRegistryId(SPAWNER_MINECART);
TNT_MINECART = getById("tnt_minecart");
TNT_MINECART$registryId = getRegistryId(TNT_MINECART);
}
}

View File

@@ -11,13 +11,13 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.gui.CraftEngineInventoryHolder;
import net.momirealms.craftengine.bukkit.plugin.network.payload.DiscardedPayload;
import net.momirealms.craftengine.bukkit.plugin.reflection.bukkit.CraftBukkitReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MAttributeHolders;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MMobEffects;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections;
import net.momirealms.craftengine.bukkit.util.*;
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
import net.momirealms.craftengine.core.block.BlockSettings;
import net.momirealms.craftengine.core.block.BlockStateWrapper;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.entity.player.GameMode;
@@ -111,14 +111,16 @@ public class BukkitServerPlayer extends Player {
private final Map<Integer, EntityPacketHandler> entityTypeView = new ConcurrentHashMap<>();
public BukkitServerPlayer(BukkitCraftEngine plugin, Channel channel) {
public BukkitServerPlayer(BukkitCraftEngine plugin, @Nullable Channel channel) {
this.channel = channel;
this.plugin = plugin;
for (String name : channel.pipeline().names()) {
ChannelHandler handler = channel.pipeline().get(name);
if (NetworkReflections.clazz$Connection.isInstance(handler)) {
this.connection = handler;
break;
if (channel != null) {
for (String name : channel.pipeline().names()) {
ChannelHandler handler = channel.pipeline().get(name);
if (NetworkReflections.clazz$Connection.isInstance(handler)) {
this.connection = handler;
break;
}
}
}
}
@@ -185,6 +187,7 @@ public class BukkitServerPlayer extends Player {
};
}
@SuppressWarnings("UnstableApiUsage")
@Override
public void setGameMode(GameMode gameMode) {
platformPlayer().setGameMode(Objects.requireNonNull(org.bukkit.GameMode.getByValue(gameMode.id())));
@@ -327,22 +330,37 @@ public class BukkitServerPlayer extends Player {
}
@Override
public void sendCustomPayload(Key channel, byte[] data) {
public void sendPackets(List<Object> packet, boolean immediately) {
this.plugin.networkManager().sendPackets(this, packet, immediately);
}
@Override
public void sendPackets(List<Object> packet, boolean immediately, Runnable sendListener) {
this.plugin.networkManager().sendPackets(this, packet, immediately, sendListener);
}
@Override
public void simulatePacket(Object packet) {
this.plugin.networkManager().simulatePacket(this, packet);
}
@Override
public void sendCustomPayload(Key channelId, byte[] data) {
try {
Object channelKey = KeyUtils.toResourceLocation(channel);
Object channelResourceLocation = KeyUtils.toResourceLocation(channelId);
Object responsePacket;
if (VersionHelper.isOrAbove1_20_2()) {
Object dataPayload;
if (NetworkReflections.clazz$UnknownPayload != null) {
dataPayload = NetworkReflections.constructor$UnknownPayload.newInstance(channelKey, Unpooled.wrappedBuffer(data));
dataPayload = NetworkReflections.constructor$UnknownPayload.newInstance(channelResourceLocation, Unpooled.wrappedBuffer(data));
} else if (DiscardedPayload.useNewMethod) {
dataPayload = NetworkReflections.constructor$DiscardedPayload.newInstance(channelKey, data);
dataPayload = NetworkReflections.constructor$DiscardedPayload.newInstance(channelResourceLocation, data);
} else {
dataPayload = NetworkReflections.constructor$DiscardedPayload.newInstance(channelKey, Unpooled.wrappedBuffer(data));
dataPayload = NetworkReflections.constructor$DiscardedPayload.newInstance(channelResourceLocation, Unpooled.wrappedBuffer(data));
}
responsePacket = NetworkReflections.constructor$ClientboundCustomPayloadPacket.newInstance(dataPayload);
} else {
responsePacket = NetworkReflections.constructor$ClientboundCustomPayloadPacket.newInstance(channelKey, FastNMS.INSTANCE.constructor$FriendlyByteBuf(Unpooled.wrappedBuffer(data)));
responsePacket = NetworkReflections.constructor$ClientboundCustomPayloadPacket.newInstance(channelResourceLocation, FastNMS.INSTANCE.constructor$FriendlyByteBuf(Unpooled.wrappedBuffer(data)));
}
this.sendPacket(responsePacket, true);
} catch (Exception e) {
@@ -364,21 +382,6 @@ public class BukkitServerPlayer extends Player {
}
}
@Override
public void sendPackets(List<Object> packet, boolean immediately) {
this.plugin.networkManager().sendPackets(this, packet, immediately);
}
@Override
public void sendPackets(List<Object> packet, boolean immediately, Runnable sendListener) {
this.plugin.networkManager().sendPackets(this, packet, immediately, sendListener);
}
@Override
public void simulatePacket(Object packet) {
this.plugin.networkManager().simulatePacket(this, packet);
}
@Override
public ConnectionState decoderState() {
return decoderState;
@@ -456,6 +459,9 @@ public class BukkitServerPlayer extends Player {
private void updateGUI() {
org.bukkit.inventory.Inventory top = !VersionHelper.isOrAbove1_21() ? LegacyInventoryUtils.getTopInventory(platformPlayer()) : platformPlayer().getOpenInventory().getTopInventory();
if (!CraftBukkitReflections.clazz$MinecraftInventory.isInstance(FastNMS.INSTANCE.method$CraftInventory$getInventory(top))) {
return;
}
if (top.getHolder() instanceof CraftEngineInventoryHolder holder) {
holder.gui().onTimer();
}
@@ -463,7 +469,21 @@ public class BukkitServerPlayer extends Player {
@Override
public float getDestroyProgress(Object blockState, BlockPos pos) {
return FastNMS.INSTANCE.method$BlockStateBase$getDestroyProgress(blockState, serverPlayer(), FastNMS.INSTANCE.field$CraftWorld$ServerLevel(platformPlayer().getWorld()), LocationUtils.toBlockPos(pos));
Optional<ImmutableBlockState> optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(blockState);
float progress = FastNMS.INSTANCE.method$BlockStateBase$getDestroyProgress(blockState, serverPlayer(), FastNMS.INSTANCE.field$CraftWorld$ServerLevel(platformPlayer().getWorld()), LocationUtils.toBlockPos(pos));
if (optionalCustomState.isPresent()) {
ImmutableBlockState customState = optionalCustomState.get();
Item<ItemStack> tool = getItemInHand(InteractionHand.MAIN_HAND);
boolean isCorrectTool = FastNMS.INSTANCE.method$ItemStack$isCorrectToolForDrops(tool.getLiteralObject(), blockState);
// 如果自定义方块在服务端侧未使用正确地工具,那么需要还原挖掘速度
if (!isCorrectTool) {
progress *= (10f / 3f);
}
if (!BlockStateUtils.isCorrectTool(customState, tool)) {
progress *= customState.settings().incorrectToolSpeed();
}
}
return progress;
}
private void predictNextBlockToMine() {
@@ -648,28 +668,6 @@ public class BukkitServerPlayer extends Player {
// double check custom block
if (optionalCustomState.isPresent()) {
ImmutableBlockState customState = optionalCustomState.get();
BlockSettings blockSettings = customState.settings();
if (blockSettings.requireCorrectTool()) {
if (!item.isEmpty()) {
// it's correct on plugin side
if (blockSettings.isCorrectTool(item.id())) {
// but not on serverside
if (!FastNMS.INSTANCE.method$ItemStack$isCorrectToolForDrops(item.getLiteralObject(), destroyedState)) {
// we fix the speed
progressToAdd = progressToAdd * (10f / 3f);
}
} else {
// not a correct tool on plugin side and not a correct tool on serverside
if (!blockSettings.respectToolComponent() || !FastNMS.INSTANCE.method$ItemStack$isCorrectToolForDrops(item.getLiteralObject(), destroyedState)) {
progressToAdd = progressToAdd * (10f / 3f) * blockSettings.incorrectToolSpeed();
}
}
} else {
// item is null, but it requires correct tool, then we reset the speed
progressToAdd = progressToAdd * (10f / 3f) * blockSettings.incorrectToolSpeed();
}
}
// accumulate progress
this.miningProgress = progressToAdd + miningProgress;
int packetStage = (int) (this.miningProgress * 10.0F);
@@ -857,6 +855,11 @@ public class BukkitServerPlayer extends Player {
return this.connection;
}
@Override
public boolean isFakePlayer() {
return false;
}
@Override
public org.bukkit.entity.Player literalObject() {
return platformPlayer();

View File

@@ -0,0 +1,32 @@
package net.momirealms.craftengine.bukkit.plugin.user;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
public class FakeBukkitServerPlayer extends BukkitServerPlayer {
public FakeBukkitServerPlayer(BukkitCraftEngine plugin) {
super(plugin, null);
}
@Override
public Channel nettyChannel() {
return null;
}
@Override
public ChannelHandler connection() {
return null;
}
@Override
public void kick(Component message) {
}
@Override
public boolean isFakePlayer() {
return true;
}
}

View File

@@ -10,7 +10,7 @@ import org.bukkit.Location;
import org.bukkit.inventory.ItemStack;
@SuppressWarnings("DuplicatedCode")
public class AdventureModeUtils {
public final class AdventureModeUtils {
private AdventureModeUtils() {}

View File

@@ -22,7 +22,7 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
public class BlockStateUtils {
public final class BlockStateUtils {
public static final IdentityHashMap<Object, Object> CLIENT_SIDE_NOTE_BLOCKS = new IdentityHashMap<>();
private static int vanillaStateSize;
private static boolean hasInit;

View File

@@ -7,7 +7,7 @@ import net.momirealms.craftengine.core.util.Key;
import java.util.HashMap;
import java.util.Map;
public class BlockTags {
public final class BlockTags {
private static final Map<Key, Object> CACHE = new HashMap<>();
private BlockTags() {}

View File

@@ -6,7 +6,7 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.paper.PaperReflections;
import net.momirealms.craftengine.core.util.AdventureHelper;
public class ComponentUtils {
public final class ComponentUtils {
private ComponentUtils() {}

View File

@@ -3,7 +3,7 @@ package net.momirealms.craftengine.bukkit.util;
import net.momirealms.craftengine.core.util.DamageSource;
import org.bukkit.event.entity.EntityDamageEvent;
public class DamageCauseUtils {
public final class DamageCauseUtils {
private DamageCauseUtils() {}

View File

@@ -4,7 +4,7 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflect
import net.momirealms.craftengine.core.util.Direction;
import org.bukkit.block.BlockFace;
public class DirectionUtils {
public final class DirectionUtils {
private DirectionUtils() {}

View File

@@ -0,0 +1,6 @@
package net.momirealms.craftengine.bukkit.util;
import org.bukkit.event.Listener;
public class DummyListener implements Listener {
}

View File

@@ -5,7 +5,7 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflect
import java.util.HashMap;
import java.util.Map;
public class EnchantmentUtils {
public final class EnchantmentUtils {
private EnchantmentUtils() {}

View File

@@ -1,8 +1,6 @@
package net.momirealms.craftengine.bukkit.util;
import net.momirealms.craftengine.core.util.VersionHelper;
public class EntityDataUtils {
public final class EntityDataUtils {
private EntityDataUtils() {}
@@ -11,12 +9,7 @@ public class EntityDataUtils {
private static final int USE_DEFAULT_BACKGROUND = 0x04; // 4
private static final int LEFT_ALIGNMENT = 0x08; // 8
private static final int RIGHT_ALIGNMENT = 0x10; // 16
public static final int BLOCK_STATE_DATA_ID = VersionHelper.isOrAbove1_20_2() ? 23 : 22;
public static final int TEXT_DATA_ID = VersionHelper.isOrAbove1_20_2() ? 23 : 22;
public static final int DISPLAYED_ITEM_DATA_ID = VersionHelper.isOrAbove1_20_2() ? 23 : 22;
public static final int CUSTOM_NAME_DATA_ID = 2;
public static final int ITEM_DATA_ID = 8;
public static final int ITEM_FRAME_DATA_ID = VersionHelper.isOrAbove1_21_6() ? 9 : 8;
public static final int UNSAFE_ITEM_DATA_ID = 8; // 正常来说应该通过定义 Data 获取 id 这样的做法未经验证可能不安全
public static byte encodeTextDisplayMask(boolean hasShadow, boolean isSeeThrough, boolean useDefaultBackground, int alignment) {
int bitMask = 0;

View File

@@ -15,7 +15,7 @@ import org.bukkit.event.entity.CreatureSpawnEvent;
import java.util.function.Consumer;
public class EntityUtils {
public final class EntityUtils {
private EntityUtils() {
}

View File

@@ -4,7 +4,7 @@ import org.bukkit.Bukkit;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
public class EventUtils {
public final class EventUtils {
private EventUtils() {}

View File

@@ -5,7 +5,7 @@ import org.bukkit.event.block.BlockExplodeEvent;
import org.bukkit.event.entity.EntityExplodeEvent;
@SuppressWarnings("UnstableApiUsage")
public class ExplosionUtils {
public final class ExplosionUtils {
public static boolean isDroppingItems(BlockExplodeEvent event) {
return event.getExplosionResult() != ExplosionResult.KEEP && event.getExplosionResult() != ExplosionResult.TRIGGER_BLOCK;

View File

@@ -4,7 +4,7 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistries;
import net.momirealms.craftengine.core.util.Key;
public class FeatureUtils {
public final class FeatureUtils {
private FeatureUtils() {}

View File

@@ -3,7 +3,7 @@ package net.momirealms.craftengine.bukkit.util;
import net.momirealms.craftengine.core.world.FluidCollisionRule;
import org.bukkit.FluidCollisionMode;
public class FluidUtils {
public final class FluidUtils {
private FluidUtils() {}

View File

@@ -36,7 +36,7 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
public class InteractUtils {
public final class InteractUtils {
private static final Map<Key, QuadFunction<Player, Item<ItemStack>, BlockData, BlockHitResult, Boolean>> INTERACTIONS = new HashMap<>();
private static final Map<Key, QuadFunction<Player, Item<ItemStack>, BlockData, BlockHitResult, Boolean>> WILL_CONSUME = new HashMap<>();
private static final Map<Key, TriFunction<Player, Entity, @Nullable Item<ItemStack>, Boolean>> ENTITY_INTERACTIONS = new HashMap<>();

View File

@@ -6,7 +6,7 @@ import org.bukkit.event.inventory.InventoryEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
public class InventoryUtils {
public final class InventoryUtils {
private InventoryUtils() {}

View File

@@ -7,7 +7,7 @@ import net.momirealms.craftengine.core.util.Key;
import java.util.HashMap;
import java.util.Map;
public class ItemTags {
public final class ItemTags {
private static final Map<Key, Object> CACHE = new HashMap<>();
public static final Key AXES = Key.of("minecraft:axes");

View File

@@ -9,7 +9,7 @@ import java.util.BitSet;
import java.util.List;
import java.util.Map;
public class LightUtils {
public final class LightUtils {
private LightUtils() {}

View File

@@ -9,7 +9,7 @@ import org.bukkit.Location;
import org.bukkit.World;
import org.jetbrains.annotations.NotNull;
public class LocationUtils {
public final class LocationUtils {
private LocationUtils() {}

View File

@@ -10,7 +10,7 @@ import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
public class MaterialUtils {
public final class MaterialUtils {
public static Material MACE;

View File

@@ -3,7 +3,7 @@ package net.momirealms.craftengine.bukkit.util;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.core.util.Mirror;
public class MirrorUtils {
public final class MirrorUtils {
private MirrorUtils() {}

View File

@@ -2,7 +2,7 @@ package net.momirealms.craftengine.bukkit.util;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections;
public class MobEffectUtils {
public final class MobEffectUtils {
private MobEffectUtils() {}

View File

@@ -2,7 +2,7 @@ package net.momirealms.craftengine.bukkit.util;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
public class NoteBlockChainUpdateUtils {
public final class NoteBlockChainUpdateUtils {
private NoteBlockChainUpdateUtils() {}

View File

@@ -1,6 +0,0 @@
package net.momirealms.craftengine.bukkit.util;
public class OptimizedReflections {
}

Some files were not shown because too many files have changed in this diff Show More