mirror of
https://github.com/Xiao-MoMi/craft-engine.git
synced 2026-01-04 15:41:38 +00:00
Merge branch 'Xiao-MoMi:dev' into dev
This commit is contained in:
@@ -17,6 +17,7 @@ import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.context.ContextHolder;
|
||||
import net.momirealms.craftengine.core.world.Vec3d;
|
||||
import net.momirealms.craftengine.core.world.World;
|
||||
import net.momirealms.craftengine.core.world.WorldEvents;
|
||||
import net.momirealms.sparrow.nbt.CompoundTag;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
@@ -183,7 +184,7 @@ public final class CraftEngineBlocks {
|
||||
world.playBlockSound(vec3d, state.sounds().breakSound());
|
||||
}
|
||||
if (sendParticles) {
|
||||
FastNMS.INSTANCE.method$Level$levelEvent(world.serverWorld(), 2001, LocationUtils.toBlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()), state.customBlockState().registryId());
|
||||
FastNMS.INSTANCE.method$Level$levelEvent(world.serverWorld(), WorldEvents.BLOCK_BREAK_EFFECT, LocationUtils.toBlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()), state.customBlockState().registryId());
|
||||
}
|
||||
block.setType(Material.AIR, applyPhysics);
|
||||
return true;
|
||||
|
||||
@@ -7,15 +7,14 @@ import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
|
||||
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.ImmutableBlockState;
|
||||
import net.momirealms.craftengine.core.block.properties.Property;
|
||||
import net.momirealms.craftengine.core.entity.player.InteractionHand;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.item.ItemKeys;
|
||||
import net.momirealms.craftengine.core.loot.LootTable;
|
||||
import net.momirealms.craftengine.core.loot.parameter.LootParameters;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import net.momirealms.craftengine.core.util.context.ContextHolder;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
@@ -38,7 +37,6 @@ import org.bukkit.event.world.GenericGameEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class BlockEventListener implements Listener {
|
||||
private final BukkitCraftEngine plugin;
|
||||
@@ -95,7 +93,7 @@ public class BlockEventListener implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) // I forget why it's LOW before
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
|
||||
public void onPlayerBreak(BlockBreakEvent event) {
|
||||
org.bukkit.block.Block block = event.getBlock();
|
||||
Object blockState = BlockStateUtils.blockDataToBlockState(block.getBlockData());
|
||||
@@ -105,6 +103,11 @@ public class BlockEventListener implements Listener {
|
||||
ImmutableBlockState state = manager.getImmutableBlockStateUnsafe(stateId);
|
||||
if (!state.isEmpty()) {
|
||||
Location location = block.getLocation();
|
||||
BukkitServerPlayer serverPlayer = this.plugin.adapt(player);
|
||||
// double check adventure mode to prevent dupe
|
||||
if (!FastNMS.INSTANCE.mayBuild(serverPlayer.serverPlayer()) && !serverPlayer.canBreak(LocationUtils.toBlockPos(location), null)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// trigger event
|
||||
CustomBlockBreakEvent customBreakEvent = new CustomBlockBreakEvent(event.getPlayer(), location, block, state);
|
||||
@@ -127,16 +130,19 @@ public class BlockEventListener implements Listener {
|
||||
// play sound
|
||||
Vec3d vec3d = new Vec3d(location.getBlockX() + 0.5, location.getBlockY() + 0.5, location.getBlockZ() + 0.5);
|
||||
world.playBlockSound(vec3d, state.sounds().breakSound());
|
||||
if (player.getGameMode() == GameMode.CREATIVE) {
|
||||
if (player.getGameMode() == GameMode.CREATIVE || !customBreakEvent.dropItems()) {
|
||||
return;
|
||||
}
|
||||
|
||||
BukkitServerPlayer serverPlayer = this.plugin.adapt(player);
|
||||
Item<ItemStack> itemInHand = serverPlayer.getItemInHand(InteractionHand.MAIN_HAND);
|
||||
Key itemId = Optional.ofNullable(itemInHand).map(Item::id).orElse(ItemKeys.AIR);
|
||||
// do not drop if it's not the correct tool
|
||||
if (!state.settings().isCorrectTool(itemId) || !customBreakEvent.dropItems()) {
|
||||
return;
|
||||
BlockSettings settings = state.settings();
|
||||
if (settings.requireCorrectTool()) {
|
||||
if (itemInHand == null) return;
|
||||
if (!settings.isCorrectTool(itemInHand.id()) &&
|
||||
(!settings.respectToolComponent() || !FastNMS.INSTANCE.method$ItemStack$isCorrectToolForDrops(itemInHand.getLiteralObject(), state.customBlockState().handle()))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// drop items
|
||||
ContextHolder.Builder builder = ContextHolder.builder();
|
||||
@@ -336,25 +342,21 @@ public class BlockEventListener implements Listener {
|
||||
if (!this.enableNoteBlockCheck) return;
|
||||
// for vanilla blocks
|
||||
if (event.getChangedType() == Material.NOTE_BLOCK) {
|
||||
try {
|
||||
Block block = event.getBlock();
|
||||
World world = block.getWorld();
|
||||
Location location = block.getLocation();
|
||||
Block sourceBlock = event.getSourceBlock();
|
||||
BlockFace direction = sourceBlock.getFace(block);
|
||||
if (direction == BlockFace.UP || direction == BlockFace.DOWN) {
|
||||
Object serverLevel = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(world);
|
||||
Object chunkSource = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(serverLevel);
|
||||
Object blockPos = LocationUtils.toBlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ());
|
||||
FastNMS.INSTANCE.method$ServerChunkCache$blockChanged(chunkSource, blockPos);
|
||||
if (direction == BlockFace.UP) {
|
||||
NoteBlockChainUpdateUtils.noteBlockChainUpdate(serverLevel, chunkSource, Reflections.instance$Direction$UP, blockPos, 0);
|
||||
} else {
|
||||
NoteBlockChainUpdateUtils.noteBlockChainUpdate(serverLevel, chunkSource, Reflections.instance$Direction$DOWN, blockPos, 0);
|
||||
}
|
||||
Block block = event.getBlock();
|
||||
World world = block.getWorld();
|
||||
Location location = block.getLocation();
|
||||
Block sourceBlock = event.getSourceBlock();
|
||||
BlockFace direction = sourceBlock.getFace(block);
|
||||
if (direction == BlockFace.UP || direction == BlockFace.DOWN) {
|
||||
Object serverLevel = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(world);
|
||||
Object chunkSource = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(serverLevel);
|
||||
Object blockPos = LocationUtils.toBlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ());
|
||||
FastNMS.INSTANCE.method$ServerChunkCache$blockChanged(chunkSource, blockPos);
|
||||
if (direction == BlockFace.UP) {
|
||||
NoteBlockChainUpdateUtils.noteBlockChainUpdate(serverLevel, chunkSource, Reflections.instance$Direction$UP, blockPos, 0);
|
||||
} else {
|
||||
NoteBlockChainUpdateUtils.noteBlockChainUpdate(serverLevel, chunkSource, Reflections.instance$Direction$DOWN, blockPos, 0);
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
plugin.logger().warn("Failed to sync note block states", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -798,7 +798,7 @@ public class BukkitBlockManager extends AbstractBlockManager {
|
||||
newBlockState = getOnlyBlockState(newRealBlock);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Optional<Object> optionalHolder = (Optional<Object>) Reflections.method$Registry$getHolder0.invoke(Reflections.instance$BuiltInRegistries$BLOCK, resourceLocation);
|
||||
Optional<Object> optionalHolder = (Optional<Object>) Reflections.method$Registry$getHolder1.invoke(Reflections.instance$BuiltInRegistries$BLOCK, Reflections.method$ResourceKey$create.invoke(null, Reflections.instance$Registries$BLOCK, resourceLocation));
|
||||
blockHolder = optionalHolder.get();
|
||||
} else {
|
||||
try {
|
||||
|
||||
@@ -133,6 +133,7 @@ public class BukkitCustomBlock extends CustomBlock {
|
||||
if (settings.burnable()) {
|
||||
Reflections.method$FireBlock$setFlammable.invoke(Reflections.instance$Blocks$FIRE, mcBlock, settings.burnChance(), settings.fireSpreadChance());
|
||||
}
|
||||
Reflections.field$BlockStateBase$requiresCorrectToolForDrops.set(mcBlockState, settings.requireCorrectTool());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
CraftEngine.instance().logger().warn("Failed to init block settings", e);
|
||||
|
||||
@@ -19,6 +19,7 @@ import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import net.momirealms.craftengine.core.util.context.ContextHolder;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
import net.momirealms.craftengine.core.world.Vec3d;
|
||||
import net.momirealms.craftengine.core.world.WorldEvents;
|
||||
import net.momirealms.craftengine.shared.block.BlockBehavior;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
@@ -76,7 +77,7 @@ public class BushBlockBehavior extends BukkitBlockBehavior {
|
||||
world.dropItemNaturally(vec3d, item);
|
||||
}
|
||||
world.playBlockSound(vec3d, previousState.sounds().breakSound());
|
||||
FastNMS.INSTANCE.method$Level$levelEvent(level, 2001, blockPos, stateId);
|
||||
FastNMS.INSTANCE.method$Level$levelEvent(level, WorldEvents.BLOCK_BREAK_EFFECT, blockPos, stateId);
|
||||
}
|
||||
return Reflections.method$Block$defaultBlockState.invoke(Reflections.instance$Blocks$AIR);
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ public class ConcretePowderBlockBehavior extends FallingBlockBehavior {
|
||||
if (direction != Reflections.instance$Direction$DOWN || canSolidify(blockState)) {
|
||||
Reflections.method$MutableBlockPos$setWithOffset.invoke(mutablePos, pos, direction);
|
||||
blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, mutablePos);
|
||||
if (canSolidify(blockState) && !(boolean) Reflections.method$BlockStateBase$isFaceSturdy.invoke(blockState, level, pos, Reflections.getOppositeDirection(direction), Reflections.instance$SupportType$FULL)) {
|
||||
if (canSolidify(blockState) && !(boolean) Reflections.method$BlockStateBase$isFaceSturdy.invoke(blockState, level, pos, FastNMS.INSTANCE.method$Direction$getOpposite(direction), Reflections.instance$SupportType$FULL)) {
|
||||
flag = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import net.momirealms.craftengine.core.util.context.ContextHolder;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
import net.momirealms.craftengine.core.world.Vec3d;
|
||||
import net.momirealms.craftengine.core.world.WorldEvents;
|
||||
import net.momirealms.craftengine.shared.block.BlockBehavior;
|
||||
|
||||
import java.util.List;
|
||||
@@ -69,7 +70,7 @@ public class SugarCaneBlockBehavior extends BushBlockBehavior {
|
||||
world.dropItemNaturally(vec3d, item);
|
||||
}
|
||||
world.playBlockSound(vec3d, currentState.sounds().breakSound());
|
||||
FastNMS.INSTANCE.method$Level$levelEvent(level, 2001, blockPos, stateId);
|
||||
FastNMS.INSTANCE.method$Level$levelEvent(level, WorldEvents.BLOCK_BREAK_EFFECT, blockPos, stateId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -349,6 +349,20 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
|
||||
public void handleBaseEntityLoadEarly(ItemDisplay display) {
|
||||
String id = display.getPersistentDataContainer().get(FURNITURE_KEY, PersistentDataType.STRING);
|
||||
if (id == null) return;
|
||||
// Remove the entity if it's not a valid furniture
|
||||
if (Config.handleInvalidFurniture()) {
|
||||
String mapped = Config.furnitureMappings().get(id);
|
||||
if (mapped != null) {
|
||||
if (mapped.isEmpty()) {
|
||||
display.remove();
|
||||
return;
|
||||
} else {
|
||||
id = mapped;
|
||||
display.getPersistentDataContainer().set(FURNITURE_KEY, PersistentDataType.STRING, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Key key = Key.of(id);
|
||||
Optional<CustomFurniture> optionalFurniture = furnitureById(key);
|
||||
if (optionalFurniture.isPresent()) {
|
||||
@@ -357,13 +371,6 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
|
||||
if (previous != null) return;
|
||||
LoadedFurniture furniture = addNewFurniture(display, customFurniture, getAnchorType(display, customFurniture));
|
||||
furniture.initializeColliders(); // safely do it here
|
||||
return;
|
||||
}
|
||||
// Remove the entity if it's not a valid furniture
|
||||
if (Config.removeInvalidFurniture()) {
|
||||
if (Config.furnitureToRemove().isEmpty() || Config.furnitureToRemove().contains(id)) {
|
||||
display.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,5 +10,6 @@ public class BukkitHitBoxTypes extends HitBoxTypes {
|
||||
register(INTERACTION, InteractionHitBox.FACTORY);
|
||||
register(SHULKER, ShulkerHitBox.FACTORY);
|
||||
register(HAPPY_GHAST, HappyGhastHitBox.FACTORY);
|
||||
register(CUSTOM, CustomHitBox.FACTORY);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
package net.momirealms.craftengine.bukkit.entity.furniture.hitbox;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.util.Reflections;
|
||||
import net.momirealms.craftengine.core.entity.furniture.*;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.Registry;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.joml.Quaternionf;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class CustomHitBox extends AbstractHitBox {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
private final float scale;
|
||||
private final EntityType entityType;
|
||||
private final List<Object> cachedValues = new ArrayList<>();
|
||||
|
||||
public CustomHitBox(Seat[] seats, Vector3f position, EntityType type, float scale) {
|
||||
super(seats, position);
|
||||
this.scale = scale;
|
||||
this.entityType = type;
|
||||
BaseEntityData.NoGravity.addEntityDataIfNotDefaultValue(true, this.cachedValues);
|
||||
BaseEntityData.Silent.addEntityDataIfNotDefaultValue(true, this.cachedValues);
|
||||
BaseEntityData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, this.cachedValues);
|
||||
}
|
||||
|
||||
public EntityType entityType() {
|
||||
return entityType;
|
||||
}
|
||||
|
||||
public float scale() {
|
||||
return scale;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key type() {
|
||||
return HitBoxTypes.CUSTOM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSpawnPackets(int[] entityId, double x, double y, double z, float yaw, Quaternionf conjugated, BiConsumer<Object, Boolean> packets) {
|
||||
Vector3f offset = conjugated.transform(new Vector3f(position()));
|
||||
try {
|
||||
packets.accept(Reflections.constructor$ClientboundAddEntityPacket.newInstance(
|
||||
entityId[0], UUID.randomUUID(), x + offset.x, y + offset.y, z - offset.z, 0, yaw,
|
||||
FastNMS.INSTANCE.toNMSEntityType(this.entityType), 0, Reflections.instance$Vec3$Zero, 0
|
||||
), true);
|
||||
packets.accept(Reflections.constructor$ClientboundSetEntityDataPacket.newInstance(entityId[0], List.copyOf(this.cachedValues)), true);
|
||||
if (VersionHelper.isVersionNewerThan1_20_5() && this.scale != 1) {
|
||||
Object attributeInstance = Reflections.constructor$AttributeInstance.newInstance(Reflections.instance$Holder$Attribute$scale, (Consumer<?>) (o) -> {});
|
||||
Reflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, this.scale);
|
||||
packets.accept(Reflections.constructor$ClientboundUpdateAttributesPacket0.newInstance(entityId[0], Collections.singletonList(attributeInstance)), false);
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException("Failed to construct custom hitbox spawn packet", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] acquireEntityIds(Supplier<Integer> entityIdSupplier) {
|
||||
return new int[] {entityIdSupplier.get()};
|
||||
}
|
||||
|
||||
public static class Factory implements HitBoxFactory {
|
||||
|
||||
@Override
|
||||
public HitBox create(Map<String, Object> arguments) {
|
||||
Vector3f position = MiscUtils.getVector3f(arguments.getOrDefault("position", "0"));
|
||||
float scale = MiscUtils.getAsFloat(arguments.getOrDefault("scale", "1"));
|
||||
EntityType entityType = Registry.ENTITY_TYPE.get(new NamespacedKey("minecraft", (String) arguments.getOrDefault("entity-type", "slime")));
|
||||
if (entityType == null) {
|
||||
throw new IllegalArgumentException("EntityType not found: " + arguments.get("entity-type"));
|
||||
}
|
||||
return new CustomHitBox(HitBoxFactory.getSeats(arguments), position, entityType, scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -55,7 +55,7 @@ public class InteractionHitBox extends AbstractHitBox {
|
||||
), true);
|
||||
packets.accept(Reflections.constructor$ClientboundSetEntityDataPacket.newInstance(entityId[0], List.copyOf(this.cachedValues)), true);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException("Failed to construct hitbox spawn packet", e);
|
||||
throw new RuntimeException("Failed to construct interaction hitbox spawn packet", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -140,9 +140,9 @@ public class ShulkerHitBox extends AbstractHitBox {
|
||||
entityIds[1], (short) 0, ya, (short) 0, true
|
||||
), false);
|
||||
}
|
||||
if (VersionHelper.isVersionNewerThan1_20_5()) {
|
||||
if (VersionHelper.isVersionNewerThan1_20_5() && this.scale != 1) {
|
||||
Object attributeInstance = Reflections.constructor$AttributeInstance.newInstance(Reflections.instance$Holder$Attribute$scale, (Consumer<?>) (o) -> {});
|
||||
Reflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, scale);
|
||||
Reflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, this.scale);
|
||||
packets.accept(Reflections.constructor$ClientboundUpdateAttributesPacket0.newInstance(entityIds[1], Collections.singletonList(attributeInstance)), false);
|
||||
}
|
||||
if (this.interactionEntity) {
|
||||
|
||||
@@ -145,8 +145,8 @@ public class BukkitCustomItem implements CustomItem<ItemStack> {
|
||||
private Key id;
|
||||
private Material material;
|
||||
private Key materialKey;
|
||||
private ItemSettings settings;
|
||||
private final List<ItemBehavior> behaviors = new ArrayList<>();
|
||||
private ItemSettings settings = ItemSettings.of();
|
||||
private final List<ItemDataModifier<ItemStack>> modifiers = new ArrayList<>();
|
||||
private final List<ItemDataModifier<ItemStack>> clientBoundModifiers = new ArrayList<>();
|
||||
|
||||
@@ -208,7 +208,7 @@ public class BukkitCustomItem implements CustomItem<ItemStack> {
|
||||
@Override
|
||||
public CustomItem<ItemStack> build() {
|
||||
this.modifiers.addAll(this.settings.modifiers());
|
||||
return new BukkitCustomItem(id, materialKey, material, modifiers, clientBoundModifiers, behaviors, settings);
|
||||
return new BukkitCustomItem(this.id, this.materialKey, this.material, this.modifiers, this.clientBoundModifiers, this.behaviors, this.settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ import net.momirealms.craftengine.core.item.behavior.ItemBehaviors;
|
||||
import net.momirealms.craftengine.core.item.modifier.CustomModelDataModifier;
|
||||
import net.momirealms.craftengine.core.item.modifier.IdModifier;
|
||||
import net.momirealms.craftengine.core.item.modifier.ItemModelModifier;
|
||||
import net.momirealms.craftengine.core.pack.LegacyOverridesModel;
|
||||
import net.momirealms.craftengine.core.pack.LoadingSequence;
|
||||
import net.momirealms.craftengine.core.pack.Pack;
|
||||
import net.momirealms.craftengine.core.pack.ResourceLocation;
|
||||
@@ -79,9 +78,9 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
|
||||
if (plugin.hasMod()) {
|
||||
Class<?> clazz$CustomStreamCodec = ReflectionUtils.getClazz("net.momirealms.craftengine.mod.item.CustomStreamCodec");
|
||||
if (clazz$CustomStreamCodec != null) {
|
||||
Field cProcessor = ReflectionUtils.getDeclaredField(clazz$CustomStreamCodec, Function.class, 0);
|
||||
Field sProcessor = ReflectionUtils.getDeclaredField(clazz$CustomStreamCodec, Function.class, 1);
|
||||
Function<Object, Object> c = (raw) -> {
|
||||
Field s2cProcessor = ReflectionUtils.getDeclaredField(clazz$CustomStreamCodec, Function.class, 0);
|
||||
Field c2sProcessor = ReflectionUtils.getDeclaredField(clazz$CustomStreamCodec, Function.class, 1);
|
||||
Function<Object, Object> s2c = (raw) -> {
|
||||
ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(raw);
|
||||
Item<ItemStack> wrapped = this.wrap(itemStack.clone());
|
||||
Optional<CustomItem<ItemStack>> customItem = wrapped.getCustomItem();
|
||||
@@ -99,7 +98,7 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
|
||||
return wrapped.getLiteralObject();
|
||||
};
|
||||
|
||||
Function<Object, Object> s = (raw) -> {
|
||||
Function<Object, Object> c2s = (raw) -> {
|
||||
ItemStack itemStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(raw);
|
||||
Item<ItemStack> wrapped = this.wrap(itemStack);
|
||||
Optional<CustomItem<ItemStack>> customItem = wrapped.getCustomItem();
|
||||
@@ -117,10 +116,10 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
|
||||
return wrapped.getLiteralObject();
|
||||
};
|
||||
try {
|
||||
assert cProcessor != null;
|
||||
cProcessor.set(null, c);
|
||||
assert sProcessor != null;
|
||||
sProcessor.set(null, s);
|
||||
assert s2cProcessor != null;
|
||||
s2cProcessor.set(null, s2c);
|
||||
assert c2sProcessor != null;
|
||||
c2sProcessor.set(null, c2s);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
plugin.logger().warn("Failed to load custom stream codec", e);
|
||||
}
|
||||
@@ -267,7 +266,10 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
|
||||
.orElseGet(() -> ((WritableRegistry<Key>) BuiltInRegistries.OPTIMIZED_ITEM_ID)
|
||||
.register(new ResourceKey<>(BuiltInRegistries.OPTIMIZED_ITEM_ID.key().location(), id), id));
|
||||
|
||||
boolean isVanillaItem = id.namespace().equals("minecraft") && Registry.MATERIAL.get(new NamespacedKey(id.namespace(), id.value())) != null;
|
||||
String materialStringId = (String) section.get("material");
|
||||
if (isVanillaItem)
|
||||
materialStringId = id.value();
|
||||
if (materialStringId == null) {
|
||||
TranslationManager.instance().log("warning.config.item.lack_material", path.toString(), id.toString());
|
||||
return;
|
||||
@@ -284,8 +286,6 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
|
||||
Key itemModelKey = null;
|
||||
|
||||
CustomItem.Builder<ItemStack> itemBuilder = BukkitCustomItem.builder().id(id).material(materialId);
|
||||
itemBuilder.dataModifier(new IdModifier<>(id));
|
||||
|
||||
boolean hasItemModelSection = section.containsKey("item-model");
|
||||
|
||||
// To get at least one model provider
|
||||
@@ -341,6 +341,10 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
|
||||
}
|
||||
}
|
||||
|
||||
// Add it here to make sure that ce id is always applied
|
||||
if (!isVanillaItem)
|
||||
itemBuilder.dataModifier(new IdModifier<>(id));
|
||||
|
||||
// Get item data
|
||||
Map<String, Object> clientSideDataSection = MiscUtils.castToMap(section.get("client-bound-data"), true);
|
||||
if (clientSideDataSection != null) {
|
||||
@@ -355,10 +359,17 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
|
||||
}
|
||||
}
|
||||
|
||||
ItemSettings itemSettings;
|
||||
if (section.containsKey("settings")) {
|
||||
Map<String, Object> settings = MiscUtils.castToMap(section.get("settings"), false);
|
||||
itemBuilder.settings(ItemSettings.fromMap(settings));
|
||||
itemSettings = ItemSettings.fromMap(settings);
|
||||
} else {
|
||||
itemSettings = ItemSettings.of();
|
||||
}
|
||||
if (isVanillaItem) {
|
||||
itemSettings.canPlaceRelatedVanillaBlock(true);
|
||||
}
|
||||
itemBuilder.settings(itemSettings);
|
||||
|
||||
CustomItem<ItemStack> customItem = itemBuilder.build();
|
||||
customItems.put(id, customItem);
|
||||
|
||||
@@ -219,7 +219,7 @@ public class ItemEventListener implements Listener {
|
||||
int currentTicks = player.gameTicks();
|
||||
// The client will send multiple packets to the server if the client thinks it should
|
||||
// However, if the main hand item interaction is successful, the off-hand item should be blocked.
|
||||
if (!player.updateLastSuccessfulInteractionTick(currentTicks)) {
|
||||
if (player.lastSuccessfulInteractionTick() == currentTicks) {
|
||||
event.setCancelled(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -5,10 +5,7 @@ import net.momirealms.craftengine.bukkit.api.event.CustomBlockAttemptPlaceEvent;
|
||||
import net.momirealms.craftengine.bukkit.api.event.CustomBlockPlaceEvent;
|
||||
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.util.DirectionUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.EventUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.LocationUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.Reflections;
|
||||
import net.momirealms.craftengine.bukkit.util.*;
|
||||
import net.momirealms.craftengine.core.block.CustomBlock;
|
||||
import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||
import net.momirealms.craftengine.core.block.UpdateOption;
|
||||
@@ -22,6 +19,7 @@ import net.momirealms.craftengine.core.item.context.BlockPlaceContext;
|
||||
import net.momirealms.craftengine.core.item.context.UseOnContext;
|
||||
import net.momirealms.craftengine.core.pack.Pack;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
@@ -76,21 +74,35 @@ public class BlockItemBehavior extends ItemBehavior {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
Player player = placeContext.getPlayer();
|
||||
int gameTicks = player.gameTicks();
|
||||
if (!player.updateLastSuccessfulInteractionTick(gameTicks)) {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
|
||||
BlockPos pos = placeContext.getClickedPos();
|
||||
BlockPos againstPos = placeContext.getAgainstPos();
|
||||
World world = (World) placeContext.getLevel().platformWorld();
|
||||
Location placeLocation = new Location(world, pos.x(), pos.y(), pos.z());
|
||||
|
||||
int gameTicks = player.gameTicks();
|
||||
if (!player.updateLastSuccessfulInteractionTick(gameTicks)) {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
|
||||
Block bukkitBlock = world.getBlockAt(placeLocation);
|
||||
Block againstBlock = world.getBlockAt(againstPos.x(), againstPos.y(), againstPos.z());
|
||||
org.bukkit.entity.Player bukkitPlayer = (org.bukkit.entity.Player) player.platformPlayer();
|
||||
|
||||
// todo adventure check
|
||||
if (player.isAdventureMode()) {
|
||||
Object againstBlockState = BlockStateUtils.blockDataToBlockState(againstBlock.getBlockData());
|
||||
int stateId = BlockStateUtils.blockStateToId(againstBlockState);
|
||||
if (BlockStateUtils.isVanillaBlock(stateId)) {
|
||||
if (!AdventureModeUtils.canPlace(context.getItem(), context.getLevel(), againstPos, againstBlockState)) {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
} else {
|
||||
ImmutableBlockState customState = BukkitBlockManager.instance().getImmutableBlockStateUnsafe(stateId);
|
||||
// custom block
|
||||
if (!AdventureModeUtils.canPlace(context.getItem(), context.getLevel(), againstPos, Config.simplifyAdventurePlaceCheck() ? customState.vanillaBlockState().handle() : againstBlockState)) {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// trigger event
|
||||
CustomBlockAttemptPlaceEvent attemptPlaceEvent = new CustomBlockAttemptPlaceEvent(bukkitPlayer, placeLocation.clone(), blockStateToPlace,
|
||||
|
||||
@@ -67,8 +67,10 @@ public class FurnitureItemBehavior extends ItemBehavior {
|
||||
}
|
||||
|
||||
Player player = context.getPlayer();
|
||||
|
||||
// todo adventure check
|
||||
if (player.isAdventureMode()) {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
|
||||
int gameTicks = player.gameTicks();
|
||||
if (!player.updateLastSuccessfulInteractionTick(gameTicks)) {
|
||||
|
||||
@@ -9,6 +9,7 @@ import net.momirealms.craftengine.core.item.ItemWrapper;
|
||||
import net.momirealms.craftengine.core.item.modifier.IdModifier;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.Objects;
|
||||
@@ -43,7 +44,10 @@ public abstract class BukkitItemFactory extends ItemFactory<CraftEngine, RTagIte
|
||||
@Override
|
||||
protected Key id(ItemWrapper<ItemStack> item) {
|
||||
Object id = item.get(IdModifier.CRAFT_ENGINE_ID);
|
||||
if (id == null) return Key.of(item.getItem().getType().getKey().asString());
|
||||
if (id == null) {
|
||||
NamespacedKey key = item.getItem().getType().getKey();
|
||||
return Key.of(key.getNamespace(), key.getKey());
|
||||
}
|
||||
return Key.of(id.toString());
|
||||
}
|
||||
|
||||
|
||||
@@ -14,8 +14,8 @@ import net.momirealms.craftengine.bukkit.util.RecipeUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.Reflections;
|
||||
import net.momirealms.craftengine.core.item.CustomItem;
|
||||
import net.momirealms.craftengine.core.item.ItemBuildContext;
|
||||
import net.momirealms.craftengine.core.item.recipe.Recipe;
|
||||
import net.momirealms.craftengine.core.item.recipe.*;
|
||||
import net.momirealms.craftengine.core.item.recipe.Recipe;
|
||||
import net.momirealms.craftengine.core.item.recipe.vanilla.*;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
|
||||
@@ -12,8 +12,8 @@ import net.momirealms.craftengine.bukkit.util.ItemUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.LegacyInventoryUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.Reflections;
|
||||
import net.momirealms.craftengine.core.item.*;
|
||||
import net.momirealms.craftengine.core.item.recipe.Recipe;
|
||||
import net.momirealms.craftengine.core.item.recipe.*;
|
||||
import net.momirealms.craftengine.core.item.recipe.Recipe;
|
||||
import net.momirealms.craftengine.core.item.recipe.input.CraftingInput;
|
||||
import net.momirealms.craftengine.core.item.recipe.input.SingleItemInput;
|
||||
import net.momirealms.craftengine.core.item.recipe.input.SmithingInput;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package net.momirealms.craftengine.bukkit.pack;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.momirealms.craftengine.bukkit.api.event.AsyncResourcePackGenerateEvent;
|
||||
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||
import net.momirealms.craftengine.bukkit.plugin.command.feature.ReloadCommand;
|
||||
@@ -8,236 +7,161 @@ import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
|
||||
import net.momirealms.craftengine.bukkit.util.ComponentUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.EventUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.Reflections;
|
||||
import net.momirealms.craftengine.bukkit.util.ResourcePackUtils;
|
||||
import net.momirealms.craftengine.core.entity.player.Player;
|
||||
import net.momirealms.craftengine.core.pack.AbstractPackManager;
|
||||
import net.momirealms.craftengine.core.pack.host.HostMode;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHost;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackDownloadData;
|
||||
import net.momirealms.craftengine.core.pack.host.impl.NoneHost;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.plugin.network.ConnectionState;
|
||||
import net.momirealms.craftengine.core.plugin.network.NetWorkUser;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerResourcePackStatusEvent;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class BukkitPackManager extends AbstractPackManager implements Listener {
|
||||
private final BukkitCraftEngine plugin;
|
||||
private HostMode previousHostMode = HostMode.NONE;
|
||||
private UUID previousHostUUID;
|
||||
public static final String FAKE_URL = "https://127.0.0.1:65536";
|
||||
private final BukkitCraftEngine plugin;
|
||||
|
||||
public BukkitPackManager(BukkitCraftEngine plugin) {
|
||||
public BukkitPackManager(BukkitCraftEngine plugin) {
|
||||
super(plugin, (rf, zp) -> {
|
||||
AsyncResourcePackGenerateEvent endEvent = new AsyncResourcePackGenerateEvent(rf, zp);
|
||||
EventUtils.fireAndForget(endEvent);
|
||||
});
|
||||
AsyncResourcePackGenerateEvent endEvent = new AsyncResourcePackGenerateEvent(rf, zp);
|
||||
EventUtils.fireAndForget(endEvent);
|
||||
});
|
||||
this.plugin = plugin;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delayedInit() {
|
||||
super.delayedInit();
|
||||
Bukkit.getPluginManager().registerEvents(this, plugin.bootstrap());
|
||||
}
|
||||
@Override
|
||||
public void delayedInit() {
|
||||
super.delayedInit();
|
||||
Bukkit.getPluginManager().registerEvents(this, plugin.bootstrap());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||
// for 1.20.1 servers, not recommended to use
|
||||
if (Config.sendPackOnJoin() && !VersionHelper.isVersionNewerThan1_20_2()) {
|
||||
this.sendResourcePack(plugin.networkManager().getUser(event.getPlayer()), null);
|
||||
}
|
||||
}
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||
if (Config.sendPackOnJoin() && !VersionHelper.isVersionNewerThan1_20_2()) {
|
||||
Player player = plugin.adapt(event.getPlayer());
|
||||
this.sendResourcePack(player);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
public void onResourcePackStatus(PlayerResourcePackStatusEvent event) {
|
||||
// for 1.20.1 servers, not recommended to use
|
||||
if (Config.sendPackOnJoin() && Config.kickOnDeclined() && !VersionHelper.isVersionNewerThan1_20_2()) {
|
||||
if (event.getStatus() == PlayerResourcePackStatusEvent.Status.DECLINED || event.getStatus() == PlayerResourcePackStatusEvent.Status.FAILED_DOWNLOAD) {
|
||||
event.getPlayer().kick();
|
||||
}
|
||||
}
|
||||
}
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
public void onResourcePackStatus(PlayerResourcePackStatusEvent event) {
|
||||
// for 1.20.1 servers, not recommended to use
|
||||
if (Config.sendPackOnJoin() && Config.kickOnDeclined() && !VersionHelper.isVersionNewerThan1_20_2()) {
|
||||
if (event.getStatus() == PlayerResourcePackStatusEvent.Status.DECLINED || event.getStatus() == PlayerResourcePackStatusEvent.Status.FAILED_DOWNLOAD) {
|
||||
event.getPlayer().kick();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
super.load();
|
||||
@Override
|
||||
public void load() {
|
||||
if (ReloadCommand.RELOAD_PACK_FLAG || CraftEngine.instance().isInitializing()) {
|
||||
super.load();
|
||||
if (Config.sendPackOnJoin() && VersionHelper.isVersionNewerThan1_20_2() && !(resourcePackHost() instanceof NoneHost)) {
|
||||
this.modifyServerSettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update server properties
|
||||
if (VersionHelper.isVersionNewerThan1_20_2()) {
|
||||
if (Config.hostMode() == HostMode.SELF_HOST) {
|
||||
if (Files.exists(resourcePackPath())) {
|
||||
updateResourcePackSettings(super.packUUID, ResourcePackHost.instance().url(), super.packHash, Config.kickOnDeclined(), Config.resourcePackPrompt());
|
||||
}
|
||||
} else if (Config.hostMode() == HostMode.EXTERNAL_HOST) {
|
||||
updateResourcePackSettings(Config.externalPackUUID(), Config.externalPackUrl(), Config.externalPackSha1(), Config.kickOnDeclined(), Config.resourcePackPrompt());
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.sendPackOnReload()) {
|
||||
if (this.previousHostMode == HostMode.SELF_HOST) {
|
||||
this.previousHostUUID = super.packUUID;
|
||||
}
|
||||
// unload packs if user changed to none host
|
||||
if (Config.hostMode() == HostMode.NONE && this.previousHostMode != HostMode.NONE) {
|
||||
unloadResourcePackForOnlinePlayers(this.previousHostUUID);
|
||||
}
|
||||
// load new external resource pack on reload
|
||||
if (Config.hostMode() == HostMode.EXTERNAL_HOST) {
|
||||
if (this.previousHostMode == HostMode.NONE) {
|
||||
updateResourcePackForOnlinePlayers(null);
|
||||
} else {
|
||||
updateResourcePackForOnlinePlayers(this.previousHostUUID);
|
||||
}
|
||||
// record previous host uuid here
|
||||
this.previousHostUUID = Config.externalPackUUID();
|
||||
}
|
||||
if (Config.hostMode() == HostMode.SELF_HOST && this.previousHostMode != HostMode.SELF_HOST) {
|
||||
if (ReloadCommand.RELOAD_PACK_FLAG) {
|
||||
ReloadCommand.RELOAD_PACK_FLAG = false;
|
||||
if (this.previousHostMode == HostMode.NONE) {
|
||||
updateResourcePackForOnlinePlayers(null);
|
||||
} else if (this.previousHostMode == HostMode.EXTERNAL_HOST) {
|
||||
updateResourcePackForOnlinePlayers(this.previousHostUUID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.previousHostMode = Config.hostMode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload() {
|
||||
super.unload();
|
||||
if (VersionHelper.isVersionNewerThan1_20_2() && this.previousHostMode != HostMode.NONE) {
|
||||
resetResourcePackSettings();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
super.disable();
|
||||
HandlerList.unregisterAll(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateResourcePack() {
|
||||
// generate pack
|
||||
super.generateResourcePack();
|
||||
// update server properties
|
||||
if (VersionHelper.isVersionNewerThan1_20_2()) {
|
||||
if (Config.hostMode() == HostMode.SELF_HOST) {
|
||||
updateResourcePackSettings(super.packUUID, ResourcePackHost.instance().url(), super.packHash, Config.kickOnDeclined(), Config.resourcePackPrompt());
|
||||
}
|
||||
}
|
||||
// resend packs
|
||||
if (Config.hostMode() == HostMode.SELF_HOST && Config.sendPackOnReload()) {
|
||||
updateResourcePackForOnlinePlayers(this.previousHostUUID);
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateResourcePackForOnlinePlayers(UUID previousUUID) {
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
BukkitServerPlayer serverPlayer = plugin.adapt(player);
|
||||
sendResourcePack(serverPlayer, previousUUID);
|
||||
}
|
||||
}
|
||||
|
||||
private void resetResourcePackSettings() {
|
||||
try {
|
||||
Object settings = Reflections.field$DedicatedServer$settings.get(Reflections.method$MinecraftServer$getServer.invoke(null));
|
||||
Object properties = Reflections.field$DedicatedServerSettings$properties.get(settings);
|
||||
Reflections.field$DedicatedServerProperties$serverResourcePackInfo.set(properties, Optional.empty());
|
||||
} catch (Exception e) {
|
||||
this.plugin.logger().warn("Failed to update resource pack settings", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateResourcePackSettings(UUID uuid, String url, String sha1, boolean required, Component prompt) {
|
||||
if (!Config.sendPackOnJoin()) {
|
||||
resetResourcePackSettings();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Object settings = Reflections.field$DedicatedServer$settings.get(Reflections.method$MinecraftServer$getServer.invoke(null));
|
||||
Object properties = Reflections.field$DedicatedServerSettings$properties.get(settings);
|
||||
public void modifyServerSettings() {
|
||||
try {
|
||||
Object settings = Reflections.field$DedicatedServer$settings.get(Reflections.method$MinecraftServer$getServer.invoke(null));
|
||||
Object properties = Reflections.field$DedicatedServerSettings$properties.get(settings);
|
||||
Object info;
|
||||
if (VersionHelper.isVersionNewerThan1_20_3()) {
|
||||
info = Reflections.constructor$ServerResourcePackInfo.newInstance(uuid, url, sha1, required, ComponentUtils.adventureToMinecraft(prompt));
|
||||
info = Reflections.constructor$ServerResourcePackInfo.newInstance(new UUID(0, 0), FAKE_URL, "", Config.kickOnDeclined(), ComponentUtils.adventureToMinecraft(Config.resourcePackPrompt()));
|
||||
} else {
|
||||
info = Reflections.constructor$ServerResourcePackInfo.newInstance(url + uuid, sha1, required, ComponentUtils.adventureToMinecraft(prompt));
|
||||
info = Reflections.constructor$ServerResourcePackInfo.newInstance(FAKE_URL, "", Config.kickOnDeclined(), ComponentUtils.adventureToMinecraft(Config.resourcePackPrompt()));
|
||||
}
|
||||
Reflections.field$DedicatedServerProperties$serverResourcePackInfo.set(properties, Optional.of(info));
|
||||
} catch (Exception e) {
|
||||
this.plugin.logger().warn("Failed to update resource pack settings", e);
|
||||
}
|
||||
}
|
||||
this.plugin.logger().warn("Failed to update resource pack settings", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendResourcePack(NetWorkUser user, @Nullable UUID previousPack) {
|
||||
if (Config.hostMode() == HostMode.NONE) return;
|
||||
String url;
|
||||
String sha1;
|
||||
UUID uuid;
|
||||
if (Config.hostMode() == HostMode.SELF_HOST) {
|
||||
url = ResourcePackHost.instance().url();
|
||||
sha1 = super.packHash;
|
||||
uuid = super.packUUID;
|
||||
if (!Files.exists(resourcePackPath())) return;
|
||||
} else {
|
||||
url = Config.externalPackUrl();
|
||||
sha1 = Config.externalPackSha1();
|
||||
uuid = Config.externalPackUUID();
|
||||
if (uuid.equals(previousPack)) return;
|
||||
}
|
||||
@Override
|
||||
public void unload() {
|
||||
super.unload();
|
||||
if (ReloadCommand.RELOAD_PACK_FLAG) {
|
||||
if (VersionHelper.isVersionNewerThan1_20_2()) {
|
||||
this.resetServerSettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Object packPrompt = ComponentUtils.adventureToMinecraft(Config.resourcePackPrompt());
|
||||
try {
|
||||
Object packPacket;
|
||||
if (VersionHelper.isVersionNewerThan1_20_5()) {
|
||||
packPacket = Reflections.constructor$ClientboundResourcePackPushPacket.newInstance(
|
||||
uuid, url, sha1, Config.kickOnDeclined(), Optional.of(packPrompt)
|
||||
);
|
||||
} else if (VersionHelper.isVersionNewerThan1_20_3()) {
|
||||
packPacket = Reflections.constructor$ClientboundResourcePackPushPacket.newInstance(
|
||||
uuid, url, sha1, Config.kickOnDeclined(), packPrompt
|
||||
);
|
||||
} else {
|
||||
packPacket = Reflections.constructor$ClientboundResourcePackPushPacket.newInstance(
|
||||
url + uuid, sha1, Config.kickOnDeclined(), packPrompt
|
||||
);
|
||||
}
|
||||
if (user.decoderState() == ConnectionState.PLAY) {
|
||||
if (previousPack != null && VersionHelper.isVersionNewerThan1_20_3()) {
|
||||
plugin.networkManager().sendPackets(user, List.of(Reflections.constructor$ClientboundResourcePackPopPacket.newInstance(Optional.of(previousPack)), packPacket));
|
||||
} else {
|
||||
user.sendPacket(packPacket, false);
|
||||
}
|
||||
} else {
|
||||
user.nettyChannel().writeAndFlush(packPacket);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
this.plugin.logger().warn("Failed to send resource pack", e);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void disable() {
|
||||
super.disable();
|
||||
HandlerList.unregisterAll(this);
|
||||
this.resetServerSettings();
|
||||
}
|
||||
|
||||
public void unloadResourcePackForOnlinePlayers(UUID uuid) {
|
||||
try {
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
BukkitServerPlayer serverPlayer = plugin.adapt(player);
|
||||
if (serverPlayer.decoderState() == ConnectionState.PLAY) {
|
||||
serverPlayer.sendPacket(Reflections.constructor$ClientboundResourcePackPopPacket.newInstance(Optional.of(uuid)), true);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
this.plugin.logger().warn("Failed to unload online player resource pack", e);
|
||||
}
|
||||
}
|
||||
public void resetServerSettings() {
|
||||
try {
|
||||
Object settings = Reflections.field$DedicatedServer$settings.get(Reflections.method$MinecraftServer$getServer.invoke(null));
|
||||
Object properties = Reflections.field$DedicatedServerSettings$properties.get(settings);
|
||||
Reflections.field$DedicatedServerProperties$serverResourcePackInfo.set(properties, Optional.empty());
|
||||
} catch (Exception e) {
|
||||
this.plugin.logger().warn("Failed to reset resource pack settings", e);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onAsyncResourcePackGenerate(AsyncResourcePackGenerateEvent event) {
|
||||
if (!Config.autoUpload()) return;
|
||||
uploadResourcePack();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uploadResourcePack() {
|
||||
resourcePackHost().upload(Config.fileToUpload()).whenComplete((d, e) -> {
|
||||
if (e != null) {
|
||||
CraftEngine.instance().logger().warn("Failed to upload resource pack", e);
|
||||
return;
|
||||
}
|
||||
if (!Config.sendPackOnUpload()) return;
|
||||
for (BukkitServerPlayer player : this.plugin.networkManager().onlineUsers()) {
|
||||
sendResourcePack(player);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void sendResourcePack(Player player) {
|
||||
CompletableFuture<List<ResourcePackDownloadData>> future = resourcePackHost().requestResourcePackDownloadLink(player.uuid());
|
||||
future.thenAccept(dataList -> {
|
||||
if (player.isOnline()) {
|
||||
player.unloadCurrentResourcePack();
|
||||
if (dataList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (dataList.size() == 1) {
|
||||
ResourcePackDownloadData data = dataList.get(0);
|
||||
player.sendPacket(ResourcePackUtils.createPacket(data.uuid(), data.url(), data.sha1()), true);
|
||||
player.addResourcePackUUID(data.uuid());
|
||||
} else {
|
||||
List<Object> packets = new ArrayList<>();
|
||||
for (ResourcePackDownloadData data : dataList) {
|
||||
packets.add(ResourcePackUtils.createPacket(data.uuid(), data.url(), data.sha1()));
|
||||
player.addResourcePackUUID(data.uuid());
|
||||
}
|
||||
player.sendPackets(packets, true);
|
||||
}
|
||||
}
|
||||
}).exceptionally(throwable -> {
|
||||
CraftEngine.instance().logger().warn("Failed to send resource pack to player " + player.name(), throwable);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Pose;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
|
||||
@@ -46,7 +46,11 @@ public class BukkitCommandManager extends AbstractCommandManager<CommandSender>
|
||||
new DebugSetBlockCommand(this, plugin),
|
||||
new DebugSpawnFurnitureCommand(this, plugin),
|
||||
new DebugTargetBlockCommand(this, plugin),
|
||||
new TotemAnimationCommand(this, plugin)
|
||||
new TotemAnimationCommand(this, plugin),
|
||||
new EnableResourceCommand(this, plugin),
|
||||
new DisableResourceCommand(this, plugin),
|
||||
new ListResourceCommand(this, plugin),
|
||||
new UploadPackCommand(this, plugin)
|
||||
));
|
||||
final LegacyPaperCommandManager<CommandSender> manager = (LegacyPaperCommandManager<CommandSender>) getCommandManager();
|
||||
manager.settings().set(ManagerSetting.ALLOW_UNSAFE_REGISTRATION, true);
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package net.momirealms.craftengine.bukkit.plugin.command.feature;
|
||||
|
||||
import dev.dejvokep.boostedyaml.YamlDocument;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
|
||||
import net.momirealms.craftengine.core.pack.Pack;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
|
||||
import net.momirealms.craftengine.core.plugin.locale.MessageConstants;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.incendo.cloud.Command;
|
||||
import org.incendo.cloud.context.CommandContext;
|
||||
import org.incendo.cloud.context.CommandInput;
|
||||
import org.incendo.cloud.parser.standard.StringParser;
|
||||
import org.incendo.cloud.suggestion.Suggestion;
|
||||
import org.incendo.cloud.suggestion.SuggestionProvider;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class DisableResourceCommand extends BukkitCommandFeature<CommandSender> {
|
||||
|
||||
public DisableResourceCommand(CraftEngineCommandManager<CommandSender> commandManager, CraftEngine plugin) {
|
||||
super(commandManager, plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Command.Builder<? extends CommandSender> assembleCommand(org.incendo.cloud.CommandManager<CommandSender> manager, Command.Builder<CommandSender> builder) {
|
||||
return builder
|
||||
.flag(manager.flagBuilder("silent").withAliases("s"))
|
||||
.required("pack", StringParser.stringComponent(StringParser.StringMode.GREEDY).suggestionProvider(new SuggestionProvider<>() {
|
||||
@Override
|
||||
public @NonNull CompletableFuture<? extends @NonNull Iterable<? extends @NonNull Suggestion>> suggestionsFuture(@NonNull CommandContext<Object> context, @NonNull CommandInput input) {
|
||||
return CompletableFuture.completedFuture(plugin().packManager().loadedPacks().stream().filter(Pack::enabled).map(pack -> Suggestion.suggestion(pack.name())).toList());
|
||||
}
|
||||
}))
|
||||
.handler(context -> {
|
||||
String packFolder = context.get("pack");
|
||||
Path path = plugin().dataFolderPath().resolve("resources").resolve(packFolder);
|
||||
if (!Files.exists(path)) {
|
||||
handleFeedback(context, MessageConstants.COMMAND_RESOURCE_DISABLE_FAILURE, Component.text(packFolder));
|
||||
return;
|
||||
}
|
||||
Path packMetaPath = path.resolve("pack.yml");
|
||||
if (!Files.exists(packMetaPath)) {
|
||||
try {
|
||||
Files.createFile(packMetaPath);
|
||||
} catch (IOException e) {
|
||||
plugin().logger().warn("Could not create pack.yml file: " + packMetaPath);
|
||||
return;
|
||||
}
|
||||
}
|
||||
YamlDocument document = plugin().config().loadYamlData(packMetaPath.toFile());
|
||||
document.set("enable", false);
|
||||
try {
|
||||
document.save(packMetaPath.toFile());
|
||||
} catch (IOException e) {
|
||||
plugin().logger().warn("Could not save pack.yml file: " + packMetaPath);
|
||||
return;
|
||||
}
|
||||
handleFeedback(context, MessageConstants.COMMAND_RESOURCE_DISABLE_SUCCESS, Component.text(packFolder));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFeatureID() {
|
||||
return "disable_resource";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package net.momirealms.craftengine.bukkit.plugin.command.feature;
|
||||
|
||||
import dev.dejvokep.boostedyaml.YamlDocument;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
|
||||
import net.momirealms.craftengine.core.plugin.locale.MessageConstants;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.incendo.cloud.Command;
|
||||
import org.incendo.cloud.context.CommandContext;
|
||||
import org.incendo.cloud.context.CommandInput;
|
||||
import org.incendo.cloud.parser.standard.StringParser;
|
||||
import org.incendo.cloud.suggestion.Suggestion;
|
||||
import org.incendo.cloud.suggestion.SuggestionProvider;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class EnableResourceCommand extends BukkitCommandFeature<CommandSender> {
|
||||
|
||||
public EnableResourceCommand(CraftEngineCommandManager<CommandSender> commandManager, CraftEngine plugin) {
|
||||
super(commandManager, plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Command.Builder<? extends CommandSender> assembleCommand(org.incendo.cloud.CommandManager<CommandSender> manager, Command.Builder<CommandSender> builder) {
|
||||
return builder
|
||||
.flag(manager.flagBuilder("silent").withAliases("s"))
|
||||
.required("pack", StringParser.stringComponent(StringParser.StringMode.GREEDY).suggestionProvider(new SuggestionProvider<>() {
|
||||
@Override
|
||||
public @NonNull CompletableFuture<? extends @NonNull Iterable<? extends @NonNull Suggestion>> suggestionsFuture(@NonNull CommandContext<Object> context, @NonNull CommandInput input) {
|
||||
return CompletableFuture.completedFuture(plugin().packManager().loadedPacks().stream().filter(pack -> !pack.enabled()).map(pack -> Suggestion.suggestion(pack.name())).toList());
|
||||
}
|
||||
}))
|
||||
.handler(context -> {
|
||||
String packFolder = context.get("pack");
|
||||
Path path = plugin().dataFolderPath().resolve("resources").resolve(packFolder);
|
||||
if (!Files.exists(path)) {
|
||||
handleFeedback(context, MessageConstants.COMMAND_RESOURCE_ENABLE_FAILURE, Component.text(packFolder));
|
||||
return;
|
||||
}
|
||||
Path packMetaPath = path.resolve("pack.yml");
|
||||
if (!Files.exists(packMetaPath)) {
|
||||
try {
|
||||
Files.createFile(packMetaPath);
|
||||
} catch (IOException e) {
|
||||
plugin().logger().warn("Could not create pack.yml file: " + packMetaPath);
|
||||
return;
|
||||
}
|
||||
}
|
||||
YamlDocument document = plugin().config().loadYamlData(packMetaPath.toFile());
|
||||
document.set("enable", true);
|
||||
try {
|
||||
document.save(packMetaPath.toFile());
|
||||
} catch (IOException e) {
|
||||
plugin().logger().warn("Could not save pack.yml file: " + packMetaPath);
|
||||
return;
|
||||
}
|
||||
handleFeedback(context, MessageConstants.COMMAND_RESOURCE_ENABLE_SUCCESS, Component.text(packFolder));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFeatureID() {
|
||||
return "enable_resource";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package net.momirealms.craftengine.bukkit.plugin.command.feature;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.HoverEvent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
|
||||
import net.momirealms.craftengine.core.pack.Pack;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
|
||||
import net.momirealms.craftengine.core.plugin.locale.MessageConstants;
|
||||
import net.momirealms.craftengine.core.util.AdventureHelper;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.incendo.cloud.Command;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class ListResourceCommand extends BukkitCommandFeature<CommandSender> {
|
||||
|
||||
public ListResourceCommand(CraftEngineCommandManager<CommandSender> commandManager, CraftEngine plugin) {
|
||||
super(commandManager, plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Command.Builder<? extends CommandSender> assembleCommand(org.incendo.cloud.CommandManager<CommandSender> manager, Command.Builder<CommandSender> builder) {
|
||||
return builder
|
||||
.handler(context -> {
|
||||
Collection<Pack> packs = plugin().packManager().loadedPacks();
|
||||
List<Pack> enabled = new ArrayList<>();
|
||||
List<Pack> disabled = new ArrayList<>();
|
||||
for (Pack pack : packs) {
|
||||
if (pack.enabled()) {
|
||||
enabled.add(pack);
|
||||
} else {
|
||||
disabled.add(pack);
|
||||
}
|
||||
}
|
||||
handleFeedback(context, MessageConstants.COMMAND_RESOURCE_LIST, Component.text(enabled.size()), Component.empty().children(getChildComponents(enabled)), Component.text(disabled.size()), Component.empty().children(getChildComponents(disabled)));
|
||||
});
|
||||
}
|
||||
|
||||
private List<Component> getChildComponents(List<Pack> disabled) {
|
||||
List<Component> components = new ArrayList<>();
|
||||
for (int i = 0; i < disabled.size(); i++) {
|
||||
Pack pack = disabled.get(i);
|
||||
components.add(getPackComponent(pack));
|
||||
if (i != disabled.size() - 1) {
|
||||
components.add(Component.text(", "));
|
||||
}
|
||||
}
|
||||
if (components.isEmpty()) {
|
||||
return List.of(Component.text("[]"));
|
||||
}
|
||||
return components;
|
||||
}
|
||||
|
||||
private Component getPackComponent(Pack pack) {
|
||||
String description = pack.meta().description();
|
||||
String version = pack.meta().version();
|
||||
String author = pack.meta().author();
|
||||
String text = version == null ? pack.name() : pack.name() + " v" + version;
|
||||
Component base = Component.text("[" + text + "]");
|
||||
if (author != null || description != null) {
|
||||
if (author != null && description != null) {
|
||||
base = base.hoverEvent(HoverEvent.showText(Component.empty().children(List.of(Component.text("by: " + author).color(NamedTextColor.YELLOW), Component.newline(), AdventureHelper.miniMessage().deserialize(description)))));
|
||||
} else if (author != null) {
|
||||
base = base.hoverEvent(HoverEvent.showText(Component.text("by: " + author)));
|
||||
} else {
|
||||
base = base.hoverEvent(HoverEvent.showText(AdventureHelper.miniMessage().deserialize(description)));
|
||||
}
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFeatureID() {
|
||||
return "list_resource";
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,6 @@ public class ReloadCommand extends BukkitCommandFeature<CommandSender> {
|
||||
}
|
||||
if (argument == ReloadArgument.CONFIG) {
|
||||
try {
|
||||
RELOAD_PACK_FLAG = true;
|
||||
plugin().reloadPlugin(plugin().scheduler().async(), r -> plugin().scheduler().sync().run(r), false).thenAccept(reloadResult -> {
|
||||
handleFeedback(context, MessageConstants.COMMAND_RELOAD_CONFIG_SUCCESS,
|
||||
Component.text(reloadResult.asyncTime() + reloadResult.syncTime()),
|
||||
@@ -49,13 +48,13 @@ public class ReloadCommand extends BukkitCommandFeature<CommandSender> {
|
||||
}
|
||||
} else if (argument == ReloadArgument.RECIPE) {
|
||||
try {
|
||||
RELOAD_PACK_FLAG = true;
|
||||
plugin().reloadPlugin(plugin().scheduler().async(), r -> plugin().scheduler().sync().run(r), true).thenAccept(reloadResult -> {
|
||||
handleFeedback(context, MessageConstants.COMMAND_RELOAD_CONFIG_SUCCESS,
|
||||
Component.text(reloadResult.asyncTime() + reloadResult.syncTime()),
|
||||
Component.text(reloadResult.asyncTime()),
|
||||
Component.text(reloadResult.syncTime())
|
||||
);
|
||||
|
||||
});
|
||||
} catch (Exception e) {
|
||||
handleFeedback(context, MessageConstants.COMMAND_RELOAD_CONFIG_FAILURE);
|
||||
@@ -69,24 +68,30 @@ public class ReloadCommand extends BukkitCommandFeature<CommandSender> {
|
||||
long time2 = System.currentTimeMillis();
|
||||
long packTime = time2 - time1;
|
||||
handleFeedback(context, MessageConstants.COMMAND_RELOAD_PACK_SUCCESS, Component.text(packTime));
|
||||
|
||||
} catch (Exception e) {
|
||||
handleFeedback(context, MessageConstants.COMMAND_RELOAD_PACK_FAILURE);
|
||||
plugin().logger().warn("Failed to generate resource pack", e);
|
||||
}
|
||||
});
|
||||
} else if (argument == ReloadArgument.ALL) {
|
||||
RELOAD_PACK_FLAG = true;
|
||||
try {
|
||||
plugin().reloadPlugin(plugin().scheduler().async(), r -> plugin().scheduler().sync().run(r), true).thenAcceptAsync(reloadResult -> {
|
||||
long time1 = System.currentTimeMillis();
|
||||
plugin().packManager().generateResourcePack();
|
||||
long time2 = System.currentTimeMillis();
|
||||
long packTime = time2 - time1;
|
||||
handleFeedback(context, MessageConstants.COMMAND_RELOAD_ALL_SUCCESS,
|
||||
Component.text(reloadResult.asyncTime() + reloadResult.syncTime() + packTime),
|
||||
Component.text(reloadResult.asyncTime()),
|
||||
Component.text(reloadResult.syncTime()),
|
||||
Component.text(packTime)
|
||||
);
|
||||
try {
|
||||
long time1 = System.currentTimeMillis();
|
||||
plugin().packManager().generateResourcePack();
|
||||
long time2 = System.currentTimeMillis();
|
||||
long packTime = time2 - time1;
|
||||
handleFeedback(context, MessageConstants.COMMAND_RELOAD_ALL_SUCCESS,
|
||||
Component.text(reloadResult.asyncTime() + reloadResult.syncTime() + packTime),
|
||||
Component.text(reloadResult.asyncTime()),
|
||||
Component.text(reloadResult.syncTime()),
|
||||
Component.text(packTime)
|
||||
);
|
||||
} finally {
|
||||
RELOAD_PACK_FLAG = false;
|
||||
}
|
||||
}, plugin().scheduler().async());
|
||||
} catch (Exception e) {
|
||||
handleFeedback(context, MessageConstants.COMMAND_RELOAD_ALL_FAILURE);
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
package net.momirealms.craftengine.bukkit.plugin.command.feature;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
|
||||
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.entity.Pose;
|
||||
import org.incendo.cloud.Command;
|
||||
|
||||
public class TestCommand extends BukkitCommandFeature<CommandSender> {
|
||||
@@ -23,10 +20,6 @@ public class TestCommand extends BukkitCommandFeature<CommandSender> {
|
||||
.senderType(Player.class)
|
||||
.handler(context -> {
|
||||
Player player = context.sender();
|
||||
ItemStack itemStack = new ItemStack(Material.STONE);
|
||||
Item<ItemStack> wrapped = BukkitItemManager.instance().wrap(itemStack);
|
||||
wrapped.lore(null);
|
||||
player.getInventory().addItem(wrapped.load());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package net.momirealms.craftengine.bukkit.plugin.command.feature;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHost;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
|
||||
import net.momirealms.craftengine.core.plugin.locale.MessageConstants;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.incendo.cloud.Command;
|
||||
|
||||
public class UploadPackCommand extends BukkitCommandFeature<CommandSender> {
|
||||
|
||||
public UploadPackCommand(CraftEngineCommandManager<CommandSender> commandManager, CraftEngine plugin) {
|
||||
super(commandManager, plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Command.Builder<? extends CommandSender> assembleCommand(org.incendo.cloud.CommandManager<CommandSender> manager, Command.Builder<CommandSender> builder) {
|
||||
return builder
|
||||
.handler(context -> {
|
||||
ResourcePackHost host = plugin().packManager().resourcePackHost();
|
||||
if (host.canUpload()) {
|
||||
handleFeedback(context, MessageConstants.COMMAND_UPLOAD_ON_PROGRESS);
|
||||
plugin().packManager().uploadResourcePack();
|
||||
} else {
|
||||
handleFeedback(context, MessageConstants.COMMAND_UPLOAD_FAILURE_NOT_SUPPORTED, Component.text(host.type().value()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFeatureID() {
|
||||
return "upload";
|
||||
}
|
||||
}
|
||||
@@ -130,6 +130,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
|
||||
registerNMSPacketConsumer(PacketConsumers.PLAYER_INFO_UPDATE, Reflections.clazz$ClientboundPlayerInfoUpdatePacket);
|
||||
registerNMSPacketConsumer(PacketConsumers.PLAYER_ACTION, Reflections.clazz$ServerboundPlayerActionPacket);
|
||||
registerNMSPacketConsumer(PacketConsumers.SWING_HAND, Reflections.clazz$ServerboundSwingPacket);
|
||||
registerNMSPacketConsumer(PacketConsumers.HELLO_C2S, Reflections.clazz$ServerboundHelloPacket);
|
||||
registerNMSPacketConsumer(PacketConsumers.USE_ITEM_ON, Reflections.clazz$ServerboundUseItemOnPacket);
|
||||
registerNMSPacketConsumer(PacketConsumers.PICK_ITEM_FROM_BLOCK, Reflections.clazz$ServerboundPickItemFromBlockPacket);
|
||||
registerNMSPacketConsumer(PacketConsumers.SET_CREATIVE_SLOT, Reflections.clazz$ServerboundSetCreativeModeSlotPacket);
|
||||
@@ -144,6 +145,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
|
||||
registerNMSPacketConsumer(PacketConsumers.SIGN_UPDATE, Reflections.clazz$ServerboundSignUpdatePacket);
|
||||
registerNMSPacketConsumer(PacketConsumers.EDIT_BOOK, Reflections.clazz$ServerboundEditBookPacket);
|
||||
registerNMSPacketConsumer(PacketConsumers.CUSTOM_PAYLOAD, Reflections.clazz$ServerboundCustomPayloadPacket);
|
||||
registerNMSPacketConsumer(PacketConsumers.RESOURCE_PACK_PUSH, Reflections.clazz$ClientboundResourcePackPushPacket);
|
||||
registerByteBufPacketConsumer(PacketConsumers.SECTION_BLOCK_UPDATE, this.packetIds.clientboundSectionBlocksUpdatePacket());
|
||||
registerByteBufPacketConsumer(PacketConsumers.BLOCK_UPDATE, this.packetIds.clientboundBlockUpdatePacket());
|
||||
registerByteBufPacketConsumer(VersionHelper.isVersionNewerThan1_21_3() ? PacketConsumers.LEVEL_PARTICLE_1_21_3 : (VersionHelper.isVersionNewerThan1_20_5() ? PacketConsumers.LEVEL_PARTICLE_1_20_5 : PacketConsumers.LEVEL_PARTICLE_1_20), this.packetIds.clientboundLevelParticlesPacket());
|
||||
@@ -286,7 +288,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
|
||||
return hasModelEngine;
|
||||
}
|
||||
|
||||
public void receivePacket(@NotNull NetWorkUser player, Object packet) {
|
||||
public void simulatePacket(@NotNull NetWorkUser player, Object packet) {
|
||||
Channel channel = player.nettyChannel();
|
||||
if (channel.isOpen()) {
|
||||
List<String> handlerNames = channel.pipeline().names();
|
||||
|
||||
@@ -13,6 +13,7 @@ import net.momirealms.craftengine.bukkit.compatibility.modelengine.ModelEngineUt
|
||||
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager;
|
||||
import net.momirealms.craftengine.bukkit.entity.furniture.LoadedFurniture;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.pack.BukkitPackManager;
|
||||
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||
import net.momirealms.craftengine.bukkit.plugin.injector.BukkitInjector;
|
||||
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
|
||||
@@ -21,6 +22,8 @@ import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||
import net.momirealms.craftengine.core.entity.player.InteractionHand;
|
||||
import net.momirealms.craftengine.core.font.FontManager;
|
||||
import net.momirealms.craftengine.core.font.IllegalCharacterProcessResult;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackDownloadData;
|
||||
import net.momirealms.craftengine.core.pack.host.ResourcePackHost;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.plugin.network.ConnectionState;
|
||||
@@ -28,6 +31,7 @@ import net.momirealms.craftengine.core.plugin.network.NetWorkUser;
|
||||
import net.momirealms.craftengine.core.plugin.network.NetworkManager;
|
||||
import net.momirealms.craftengine.core.util.*;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
import net.momirealms.craftengine.core.world.WorldEvents;
|
||||
import net.momirealms.craftengine.core.world.chunk.Palette;
|
||||
import net.momirealms.craftengine.core.world.chunk.PalettedContainer;
|
||||
import net.momirealms.craftengine.core.world.chunk.packet.MCSection;
|
||||
@@ -50,6 +54,7 @@ public class PacketConsumers {
|
||||
private static int[] mappingsMOD;
|
||||
private static IntIdentityList BLOCK_LIST;
|
||||
private static IntIdentityList BIOME_LIST;
|
||||
private static final UUID EMPTY_UUID = new UUID(0, 0);
|
||||
|
||||
public static void init(Map<Integer, Integer> map, int registrySize) {
|
||||
mappings = new int[registrySize];
|
||||
@@ -221,7 +226,7 @@ public class PacketConsumers {
|
||||
try {
|
||||
FriendlyByteBuf buf = event.getBuffer();
|
||||
int eventId = buf.readInt();
|
||||
if (eventId != 2001) return;
|
||||
if (eventId != WorldEvents.BLOCK_BREAK_EFFECT) return;
|
||||
BlockPos blockPos = buf.readBlockPos(buf);
|
||||
int state = buf.readInt();
|
||||
boolean global = buf.readBoolean();
|
||||
@@ -1118,38 +1123,32 @@ public class PacketConsumers {
|
||||
if (Config.enableSoundSystem()) {
|
||||
Object blockOwner = Reflections.field$StateHolder$owner.get(blockState);
|
||||
if (BukkitBlockManager.instance().isBlockSoundRemoved(blockOwner)) {
|
||||
player.startMiningBlock(world, pos, blockState, false, null);
|
||||
player.startMiningBlock(pos, blockState, null);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (player.isMiningBlock() || player.shouldSyncAttribute()) {
|
||||
if (player.isMiningBlock()) {
|
||||
player.stopMiningBlock();
|
||||
} else {
|
||||
player.setClientSideCanBreakBlock(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (player.isAdventureMode()) {
|
||||
Object itemStack = FastNMS.INSTANCE.method$CraftItemStack$asNMSCopy(player.platformPlayer().getInventory().getItemInMainHand());
|
||||
Object blockPos = LocationUtils.toBlockPos(pos);
|
||||
Object blockInWorld = Reflections.constructor$BlockInWorld.newInstance(serverLevel, blockPos, false);
|
||||
if (VersionHelper.isVersionNewerThan1_20_5()) {
|
||||
if (Reflections.method$ItemStack$canBreakBlockInAdventureMode != null
|
||||
&& !(boolean) Reflections.method$ItemStack$canBreakBlockInAdventureMode.invoke(
|
||||
itemStack, blockInWorld
|
||||
)) {
|
||||
if (Config.simplifyAdventureBreakCheck()) {
|
||||
ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockStateUnsafe(stateId);
|
||||
if (!player.canBreak(pos, state.vanillaBlockState().handle())) {
|
||||
player.preventMiningBlock();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (Reflections.method$ItemStack$canDestroy != null
|
||||
&& !(boolean) Reflections.method$ItemStack$canDestroy.invoke(
|
||||
itemStack, Reflections.instance$BuiltInRegistries$BLOCK, blockInWorld
|
||||
)) {
|
||||
if (!player.canBreak(pos, null)) {
|
||||
player.preventMiningBlock();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
player.startMiningBlock(world, pos, blockState, true, BukkitBlockManager.instance().getImmutableBlockStateUnsafe(stateId));
|
||||
player.startMiningBlock(pos, blockState, BukkitBlockManager.instance().getImmutableBlockStateUnsafe(stateId));
|
||||
} else if (action == Reflections.instance$ServerboundPlayerActionPacket$Action$ABORT_DESTROY_BLOCK) {
|
||||
if (player.isMiningBlock()) {
|
||||
player.abortMiningBlock();
|
||||
@@ -1161,6 +1160,28 @@ public class PacketConsumers {
|
||||
}
|
||||
}
|
||||
|
||||
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> HELLO_C2S = (user, event, packet) -> {
|
||||
try {
|
||||
BukkitServerPlayer player = (BukkitServerPlayer) user;
|
||||
String name = (String) Reflections.field$ServerboundHelloPacket$name.get(packet);
|
||||
player.setName(name);
|
||||
if (VersionHelper.isVersionNewerThan1_20_2()) {
|
||||
UUID uuid = (UUID) Reflections.field$ServerboundHelloPacket$uuid.get(packet);
|
||||
player.setUUID(uuid);
|
||||
} else {
|
||||
@SuppressWarnings("unchecked")
|
||||
Optional<UUID> uuid = (Optional<UUID>) Reflections.field$ServerboundHelloPacket$uuid.get(packet);
|
||||
if (uuid.isPresent()) {
|
||||
player.setUUID(uuid.get());
|
||||
} else {
|
||||
player.setUUID(UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
CraftEngine.instance().logger().warn("Failed to handle ServerboundHelloPacket", e);
|
||||
}
|
||||
};
|
||||
|
||||
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> SWING_HAND = (user, event, packet) -> {
|
||||
try {
|
||||
if (!user.isOnline()) return;
|
||||
@@ -1272,7 +1293,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;
|
||||
@@ -2088,4 +2109,36 @@ public class PacketConsumers {
|
||||
CraftEngine.instance().logger().warn("Failed to handle ClientboundSetScorePacket", e);
|
||||
}
|
||||
};
|
||||
|
||||
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> RESOURCE_PACK_PUSH = (user, event, packet) -> {
|
||||
try {
|
||||
if (!VersionHelper.isVersionNewerThan1_20_2()) return;
|
||||
// we should only handle fake urls
|
||||
String url = FastNMS.INSTANCE.field$ClientboundResourcePackPushPacket$url(packet);
|
||||
if (!url.equals(BukkitPackManager.FAKE_URL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.setCancelled(true);
|
||||
UUID packUUID = FastNMS.INSTANCE.field$ClientboundResourcePackPushPacket$uuid(packet);
|
||||
ResourcePackHost host = CraftEngine.instance().packManager().resourcePackHost();
|
||||
host.requestResourcePackDownloadLink(user.uuid()).thenAccept(dataList -> {
|
||||
if (dataList.isEmpty()) {
|
||||
user.simulatePacket(FastNMS.INSTANCE.constructor$ServerboundResourcePackPacket$SUCCESSFULLY_LOADED(packUUID));
|
||||
return;
|
||||
}
|
||||
for (ResourcePackDownloadData data : dataList) {
|
||||
Object newPacket = ResourcePackUtils.createPacket(data.uuid(), data.url(), data.sha1());
|
||||
user.nettyChannel().writeAndFlush(newPacket);
|
||||
user.addResourcePackUUID(data.uuid());
|
||||
}
|
||||
}).exceptionally(throwable -> {
|
||||
CraftEngine.instance().logger().warn("Failed to handle ClientboundResourcePackPushPacket", throwable);
|
||||
user.simulatePacket(FastNMS.INSTANCE.constructor$ServerboundResourcePackPacket$SUCCESSFULLY_LOADED(packUUID));
|
||||
return null;
|
||||
});
|
||||
} catch (Exception e) {
|
||||
CraftEngine.instance().logger().warn("Failed to handle ClientboundResourcePackPushPacket", e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -9,50 +9,54 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||
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.ImmutableBlockState;
|
||||
import net.momirealms.craftengine.core.block.PackedBlockState;
|
||||
import net.momirealms.craftengine.core.entity.player.InteractionHand;
|
||||
import net.momirealms.craftengine.core.entity.player.Player;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.item.ItemKeys;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.plugin.network.ConnectionState;
|
||||
import net.momirealms.craftengine.core.util.Direction;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
import net.momirealms.craftengine.core.world.World;
|
||||
import net.momirealms.craftengine.core.world.*;
|
||||
import net.momirealms.craftengine.core.world.WorldEvents;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.inventory.EquipmentSlot;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.PlayerInventory;
|
||||
import org.bukkit.util.RayTraceResult;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
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 String name;
|
||||
private UUID uuid;
|
||||
private ConnectionState decoderState;
|
||||
private ConnectionState encoderState;
|
||||
|
||||
private final Set<UUID> resourcePackUUID = Collections.synchronizedSet(new HashSet<>());
|
||||
// some references
|
||||
private Reference<org.bukkit.entity.Player> playerRef;
|
||||
private Reference<Object> 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;
|
||||
@@ -61,15 +65,25 @@ public class BukkitServerPlayer extends Player {
|
||||
private boolean isDestroyingCustomBlock;
|
||||
private boolean swingHandAck;
|
||||
private float miningProgress;
|
||||
|
||||
// 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<Integer, List<Integer>> furnitureView = new ConcurrentHashMap<>();
|
||||
private final Map<Integer, Object> entityTypeView = new ConcurrentHashMap<>();
|
||||
|
||||
@@ -79,8 +93,17 @@ public class BukkitServerPlayer extends Player {
|
||||
}
|
||||
|
||||
public void setPlayer(org.bukkit.entity.Player player) {
|
||||
playerRef = new WeakReference<>(player);
|
||||
serverPlayerRef = new WeakReference<>(FastNMS.INSTANCE.method$CraftPlayer$getHandle(player));
|
||||
this.playerRef = new WeakReference<>(player);
|
||||
this.serverPlayerRef = new WeakReference<>(FastNMS.INSTANCE.method$CraftPlayer$getHandle(player));
|
||||
this.uuid = player.getUniqueId();
|
||||
this.name = player.getName();
|
||||
if (Reflections.method$CraftPlayer$setSimplifyContainerDesyncCheck != null) {
|
||||
try {
|
||||
Reflections.method$CraftPlayer$setSimplifyContainerDesyncCheck.invoke(player, true);
|
||||
} catch (Exception e) {
|
||||
this.plugin.logger().warn("Failed to setSimplifyContainerDesyncCheck", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -108,8 +131,8 @@ public class BukkitServerPlayer extends Player {
|
||||
|
||||
@Override
|
||||
public boolean shouldSyncAttribute() {
|
||||
long current = System.currentTimeMillis();
|
||||
if (current - this.lastAttributeSyncTime > 10000) {
|
||||
long current = gameTicks();
|
||||
if (current - this.lastAttributeSyncTime > 100) {
|
||||
this.lastAttributeSyncTime = current;
|
||||
return true;
|
||||
}
|
||||
@@ -136,6 +159,16 @@ public class BukkitServerPlayer extends Player {
|
||||
return platformPlayer().getGameMode() == GameMode.ADVENTURE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBreak(BlockPos pos, @Nullable Object state) {
|
||||
return AdventureModeUtils.canBreak(platformPlayer().getInventory().getItemInMainHand(), new Location(platformPlayer().getWorld(), pos.x(), pos.y(), pos.z()), state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPlace(BlockPos pos, @Nullable Object state) {
|
||||
return AdventureModeUtils.canPlace(platformPlayer().getInventory().getItemInMainHand(), new Location(platformPlayer().getWorld(), pos.x(), pos.y(), pos.z()), state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendActionBar(Component text) {
|
||||
try {
|
||||
@@ -156,15 +189,14 @@ public class BukkitServerPlayer extends Player {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int lastSuccessfulInteractionTick() {
|
||||
return lastSuccessfulInteraction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int gameTicks() {
|
||||
try {
|
||||
Object serverPlayer = serverPlayer();
|
||||
Object gameMode = Reflections.field$ServerPlayer$gameMode.get(serverPlayer);
|
||||
return (int) Reflections.field$ServerPlayerGameMode$gameTicks.get(gameMode);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException("Failed to get current tick", e);
|
||||
}
|
||||
return this.gameTicks;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -190,9 +222,24 @@ public class BukkitServerPlayer extends Player {
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
org.bukkit.entity.Player player = platformPlayer();
|
||||
if (player == null) return "Unknown";
|
||||
return player.getName();
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
if (this.name != null) return;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID uuid() {
|
||||
return this.uuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUUID(UUID uuid) {
|
||||
if (this.uuid != null) return;
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -210,39 +257,19 @@ public class BukkitServerPlayer extends Player {
|
||||
platformPlayer().closeInventory();
|
||||
}
|
||||
|
||||
// TODO DO NOT USE BUKKIT API
|
||||
@Override
|
||||
public BlockHitResult rayTrace(double distance, FluidCollisionRule collisionRule) {
|
||||
RayTraceResult result = platformPlayer().rayTraceBlocks(distance, FluidUtils.toCollisionRule(collisionRule));
|
||||
if (result == null) {
|
||||
Location eyeLocation = platformPlayer().getEyeLocation();
|
||||
Location targetLocation = eyeLocation.clone();
|
||||
targetLocation.add(eyeLocation.getDirection().multiply(distance));
|
||||
return BlockHitResult.miss(new Vec3d(eyeLocation.getX(), eyeLocation.getY(), eyeLocation.getZ()),
|
||||
Direction.getApproximateNearest(eyeLocation.getX() - targetLocation.getX(), eyeLocation.getY() - targetLocation.getY(), eyeLocation.getZ() - targetLocation.getZ()),
|
||||
new BlockPos(targetLocation.getBlockX(), targetLocation.getBlockY(), targetLocation.getBlockZ())
|
||||
);
|
||||
} else {
|
||||
Vector hitPos = result.getHitPosition();
|
||||
Block hitBlock = result.getHitBlock();
|
||||
Location hitBlockLocation = hitBlock.getLocation();
|
||||
return new BlockHitResult(
|
||||
new Vec3d(hitPos.getX(), hitPos.getY(), hitPos.getZ()),
|
||||
DirectionUtils.toDirection(result.getHitBlockFace()),
|
||||
new BlockPos(hitBlockLocation.getBlockX(), hitBlockLocation.getBlockY(), hitBlockLocation.getBlockZ()),
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPacket(Object packet, boolean immediately) {
|
||||
this.plugin.networkManager().sendPacket(this, packet, immediately);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receivePacket(Object packet) {
|
||||
this.plugin.networkManager().receivePacket(this, packet);
|
||||
public void sendPackets(List<Object> packet, boolean immediately) {
|
||||
this.plugin.networkManager().sendPackets(this, packet, immediately);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void simulatePacket(Object packet) {
|
||||
this.plugin.networkManager().simulatePacket(this, packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -290,48 +317,89 @@ 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 (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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDestroyProgress(Object blockState, BlockPos pos) {
|
||||
try {
|
||||
Object serverPlayer = serverPlayer();
|
||||
Object blockPos = LocationUtils.toBlockPos(pos.x(), pos.y(), pos.z());
|
||||
return (float) Reflections.method$BlockStateBase$getDestroyProgress.invoke(blockState, serverPlayer, Reflections.method$Entity$level.invoke(serverPlayer), blockPos);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
this.plugin.logger().warn("Failed to get destroy progress for player " + platformPlayer().getName());
|
||||
return 0f;
|
||||
return FastNMS.INSTANCE.method$BlockStateBase$getDestroyProgress(blockState, serverPlayer(), FastNMS.INSTANCE.field$CraftWorld$ServerLevel(platformPlayer().getWorld()), LocationUtils.toBlockPos(pos));
|
||||
}
|
||||
|
||||
private void predictNextBlockToMine() {
|
||||
double range = getCachedInteractionRange() + Config.extendedInteractionRange();
|
||||
RayTraceResult result = platformPlayer().rayTraceBlocks(range, FluidCollisionMode.NEVER);
|
||||
if (result == null) {
|
||||
if (!this.clientSideCanBreak) {
|
||||
setClientSideCanBreakBlock(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
Block hitBlock = result.getHitBlock();
|
||||
if (hitBlock == null) {
|
||||
if (!this.clientSideCanBreak) {
|
||||
setClientSideCanBreakBlock(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
int stateId = BlockStateUtils.blockDataToId(hitBlock.getBlockData());
|
||||
if (BlockStateUtils.isVanillaBlock(stateId)) {
|
||||
if (!this.clientSideCanBreak) {
|
||||
setClientSideCanBreakBlock(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (this.clientSideCanBreak) {
|
||||
setClientSideCanBreakBlock(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void startMiningBlock(org.bukkit.World world, BlockPos pos, Object state, boolean custom, @Nullable ImmutableBlockState immutableBlockState) {
|
||||
public void startMiningBlock(BlockPos pos, Object state, @Nullable ImmutableBlockState immutableBlockState) {
|
||||
// instant break
|
||||
boolean custom = immutableBlockState != null;
|
||||
if (custom && getDestroyProgress(state, pos) >= 1f) {
|
||||
assert immutableBlockState != null;
|
||||
// not an instant break on client side
|
||||
PackedBlockState vanillaBlockState = immutableBlockState.vanillaBlockState();
|
||||
// if it's not an instant break on client side, we should resend level event
|
||||
if (vanillaBlockState != null && getDestroyProgress(vanillaBlockState.handle(), pos) < 1f) {
|
||||
try {
|
||||
Object levelEventPacket = Reflections.constructor$ClientboundLevelEventPacket.newInstance(2001, LocationUtils.toBlockPos(pos), BlockStateUtils.blockStateToId(this.destroyedState), false);
|
||||
sendPacket(levelEventPacket, false);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
this.plugin.logger().warn("Failed to send level event packet", e);
|
||||
}
|
||||
Object levelEventPacket = FastNMS.INSTANCE.constructor$ClientboundLevelEventPacket(
|
||||
WorldEvents.BLOCK_BREAK_EFFECT, LocationUtils.toBlockPos(pos), BlockStateUtils.blockStateToId(state), false);
|
||||
sendPacket(levelEventPacket, false);
|
||||
}
|
||||
//ParticleUtils.addBlockBreakParticles(world, LocationUtils.toBlockPos(pos), state);
|
||||
return;
|
||||
}
|
||||
setCanBreakBlock(!custom);
|
||||
if (!custom && !this.clientSideCanBreak && getDestroyProgress(state, pos) >= 1f) {
|
||||
Object levelEventPacket = FastNMS.INSTANCE.constructor$ClientboundLevelEventPacket(
|
||||
WorldEvents.BLOCK_BREAK_EFFECT, LocationUtils.toBlockPos(pos), BlockStateUtils.blockStateToId(state), false);
|
||||
sendPacket(levelEventPacket, false);
|
||||
}
|
||||
// if it's a custom one, we prevent it, otherwise we allow it
|
||||
setClientSideCanBreakBlock(!custom);
|
||||
// set some base info
|
||||
setDestroyPos(pos);
|
||||
setDestroyedState(state);
|
||||
setIsDestroyingBlock(true, custom);
|
||||
}
|
||||
|
||||
private void setCanBreakBlock(boolean canBreak) {
|
||||
@Override
|
||||
public void setClientSideCanBreakBlock(boolean canBreak) {
|
||||
try {
|
||||
if (this.clientSideCanBreak == canBreak && !shouldSyncAttribute()) {
|
||||
return;
|
||||
}
|
||||
this.clientSideCanBreak = canBreak;
|
||||
if (canBreak) {
|
||||
if (VersionHelper.isVersionNewerThan1_20_5()) {
|
||||
Object serverPlayer = serverPlayer();
|
||||
@@ -353,8 +421,7 @@ public class BukkitServerPlayer extends Player {
|
||||
} else {
|
||||
Object fatiguePacket = MobEffectUtils.createPacket(Reflections.instance$MobEffecr$mining_fatigue, entityID(), (byte) 9, -1, false, false, false);
|
||||
Object hastePacket = MobEffectUtils.createPacket(Reflections.instance$MobEffecr$haste, entityID(), (byte) 0, -1, false, false, false);
|
||||
sendPacket(fatiguePacket, true);
|
||||
sendPacket(hastePacket, true);
|
||||
sendPackets(List.of(fatiguePacket, hastePacket), true);
|
||||
}
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
@@ -364,17 +431,26 @@ public class BukkitServerPlayer extends Player {
|
||||
|
||||
@Override
|
||||
public void stopMiningBlock() {
|
||||
setCanBreakBlock(true);
|
||||
setClientSideCanBreakBlock(true);
|
||||
setIsDestroyingBlock(false, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preventMiningBlock() {
|
||||
setCanBreakBlock(false);
|
||||
setClientSideCanBreakBlock(false);
|
||||
setIsDestroyingBlock(false, false);
|
||||
abortMiningBlock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abortMiningBlock() {
|
||||
this.swingHandAck = false;
|
||||
this.miningProgress = 0;
|
||||
if (this.destroyPos != null) {
|
||||
this.broadcastDestroyProgress(platformPlayer(), this.destroyPos, LocationUtils.toBlockPos(this.destroyPos), -1);
|
||||
}
|
||||
}
|
||||
|
||||
private void resetEffect(Object mobEffect) throws ReflectiveOperationException {
|
||||
Object effectInstance = Reflections.method$ServerPlayer$getEffect.invoke(serverPlayer(), mobEffect);
|
||||
Object packet;
|
||||
@@ -386,27 +462,16 @@ public class BukkitServerPlayer extends Player {
|
||||
sendPacket(packet, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abortMiningBlock() {
|
||||
abortDestroyProgress();
|
||||
}
|
||||
|
||||
private void tickBlockDestroy() {
|
||||
// prevent server from taking over breaking blocks
|
||||
if (this.isDestroyingCustomBlock) {
|
||||
try {
|
||||
Object serverPlayer = serverPlayer();
|
||||
Object gameMode = Reflections.field$ServerPlayer$gameMode.get(serverPlayer);
|
||||
Reflections.field$ServerPlayerGameMode$isDestroyingBlock.set(gameMode, false);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
// if player swings hand is this tick
|
||||
if (!this.swingHandAck) return;
|
||||
this.swingHandAck = false;
|
||||
int currentTick = gameTicks();
|
||||
// optimize break speed, otherwise it would be too fast
|
||||
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();
|
||||
@@ -418,8 +483,8 @@ public class BukkitServerPlayer extends Player {
|
||||
}
|
||||
Object blockPos = LocationUtils.toBlockPos(hitPos);
|
||||
Object serverPlayer = serverPlayer();
|
||||
Object gameMode = Reflections.field$ServerPlayer$gameMode.get(serverPlayer);
|
||||
int currentTick = (int) Reflections.field$ServerPlayerGameMode$gameTicks.get(gameMode);
|
||||
|
||||
// send hit sound if the sound is removed
|
||||
if (currentTick - this.lastHitBlockTime > 3) {
|
||||
Object blockOwner = Reflections.field$StateHolder$owner.get(this.destroyedState);
|
||||
Object soundType = Reflections.field$BlockBehaviour$soundType.get(blockOwner);
|
||||
@@ -431,9 +496,14 @@ public class BukkitServerPlayer extends Player {
|
||||
|
||||
// accumulate progress (custom blocks only)
|
||||
if (this.isDestroyingCustomBlock) {
|
||||
// prevent server from taking over breaking custom blocks
|
||||
Object gameMode = FastNMS.INSTANCE.field$ServerPlayer$gameMode(serverPlayer);
|
||||
Reflections.field$ServerPlayerGameMode$isDestroyingBlock.set(gameMode, false);
|
||||
// check item in hand
|
||||
Item<ItemStack> item = this.getItemInHand(InteractionHand.MAIN_HAND);
|
||||
if (item != null) {
|
||||
Material itemMaterial = item.getItem().getType();
|
||||
// creative mode + invalid item in hand
|
||||
if (canInstabuild() && (itemMaterial == Material.DEBUG_STICK
|
||||
|| itemMaterial == Material.TRIDENT
|
||||
|| (VersionHelper.isVersionNewerThan1_20_5() && itemMaterial == MaterialUtils.MACE)
|
||||
@@ -442,27 +512,66 @@ public class BukkitServerPlayer extends Player {
|
||||
}
|
||||
}
|
||||
|
||||
float progressToAdd = (float) Reflections.method$BlockStateBase$getDestroyProgress.invoke(this.destroyedState, serverPlayer, Reflections.method$Entity$level.invoke(serverPlayer), blockPos);
|
||||
float progressToAdd = getDestroyProgress(this.destroyedState, hitPos);
|
||||
int id = BlockStateUtils.blockStateToId(this.destroyedState);
|
||||
ImmutableBlockState customState = BukkitBlockManager.instance().getImmutableBlockState(id);
|
||||
if (customState != null && !customState.isEmpty()
|
||||
&& !customState.settings().isCorrectTool(item == null ? ItemKeys.AIR : item.id())) {
|
||||
progressToAdd *= customState.settings().incorrectToolSpeed();
|
||||
}
|
||||
// double check custom block
|
||||
if (customState != null && !customState.isEmpty()) {
|
||||
BlockSettings blockSettings = customState.settings();
|
||||
if (blockSettings.requireCorrectTool()) {
|
||||
if (item != null) {
|
||||
// it's correct on plugin side
|
||||
if (blockSettings.isCorrectTool(item.id())) {
|
||||
// but not on serverside
|
||||
if (!FastNMS.INSTANCE.method$ItemStack$isCorrectToolForDrops(item.getLiteralObject(), this.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(), this.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();
|
||||
}
|
||||
}
|
||||
|
||||
this.miningProgress = progressToAdd + miningProgress;
|
||||
int packetStage = (int) (this.miningProgress * 10.0F);
|
||||
if (packetStage != this.lastSentState) {
|
||||
this.lastSentState = packetStage;
|
||||
broadcastDestroyProgress(player, hitPos, blockPos, packetStage);
|
||||
}
|
||||
// accumulate progress
|
||||
this.miningProgress = progressToAdd + miningProgress;
|
||||
int packetStage = (int) (this.miningProgress * 10.0F);
|
||||
if (packetStage != this.lastSentState) {
|
||||
this.lastSentState = packetStage;
|
||||
// broadcast changes
|
||||
broadcastDestroyProgress(player, hitPos, blockPos, packetStage);
|
||||
}
|
||||
|
||||
if (this.miningProgress >= 1f) {
|
||||
//Reflections.method$ServerLevel$levelEvent.invoke(Reflections.field$CraftWorld$ServerLevel.get(player.getWorld()), null, 2001, blockPos, BlockStateUtils.blockStateToId(this.destroyedState));
|
||||
Reflections.method$ServerPlayerGameMode$destroyBlock.invoke(gameMode, blockPos);
|
||||
Object levelEventPacket = Reflections.constructor$ClientboundLevelEventPacket.newInstance(2001, blockPos, id, false);
|
||||
sendPacket(levelEventPacket, false);
|
||||
this.stopMiningBlock();
|
||||
// can break now
|
||||
if (this.miningProgress >= 1f) {
|
||||
// for simplified adventure break, switch mayBuild temporarily
|
||||
if (isAdventureMode() && Config.simplifyAdventureBreakCheck()) {
|
||||
// check the appearance state
|
||||
if (canBreak(hitPos, customState.vanillaBlockState().handle())) {
|
||||
// Error might occur so we use try here
|
||||
try {
|
||||
FastNMS.INSTANCE.setMayBuild(serverPlayer, true);
|
||||
Reflections.method$ServerPlayerGameMode$destroyBlock.invoke(gameMode, blockPos);
|
||||
} finally {
|
||||
FastNMS.INSTANCE.setMayBuild(serverPlayer, false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// normal break check
|
||||
Reflections.method$ServerPlayerGameMode$destroyBlock.invoke(gameMode, blockPos);
|
||||
}
|
||||
// send break particle + (removed sounds)
|
||||
sendPacket(FastNMS.INSTANCE.constructor$ClientboundLevelEventPacket(WorldEvents.BLOCK_BREAK_EFFECT, blockPos, id, false), false);
|
||||
this.lastSuccessfulBreak = currentTick;
|
||||
this.destroyPos = null;
|
||||
this.setIsDestroyingBlock(false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@@ -470,8 +579,8 @@ public class BukkitServerPlayer extends Player {
|
||||
}
|
||||
}
|
||||
|
||||
private void broadcastDestroyProgress(org.bukkit.entity.Player player, BlockPos hitPos, Object blockPos, int stage) throws ReflectiveOperationException {
|
||||
Object packet = Reflections.constructor$ClientboundBlockDestructionPacket.newInstance(Integer.MAX_VALUE - entityID(), blockPos, stage);
|
||||
private void broadcastDestroyProgress(org.bukkit.entity.Player player, BlockPos hitPos, Object blockPos, int stage) {
|
||||
Object packet = FastNMS.INSTANCE.constructor$ClientboundBlockDestructionPacket(Integer.MAX_VALUE - entityID(), blockPos, stage);
|
||||
for (org.bukkit.entity.Player other : player.getWorld().getPlayers()) {
|
||||
Location otherLocation = other.getLocation();
|
||||
double d0 = (double) hitPos.x() - otherLocation.getX();
|
||||
@@ -484,53 +593,28 @@ public class BukkitServerPlayer extends Player {
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getInteractionRange() {
|
||||
try {
|
||||
if (VersionHelper.isVersionNewerThan1_20_5()) {
|
||||
Object attributeInstance = Reflections.method$ServerPlayer$getAttribute.invoke(serverPlayer(), Reflections.instance$Holder$Attribute$block_interaction_range);
|
||||
if (attributeInstance == null) return 4.5d;
|
||||
return (double) Reflections.method$AttributeInstance$getValue.invoke(attributeInstance);
|
||||
} else {
|
||||
return 4.5d;
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
plugin.logger().warn("Failed to get interaction range for player " + platformPlayer().getName(), e);
|
||||
return 4.5d;
|
||||
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 value, boolean custom) {
|
||||
if (value) {
|
||||
this.isDestroyingBlock = true;
|
||||
this.isDestroyingCustomBlock = custom;
|
||||
this.swingHandAck = true;
|
||||
this.miningProgress = 0;
|
||||
} else {
|
||||
this.isDestroyingBlock = false;
|
||||
this.swingHandAck = false;
|
||||
if (this.destroyPos != null) {
|
||||
try {
|
||||
this.broadcastDestroyProgress(platformPlayer(), this.destroyPos, LocationUtils.toBlockPos(this.destroyPos), -1);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
plugin.logger().warn("Failed to set isDestroyingCustomBlock", e);
|
||||
}
|
||||
}
|
||||
this.destroyPos = null;
|
||||
this.miningProgress = 0;
|
||||
this.destroyedState = null;
|
||||
this.isDestroyingCustomBlock = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abortDestroyProgress() {
|
||||
this.swingHandAck = false;
|
||||
public void setIsDestroyingBlock(boolean is, boolean custom) {
|
||||
this.miningProgress = 0;
|
||||
if (this.destroyPos == null) return;
|
||||
try {
|
||||
this.broadcastDestroyProgress(platformPlayer(), this.destroyPos, LocationUtils.toBlockPos(this.destroyPos), -1);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
plugin.logger().warn("Failed to abort destroyProgress", e);
|
||||
this.isDestroyingBlock = is;
|
||||
this.isDestroyingCustomBlock = custom && is;
|
||||
if (is) {
|
||||
this.swingHandAck = true;
|
||||
} else {
|
||||
this.swingHandAck = false;
|
||||
this.destroyedState = null;
|
||||
if (this.destroyPos != null) {
|
||||
this.broadcastDestroyProgress(platformPlayer(), this.destroyPos, LocationUtils.toBlockPos(this.destroyPos), -1);
|
||||
this.destroyPos = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -657,9 +741,29 @@ public class BukkitServerPlayer extends Player {
|
||||
this.hasClientMod = enable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addResourcePackUUID(UUID uuid) {
|
||||
if (VersionHelper.isVersionNewerThan1_20_3()) {
|
||||
this.resourcePackUUID.add(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearView() {
|
||||
this.entityTypeView.clear();
|
||||
this.furnitureView.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unloadCurrentResourcePack() {
|
||||
if (!VersionHelper.isVersionNewerThan1_20_3()) {
|
||||
return;
|
||||
}
|
||||
if (decoderState() == ConnectionState.PLAY && !this.resourcePackUUID.isEmpty()) {
|
||||
for (UUID u : this.resourcePackUUID) {
|
||||
sendPacket(FastNMS.INSTANCE.constructor$ClientboundResourcePackPopPacket(u), true);
|
||||
}
|
||||
this.resourcePackUUID.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
package net.momirealms.craftengine.bukkit.util;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
import net.momirealms.craftengine.core.world.World;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
@SuppressWarnings("DuplicatedCode")
|
||||
public class AdventureModeUtils {
|
||||
|
||||
private AdventureModeUtils() {}
|
||||
|
||||
public static boolean canBreak(ItemStack itemStack, Location pos) {
|
||||
return canPlace(itemStack, pos, null);
|
||||
}
|
||||
|
||||
public static boolean canBreak(ItemStack itemStack, Location pos, Object state) {
|
||||
Object blockPos = LocationUtils.toBlockPos(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ());
|
||||
Object blockInWorld = FastNMS.INSTANCE.constructor$BlockInWorld(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(pos.getWorld()), blockPos, false);
|
||||
if (state != null) {
|
||||
try {
|
||||
Reflections.field$BlockInWorld$state.set(blockInWorld, state);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
CraftEngine.instance().logger().warn("Failed to set field$BlockInWorld$state", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return FastNMS.INSTANCE.canBreakInAdventureMode(FastNMS.INSTANCE.field$CraftItemStack$handle(itemStack), blockInWorld);
|
||||
}
|
||||
|
||||
public static boolean canPlace(Item<?> itemStack, World world, BlockPos pos, Object state) {
|
||||
Object blockPos = LocationUtils.toBlockPos(pos);
|
||||
Object item = itemStack == null ? Reflections.instance$ItemStack$Air : itemStack.getLiteralObject();
|
||||
Object blockInWorld = FastNMS.INSTANCE.constructor$BlockInWorld(FastNMS.INSTANCE.field$CraftWorld$ServerLevel((org.bukkit.World) world.platformWorld()), blockPos, false);
|
||||
if (state != null) {
|
||||
try {
|
||||
Reflections.field$BlockInWorld$state.set(blockInWorld, state);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
CraftEngine.instance().logger().warn("Failed to set field$BlockInWorld$state", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return FastNMS.INSTANCE.canPlaceInAdventureMode(item, blockInWorld);
|
||||
}
|
||||
|
||||
public static boolean canPlace(ItemStack itemStack, Location pos, Object state) {
|
||||
Object blockPos = LocationUtils.toBlockPos(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ());
|
||||
Object blockInWorld = FastNMS.INSTANCE.constructor$BlockInWorld(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(pos.getWorld()), blockPos, false);
|
||||
if (state != null) {
|
||||
try {
|
||||
Reflections.field$BlockInWorld$state.set(blockInWorld, state);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
CraftEngine.instance().logger().warn("Failed to set field$BlockInWorld$state", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return FastNMS.INSTANCE.canPlaceInAdventureMode(FastNMS.INSTANCE.field$CraftItemStack$handle(itemStack), blockInWorld);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package net.momirealms.craftengine.bukkit.util;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
@@ -9,6 +10,8 @@ import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class EntityUtils {
|
||||
|
||||
private EntityUtils() {}
|
||||
@@ -23,12 +26,11 @@ public class EntityUtils {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static Entity spawnEntity(World world, Location loc, EntityType type, org.bukkit.util.Consumer<Entity> function) {
|
||||
try {
|
||||
return (Entity) Reflections.method$World$spawnEntity.invoke(world, loc, type, CreatureSpawnEvent.SpawnReason.CUSTOM, function);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException("Failed to spawn entity", e);
|
||||
public static Entity spawnEntity(World world, Location loc, EntityType type, Consumer<Entity> function) {
|
||||
if (VersionHelper.isVersionNewerThan1_20_2()) {
|
||||
return world.spawnEntity(loc, type, CreatureSpawnEvent.SpawnReason.CUSTOM, function);
|
||||
} else {
|
||||
return LegacyEntityUtils.spawnEntity(world, loc, type, function);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,9 @@ public class NoteBlockChainUpdateUtils {
|
||||
|
||||
private NoteBlockChainUpdateUtils() {}
|
||||
|
||||
public static void noteBlockChainUpdate(Object level, Object chunkSource, Object direction, Object blockPos, int times) throws ReflectiveOperationException {
|
||||
public static void noteBlockChainUpdate(Object level, Object chunkSource, Object direction, Object blockPos, int times) {
|
||||
if (times >= Config.maxChainUpdate()) return;
|
||||
Object relativePos = Reflections.method$BlockPos$relative.invoke(blockPos, direction);
|
||||
Object relativePos = FastNMS.INSTANCE.method$BlockPos$relative(blockPos, direction);
|
||||
Object state = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, relativePos);
|
||||
if (BlockStateUtils.isClientSideNoteBlock(state)) {
|
||||
FastNMS.INSTANCE.method$ServerChunkCache$blockChanged(chunkSource, relativePos);
|
||||
|
||||
@@ -14,17 +14,12 @@ import net.kyori.adventure.text.Component;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.core.util.ReflectionUtils;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.event.block.BlockPhysicsEvent;
|
||||
import org.bukkit.event.block.BlockPlaceEvent;
|
||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||
import org.bukkit.inventory.*;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import sun.misc.Unsafe;
|
||||
@@ -1527,7 +1522,6 @@ public class Reflections {
|
||||
public static final Object instance$Direction$WEST;
|
||||
public static final Object instance$Direction$EAST;
|
||||
public static final Object[] instance$Directions;
|
||||
private static final Map<Object, Object> oppositeDirections = new HashMap<>();
|
||||
|
||||
static {
|
||||
try {
|
||||
@@ -1538,20 +1532,14 @@ public class Reflections {
|
||||
instance$Direction$SOUTH = instance$Directions[3];
|
||||
instance$Direction$WEST = instance$Directions[4];
|
||||
instance$Direction$EAST = instance$Directions[5];
|
||||
oppositeDirections.put(instance$Direction$DOWN, instance$Direction$UP);
|
||||
oppositeDirections.put(instance$Direction$UP, instance$Direction$DOWN);
|
||||
oppositeDirections.put(instance$Direction$NORTH, instance$Direction$SOUTH);
|
||||
oppositeDirections.put(instance$Direction$SOUTH, instance$Direction$NORTH);
|
||||
oppositeDirections.put(instance$Direction$WEST, instance$Direction$EAST);
|
||||
oppositeDirections.put(instance$Direction$EAST, instance$Direction$WEST);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static Object getOppositeDirection(Object direction) {
|
||||
return oppositeDirections.get(direction);
|
||||
}
|
||||
// public static Object getOppositeDirection(Object direction) {
|
||||
// return oppositeDirections.get(direction);
|
||||
// }
|
||||
|
||||
public static final Class<?> clazz$CraftBlock = requireNonNull(
|
||||
ReflectionUtils.getClazz(
|
||||
@@ -2418,6 +2406,12 @@ public class Reflections {
|
||||
)
|
||||
);
|
||||
|
||||
public static final Field field$BlockStateBase$requiresCorrectToolForDrops = requireNonNull(
|
||||
ReflectionUtils.getDeclaredField(
|
||||
clazz$BlockStateBase, boolean.class, 5
|
||||
)
|
||||
);
|
||||
|
||||
public static final Field field$BlockStateBase$canOcclude = requireNonNull(
|
||||
ReflectionUtils.getDeclaredField(
|
||||
clazz$BlockStateBase, boolean.class, 6
|
||||
@@ -2925,11 +2919,11 @@ public class Reflections {
|
||||
)
|
||||
);
|
||||
|
||||
public static final Field field$ServerPlayer$gameMode = requireNonNull(
|
||||
ReflectionUtils.getDeclaredField(
|
||||
clazz$ServerPlayer, clazz$ServerPlayerGameMode, 0
|
||||
)
|
||||
);
|
||||
// public static final Field field$ServerPlayer$gameMode = requireNonNull(
|
||||
// ReflectionUtils.getDeclaredField(
|
||||
// clazz$ServerPlayer, clazz$ServerPlayerGameMode, 0
|
||||
// )
|
||||
// );
|
||||
|
||||
public static final Field field$ServerPlayerGameMode$destroyProgressStart = requireNonNull(
|
||||
ReflectionUtils.getDeclaredField(
|
||||
@@ -2937,11 +2931,11 @@ public class Reflections {
|
||||
)
|
||||
);
|
||||
|
||||
public static final Field field$ServerPlayerGameMode$gameTicks = requireNonNull(
|
||||
ReflectionUtils.getDeclaredField(
|
||||
clazz$ServerPlayerGameMode, int.class, 1
|
||||
)
|
||||
);
|
||||
// public static final Field field$ServerPlayerGameMode$gameTicks = requireNonNull(
|
||||
// ReflectionUtils.getDeclaredField(
|
||||
// clazz$ServerPlayerGameMode, int.class, 1
|
||||
// )
|
||||
// );
|
||||
|
||||
public static final Field field$ServerPlayerGameMode$delayedTickStart = requireNonNull(
|
||||
ReflectionUtils.getDeclaredField(
|
||||
@@ -3009,11 +3003,11 @@ public class Reflections {
|
||||
)
|
||||
);
|
||||
|
||||
public static final Method method$BlockStateBase$getDestroyProgress = requireNonNull(
|
||||
ReflectionUtils.getDeclaredMethod(
|
||||
clazz$BlockStateBase, float.class, clazz$Player, clazz$BlockGetter, clazz$BlockPos
|
||||
)
|
||||
);
|
||||
// public static final Method method$BlockStateBase$getDestroyProgress = requireNonNull(
|
||||
// ReflectionUtils.getDeclaredMethod(
|
||||
// clazz$BlockStateBase, float.class, clazz$Player, clazz$BlockGetter, clazz$BlockPos
|
||||
// )
|
||||
// );
|
||||
|
||||
public static final Class<?> clazz$ClientboundBlockDestructionPacket = requireNonNull(
|
||||
ReflectionUtils.getClazz(
|
||||
@@ -3022,11 +3016,11 @@ public class Reflections {
|
||||
)
|
||||
);
|
||||
|
||||
public static final Constructor<?> constructor$ClientboundBlockDestructionPacket = requireNonNull(
|
||||
ReflectionUtils.getConstructor(
|
||||
clazz$ClientboundBlockDestructionPacket, int.class, clazz$BlockPos, int.class
|
||||
)
|
||||
);
|
||||
// public static final Constructor<?> constructor$ClientboundBlockDestructionPacket = requireNonNull(
|
||||
// ReflectionUtils.getConstructor(
|
||||
// clazz$ClientboundBlockDestructionPacket, int.class, clazz$BlockPos, int.class
|
||||
// )
|
||||
// );
|
||||
|
||||
public static final Class<?> clazz$ServerboundSwingPacket = requireNonNull(
|
||||
ReflectionUtils.getClazz(
|
||||
@@ -3994,12 +3988,12 @@ public class Reflections {
|
||||
)
|
||||
);
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static final Method method$World$spawnEntity = requireNonNull(
|
||||
VersionHelper.isVersionNewerThan1_20_2() ?
|
||||
ReflectionUtils.getMethod(World.class, Entity.class, Location.class, EntityType.class, CreatureSpawnEvent.SpawnReason.class, Consumer.class) :
|
||||
ReflectionUtils.getMethod(World.class, Entity.class, Location.class, EntityType.class, CreatureSpawnEvent.SpawnReason.class, org.bukkit.util.Consumer.class)
|
||||
);
|
||||
// @SuppressWarnings("deprecation")
|
||||
// public static final Method method$World$spawnEntity = requireNonNull(
|
||||
// VersionHelper.isVersionNewerThan1_20_2() ?
|
||||
// ReflectionUtils.getMethod(World.class, Entity.class, Location.class, EntityType.class, CreatureSpawnEvent.SpawnReason.class, Consumer.class) :
|
||||
// ReflectionUtils.getMethod(World.class, Entity.class, Location.class, EntityType.class, CreatureSpawnEvent.SpawnReason.class, org.bukkit.util.Consumer.class)
|
||||
// );
|
||||
|
||||
// 1.21.4+
|
||||
public static final Class<?> clazz$ServerboundPickItemFromEntityPacket =
|
||||
@@ -4955,11 +4949,11 @@ public class Reflections {
|
||||
)
|
||||
);
|
||||
|
||||
public static final Constructor<?> constructor$ClientboundLevelEventPacket = requireNonNull(
|
||||
ReflectionUtils.getConstructor(
|
||||
clazz$ClientboundLevelEventPacket, int.class, clazz$BlockPos, int.class, boolean.class
|
||||
)
|
||||
);
|
||||
// public static final Constructor<?> constructor$ClientboundLevelEventPacket = requireNonNull(
|
||||
// ReflectionUtils.getConstructor(
|
||||
// clazz$ClientboundLevelEventPacket, int.class, clazz$BlockPos, int.class, boolean.class
|
||||
// )
|
||||
// );
|
||||
|
||||
public static final Field field$ClientboundLevelEventPacket$eventId = requireNonNull(
|
||||
ReflectionUtils.getDeclaredField(
|
||||
@@ -5768,24 +5762,30 @@ public class Reflections {
|
||||
)
|
||||
);
|
||||
|
||||
public static final Constructor<?> constructor$BlockInWorld = requireNonNull(
|
||||
ReflectionUtils.getConstructor(
|
||||
clazz$BlockInWorld, 0
|
||||
public static final Field field$BlockInWorld$state = requireNonNull(
|
||||
ReflectionUtils.getDeclaredField(
|
||||
clazz$BlockInWorld, clazz$BlockState, 0
|
||||
)
|
||||
);
|
||||
|
||||
// 1.20.5+
|
||||
public static final Method method$ItemStack$canBreakBlockInAdventureMode =
|
||||
ReflectionUtils.getMethod(
|
||||
clazz$ItemStack, new String[]{"canBreakBlockInAdventureMode"}, clazz$BlockInWorld
|
||||
);
|
||||
// public static final Constructor<?> constructor$BlockInWorld = requireNonNull(
|
||||
// ReflectionUtils.getConstructor(
|
||||
// clazz$BlockInWorld, 0
|
||||
// )
|
||||
// );
|
||||
|
||||
// 1.20 ~ 1.20.4
|
||||
// instance$BuiltInRegistries$BLOCK
|
||||
public static final Method method$ItemStack$canDestroy =
|
||||
ReflectionUtils.getMethod(
|
||||
clazz$ItemStack,new String[]{"b"}, clazz$Registry, clazz$BlockInWorld
|
||||
);
|
||||
// // 1.20.5+
|
||||
// public static final Method method$ItemStack$canBreakBlockInAdventureMode =
|
||||
// ReflectionUtils.getMethod(
|
||||
// clazz$ItemStack, new String[]{"canBreakBlockInAdventureMode"}, clazz$BlockInWorld
|
||||
// );
|
||||
|
||||
// // 1.20 ~ 1.20.4
|
||||
// // instance$BuiltInRegistries$BLOCK
|
||||
// public static final Method method$ItemStack$canDestroy =
|
||||
// ReflectionUtils.getMethod(
|
||||
// clazz$ItemStack,new String[]{"b"}, clazz$Registry, clazz$BlockInWorld
|
||||
// );
|
||||
|
||||
public static final Method method$BlockStateBase$getBlock = requireNonNull(
|
||||
ReflectionUtils.getMethod(
|
||||
@@ -6432,4 +6432,84 @@ public class Reflections {
|
||||
BukkitReflectionUtils.assembleMCClass("network.protocol.game.PacketPlayOutScoreboardScore")
|
||||
)
|
||||
);
|
||||
|
||||
public static final Method method$CraftPlayer$setSimplifyContainerDesyncCheck =
|
||||
ReflectionUtils.getMethod(
|
||||
clazz$CraftPlayer, new String[]{"setSimplifyContainerDesyncCheck"}, boolean.class
|
||||
);
|
||||
|
||||
public static final Class<?> clazz$ServerboundHelloPacket = requireNonNull(
|
||||
ReflectionUtils.getClazz(
|
||||
BukkitReflectionUtils.assembleMCClass("network.protocol.login.ServerboundHelloPacket"),
|
||||
BukkitReflectionUtils.assembleMCClass("network.protocol.login.PacketLoginInStart")
|
||||
)
|
||||
);
|
||||
|
||||
public static final Field field$ServerboundHelloPacket$name = requireNonNull(
|
||||
ReflectionUtils.getDeclaredField(
|
||||
clazz$ServerboundHelloPacket, String.class, 0
|
||||
)
|
||||
);
|
||||
|
||||
public static final Field field$ServerboundHelloPacket$uuid = requireNonNull(
|
||||
VersionHelper.isVersionNewerThan1_20_2() ?
|
||||
ReflectionUtils.getDeclaredField(
|
||||
clazz$ServerboundHelloPacket, UUID.class, 0
|
||||
) :
|
||||
ReflectionUtils.getDeclaredField(
|
||||
clazz$ServerboundHelloPacket, Optional.class, 0
|
||||
)
|
||||
);
|
||||
|
||||
public static final Field field$ClientboundResourcePackPushPacket$id =
|
||||
ReflectionUtils.getDeclaredField(
|
||||
clazz$ClientboundResourcePackPushPacket, UUID.class, 0
|
||||
);
|
||||
|
||||
public static final Field field$ClientboundResourcePackPushPacket$prompt = requireNonNull(
|
||||
ReflectionUtils.getDeclaredField(
|
||||
clazz$ClientboundResourcePackPushPacket,
|
||||
VersionHelper.isVersionNewerThan1_20_5() ? Optional.class : clazz$Component,
|
||||
0
|
||||
)
|
||||
);
|
||||
|
||||
public static final Class<?> clazz$ServerboundResourcePackPacket = requireNonNull(
|
||||
ReflectionUtils.getClazz(
|
||||
BukkitReflectionUtils.assembleMCClass("network.protocol.common.ServerboundResourcePackPacket"),
|
||||
BukkitReflectionUtils.assembleMCClass("network.protocol.game.PacketPlayInResourcePackStatus")
|
||||
)
|
||||
);
|
||||
|
||||
public static final Class<?> clazz$ServerboundResourcePackPacket$Action = requireNonNull(
|
||||
ReflectionUtils.getClazz(
|
||||
BukkitReflectionUtils.assembleMCClass("network.protocol.common.ServerboundResourcePackPacket$Action"),
|
||||
BukkitReflectionUtils.assembleMCClass("network.protocol.common.ServerboundResourcePackPacket$a"),
|
||||
BukkitReflectionUtils.assembleMCClass("network.protocol.game.ServerboundResourcePackPacket$Action"),
|
||||
BukkitReflectionUtils.assembleMCClass("network.protocol.game.PacketPlayInResourcePackStatus$EnumResourcePackStatus")
|
||||
)
|
||||
);
|
||||
|
||||
public static final Method method$ServerboundResourcePackPacket$Action$values = requireNonNull(
|
||||
ReflectionUtils.getStaticMethod(
|
||||
clazz$ServerboundResourcePackPacket$Action, clazz$ServerboundResourcePackPacket$Action.arrayType()
|
||||
)
|
||||
);
|
||||
|
||||
public static final Object instance$ServerboundResourcePackPacket$Action$ACCEPTED;
|
||||
|
||||
static {
|
||||
try {
|
||||
Object[] values = (Object[]) method$ServerboundResourcePackPacket$Action$values.invoke(null);
|
||||
instance$ServerboundResourcePackPacket$Action$ACCEPTED = values[3];
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static final Constructor<?> constructor$ServerboundResourcePackPacket = requireNonNull(
|
||||
field$ClientboundResourcePackPushPacket$id != null
|
||||
? ReflectionUtils.getConstructor(clazz$ServerboundResourcePackPacket, UUID.class, clazz$ServerboundResourcePackPacket$Action)
|
||||
: ReflectionUtils.getConstructor(clazz$ServerboundResourcePackPacket, clazz$ServerboundResourcePackPacket$Action)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package net.momirealms.craftengine.bukkit.util;
|
||||
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class ResourcePackUtils {
|
||||
|
||||
public static Object createPacket(UUID uuid, String url, String hash) {
|
||||
return FastNMS.INSTANCE.constructor$ClientboundResourcePackPushPacket(uuid, url, hash, Config.kickOnDeclined(), ComponentUtils.adventureToMinecraft(Config.resourcePackPrompt()));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user