diff --git a/bukkit/loader/src/main/resources/config.yml b/bukkit/loader/src/main/resources/config.yml index 66d1a045c..f9d5fbf47 100644 --- a/bukkit/loader/src/main/resources/config.yml +++ b/bukkit/loader/src/main/resources/config.yml @@ -149,10 +149,10 @@ block: # Documentation: https://mo-mi.gitbook.io/xiaomomi-plugins/craftengine/plugin-wiki/craftengine/add-new-contents/items/item-data/client-bound-item-data simplify-adventure-break-check: false # Whether plugin should predict the next block to break - # This can help improve mining experience to some extent + # This can help improve mining experience to some extent at the cost of performance predict-breaking: - enable: true - interval: 5 + enable: false + interval: 10 extended-interaction-range: 0.5 furniture: diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java index 63627d362..7ac866d3e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java @@ -1267,7 +1267,7 @@ public class PacketConsumers { if (slot - 36 != bukkitPlayer.getInventory().getHeldItemSlot()) { return; } - double interactionRange = player.getInteractionRange(); + double interactionRange = player.getCachedInteractionRange(); // do ray trace to get current block RayTraceResult result = bukkitPlayer.rayTraceBlocks(interactionRange, FluidCollisionMode.NEVER); if (result == null) return; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java index c5028a66f..c2636d665 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java @@ -40,20 +40,22 @@ import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; public class BukkitServerPlayer extends Player { - private final Channel channel; private final BukkitCraftEngine plugin; - + // connection state + private final Channel channel; private ConnectionState decoderState; private ConnectionState encoderState; - + // some references private Reference playerRef; private Reference serverPlayerRef; - + // client side dimension info private int sectionCount; - private int lastSuccessfulInteraction; - private long lastAttributeSyncTime; private Key clientSideDimension; - + // check main hand/offhand interaction + private int lastSuccessfulInteraction; + // re-sync attribute timely to prevent some bugs + private long lastAttributeSyncTime; + // for breaking blocks private int lastSentState = -1; private int lastHitBlockTime; private BlockPos destroyPos; @@ -62,21 +64,28 @@ public class BukkitServerPlayer extends Player { private boolean isDestroyingCustomBlock; private boolean swingHandAck; private float miningProgress; - - private int lastSuccessfulBreak; + // for client visual sync private int resentSoundTick; private int resentSwingTick; - + // cache used recipe private Key lastUsedRecipe = null; - + // has fabric client mod or not private boolean hasClientMod = false; + // cache if player can break blocks + private boolean clientSideCanBreak = true; + // prevent AFK players from consuming too much CPU resource on predicting + private Location previousEyeLocation; + // a cooldown for better breaking experience + private int lastSuccessfulBreak; + // player's game tick + private int gameTicks; + // cache interaction range here + private int lastUpdateInteractionRangeTick; + private double cachedInteractionRange; // for better fake furniture visual sync - // TODO CLEAR ENTITY VIEW private final Map> furnitureView = new ConcurrentHashMap<>(); private final Map entityTypeView = new ConcurrentHashMap<>(); - private boolean clientSideCanBreak = true; - public BukkitServerPlayer(BukkitCraftEngine plugin, Channel channel) { this.channel = channel; this.plugin = plugin; @@ -213,7 +222,7 @@ public class BukkitServerPlayer extends Player { @Override public int gameTicks() { - return FastNMS.INSTANCE.field$MinecraftServer$currentTick(); + return this.gameTicks; } @Override @@ -319,12 +328,18 @@ public class BukkitServerPlayer extends Player { public void tick() { // not fully online if (serverPlayer() == null) return; + this.gameTicks = FastNMS.INSTANCE.field$MinecraftServer$currentTick(); if (this.isDestroyingBlock) { this.tickBlockDestroy(); } - // if it's not destroying custom blocks, we do predict - if (Config.predictBreaking()) { - if (!this.isDestroyingCustomBlock && (gameTicks() + entityID()) % Config.predictBreakingInterval() == 0) { + if (Config.predictBreaking() && !this.isDestroyingCustomBlock) { + // if it's not destroying blocks, we do predict + if ((gameTicks() + entityID()) % Config.predictBreakingInterval() == 0) { + Location eyeLocation = platformPlayer().getEyeLocation(); + if (eyeLocation.equals(this.previousEyeLocation)) { + return; + } + this.previousEyeLocation = eyeLocation; this.predictNextBlockToMine(); } } @@ -336,7 +351,7 @@ public class BukkitServerPlayer extends Player { } private void predictNextBlockToMine() { - double range = getInteractionRange() + Config.extendedInteractionRange(); + double range = getCachedInteractionRange() + Config.extendedInteractionRange(); RayTraceResult result = platformPlayer().rayTraceBlocks(range, FluidCollisionMode.NEVER); if (result == null) { if (!this.clientSideCanBreak) { @@ -467,7 +482,7 @@ public class BukkitServerPlayer extends Player { if (currentTick - this.lastSuccessfulBreak <= 5) return; try { org.bukkit.entity.Player player = platformPlayer(); - double range = getInteractionRange(); + double range = getCachedInteractionRange(); RayTraceResult result = player.rayTraceBlocks(range, FluidCollisionMode.NEVER); if (result == null) return; Block hitBlock = result.getHitBlock(); @@ -589,8 +604,13 @@ public class BukkitServerPlayer extends Player { } @Override - public double getInteractionRange() { - return FastNMS.INSTANCE.getInteractionRange(serverPlayer()); + public double getCachedInteractionRange() { + if (this.lastUpdateInteractionRangeTick + 20 > gameTicks()) { + return this.cachedInteractionRange; + } + this.cachedInteractionRange = FastNMS.INSTANCE.getInteractionRange(serverPlayer()); + this.lastUpdateInteractionRangeTick = gameTicks(); + return this.cachedInteractionRange; } public void setIsDestroyingBlock(boolean is, boolean custom) { diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java b/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java index b2caf91c5..6da600d33 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java @@ -35,7 +35,7 @@ public abstract class Player extends Entity implements NetWorkUser { public abstract void abortMiningBlock(); - public abstract double getInteractionRange(); + public abstract double getCachedInteractionRange(); public abstract void onSwingHand(); diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/CompositeItemModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/CompositeItemModel.java index f37e3aeed..58487f35e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/CompositeItemModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/CompositeItemModel.java @@ -53,6 +53,9 @@ public class CompositeItemModel implements ItemModel { @Override public ItemModel create(Map arguments) { List> models = (List>) arguments.get("models"); + if (models == null || models.isEmpty()) { + throw new IllegalArgumentException("No 'models' specified for 'minecraft:composite'"); + } List modelList = new ArrayList<>(); for (Map model : models) { modelList.add(ItemModels.fromMap(model)); diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/ConditionItemModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/ConditionItemModel.java index 17a962483..f30d9878f 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/ConditionItemModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/ConditionItemModel.java @@ -64,8 +64,8 @@ public class ConditionItemModel implements ItemModel { @Override public ItemModel create(Map arguments) { ConditionProperty property = ConditionProperties.fromMap(arguments); - Map onTrue = Objects.requireNonNull((Map) arguments.get("on-true")); - Map onFalse = Objects.requireNonNull((Map) arguments.get("on-false")); + Map onTrue = Objects.requireNonNull((Map) arguments.get("on-true"), "No 'on-true' set for 'minecraft:condition'"); + Map onFalse = Objects.requireNonNull((Map) arguments.get("on-false"), "No 'on-false' set for 'minecraft:condition'"); return new ConditionItemModel(property, ItemModels.fromMap(onTrue), ItemModels.fromMap(onFalse)); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/RangeDispatchItemModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/RangeDispatchItemModel.java index 505059880..1efe99241 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/RangeDispatchItemModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/RangeDispatchItemModel.java @@ -108,7 +108,7 @@ public class RangeDispatchItemModel implements ItemModel { } return new RangeDispatchItemModel(property, scale, fallback == null ? null : ItemModels.fromMap(fallback), entryMap); } - throw new IllegalArgumentException("No entries set for range dispatch"); + throw new IllegalArgumentException("No 'entries' set for 'minecraft:range_dispatch'"); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/model/SelectItemModel.java b/core/src/main/java/net/momirealms/craftengine/core/pack/model/SelectItemModel.java index d11397212..38ed5829a 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/model/SelectItemModel.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/model/SelectItemModel.java @@ -117,7 +117,7 @@ public class SelectItemModel implements ItemModel { } return new SelectItemModel(property, whenMap, fallback == null ? null : ItemModels.fromMap(fallback)); } - throw new IllegalArgumentException("No cases set for select"); + throw new IllegalArgumentException("No 'cases' set for 'minecraft:select'"); } } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java b/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java index d6fda7904..283afc968 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java +++ b/core/src/main/java/net/momirealms/craftengine/core/util/MiscUtils.java @@ -21,7 +21,7 @@ public class MiscUtils { if (obj instanceof Map map) { return (Map) map; } - throw new IllegalArgumentException("Expected Map, got: " + obj.getClass().getSimpleName()); + throw new IllegalArgumentException("Expected Map, got: " + (obj == null ? null : obj.getClass().getSimpleName())); } @SuppressWarnings("unchecked")