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

Merge pull request #78 from Xiao-MoMi/dev

0.0.42
This commit is contained in:
XiaoMoMi
2025-04-02 23:31:14 +08:00
committed by GitHub
42 changed files with 675 additions and 275 deletions

View File

@@ -135,7 +135,7 @@ repositories {
```
```kotlin
dependencies {
compileOnly("net.momirealms:craft-engine-core:0.0.38")
compileOnly("net.momirealms:craft-engine-bukkit:0.0.38")
compileOnly("net.momirealms:craft-engine-core:0.0.42")
compileOnly("net.momirealms:craft-engine-bukkit:0.0.42")
}
```

View File

@@ -193,6 +193,8 @@ blocks:
type: crop_block
grow-speed: 0.25
light-requirement: 9
is-bone-meal-target: true
bone-meal-age-bonus: 1
bottom-blocks:
- minecraft:end_stone
loot:

View File

@@ -5,6 +5,7 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
import net.momirealms.craftengine.core.block.CustomBlock;
@@ -107,21 +108,16 @@ public final class CraftEngineBlocks {
@NotNull UpdateOption option,
boolean playSound) {
boolean success;
try {
Object worldServer = Reflections.field$CraftWorld$ServerLevel.get(location.getWorld());
Object blockPos = FastNMS.INSTANCE.constructor$BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ());
Object blockState = block.customBlockState().handle();
Object oldBlockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(worldServer, blockPos);
success = (boolean) Reflections.method$LevelWriter$setBlock.invoke(worldServer, blockPos, blockState, option.flags());
if (success) {
Reflections.method$BlockStateBase$onPlace.invoke(blockState, worldServer, blockPos, oldBlockState, true);
if (playSound) {
location.getWorld().playSound(location, block.sounds().placeSound().toString(), SoundCategory.BLOCKS, block.sounds().placeSound().volume(), block.sounds().placeSound().pitch());
}
Object worldServer = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(location.getWorld());
Object blockPos = FastNMS.INSTANCE.constructor$BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ());
Object blockState = block.customBlockState().handle();
Object oldBlockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(worldServer, blockPos);
success = FastNMS.INSTANCE.method$LevelWriter$setBlock(worldServer, blockPos, blockState, option.flags());
if (success) {
FastNMS.INSTANCE.method$BlockStateBase$onPlace(blockState, worldServer, blockPos, oldBlockState, false);
if (playSound) {
location.getWorld().playSound(location, block.sounds().placeSound().toString(), SoundCategory.BLOCKS, block.sounds().placeSound().volume(), block.sounds().placeSound().pitch());
}
} catch (ReflectiveOperationException e) {
CraftEngine.instance().logger().warn("Failed to set nms block", e);
return false;
}
return success;
}
@@ -189,8 +185,7 @@ public final class CraftEngineBlocks {
world.playBlockSound(vec3d, state.sounds().breakSound());
}
if (sendParticles) {
// TODO Particles
//ParticleUtils.addBlockBreakParticles(block.getWorld(), LocationUtils.toBlockPos(location), state.customBlockState().handle());
FastNMS.INSTANCE.method$Level$levelEvent(world.serverWorld(), 2001, LocationUtils.toBlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()), state.customBlockState().registryId());
}
block.setType(Material.AIR, applyPhysics);
return true;

View File

@@ -139,7 +139,7 @@ public class CraftEngineFurniture {
*/
@Nullable
public static LoadedFurniture getLoadedFurnitureByBaseEntity(@NotNull Entity baseEntity) {
return BukkitFurnitureManager.instance().getLoadedFurnitureByBaseEntityId(baseEntity.getEntityId());
return BukkitFurnitureManager.instance().getLoadedFurnitureByRealEntityId(baseEntity.getEntityId());
}
/**
@@ -152,7 +152,7 @@ public class CraftEngineFurniture {
public static LoadedFurniture getLoadedFurnitureBySeat(@NotNull Entity seat) {
Integer baseEntityId = seat.getPersistentDataContainer().get(BukkitFurnitureManager.FURNITURE_SEAT_BASE_ENTITY_KEY, PersistentDataType.INTEGER);
if (baseEntityId == null) return null;
return BukkitFurnitureManager.instance().getLoadedFurnitureByBaseEntityId(baseEntityId);
return BukkitFurnitureManager.instance().getLoadedFurnitureByRealEntityId(baseEntityId);
}
/**
@@ -163,7 +163,7 @@ public class CraftEngineFurniture {
*/
public static boolean remove(@NotNull Entity furniture) {
if (!isFurniture(furniture)) return false;
LoadedFurniture loadedFurniture = BukkitFurnitureManager.instance().getLoadedFurnitureByBaseEntityId(furniture.getEntityId());
LoadedFurniture loadedFurniture = BukkitFurnitureManager.instance().getLoadedFurnitureByRealEntityId(furniture.getEntityId());
if (loadedFurniture == null) return false;
loadedFurniture.destroy();
return true;
@@ -181,7 +181,7 @@ public class CraftEngineFurniture {
boolean dropLoot,
boolean playSound) {
if (!isFurniture(furniture)) return false;
LoadedFurniture loadedFurniture = BukkitFurnitureManager.instance().getLoadedFurnitureByBaseEntityId(furniture.getEntityId());
LoadedFurniture loadedFurniture = BukkitFurnitureManager.instance().getLoadedFurnitureByRealEntityId(furniture.getEntityId());
if (loadedFurniture == null) return false;
remove(loadedFurniture, (net.momirealms.craftengine.core.entity.player.Player) null, dropLoot, playSound);
return true;
@@ -201,7 +201,7 @@ public class CraftEngineFurniture {
boolean dropLoot,
boolean playSound) {
if (!isFurniture(furniture)) return false;
LoadedFurniture loadedFurniture = BukkitFurnitureManager.instance().getLoadedFurnitureByBaseEntityId(furniture.getEntityId());
LoadedFurniture loadedFurniture = BukkitFurnitureManager.instance().getLoadedFurnitureByRealEntityId(furniture.getEntityId());
if (loadedFurniture == null) return false;
remove(loadedFurniture, player, dropLoot, playSound);
return true;

View File

@@ -2,6 +2,7 @@ package net.momirealms.craftengine.bukkit.block;
import io.papermc.paper.event.block.BlockBreakBlockEvent;
import net.momirealms.craftengine.bukkit.api.event.CustomBlockBreakEvent;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
import net.momirealms.craftengine.bukkit.util.*;
@@ -183,34 +184,48 @@ public class BlockEventListener implements Listener {
}
}
// override vanilla block loots
// BlockBreakBlockEvent = liquid + piston
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
public void onBlockBreakBlock(BlockBreakBlockEvent event) {
Block block = event.getBlock();
Object blockState = BlockStateUtils.blockDataToBlockState(block.getBlockData());
int stateId = BlockStateUtils.blockStateToId(blockState);
if (!BlockStateUtils.isVanillaBlock(stateId)) {
return;
}
this.plugin.vanillaLootManager().getBlockLoot(stateId).ifPresent(it -> {
if (it.override()) {
event.getDrops().clear();
event.setExpToDrop(0);
}
Location location = block.getLocation();
Vec3d vec3d = new Vec3d(location.getBlockX() + 0.5, location.getBlockY() + 0.5, location.getBlockZ() + 0.5);
net.momirealms.craftengine.core.world.World world = new BukkitWorld(location.getWorld());
ContextHolder.Builder builder = ContextHolder.builder();
builder.withParameter(LootParameters.WORLD, world);
builder.withParameter(LootParameters.LOCATION, vec3d);
ContextHolder contextHolder = builder.build();
for (LootTable<?> lootTable : it.lootTables()) {
for (Item<?> item : lootTable.getRandomItems(contextHolder, world)) {
// custom blocks
ImmutableBlockState immutableBlockState = this.manager.getImmutableBlockStateUnsafe(stateId);
if (!immutableBlockState.isEmpty()) {
Location location = block.getLocation();
net.momirealms.craftengine.core.world.World world = new BukkitWorld(block.getWorld());
Vec3d vec3d = new Vec3d(location.getBlockX() + 0.5, location.getBlockY() + 0.5, location.getBlockZ() + 0.5);
ContextHolder.Builder builder = ContextHolder.builder();
builder.withParameter(LootParameters.WORLD, world);
builder.withParameter(LootParameters.LOCATION, vec3d);
for (Item<?> item : immutableBlockState.getDrops(builder, world)) {
world.dropItemNaturally(vec3d, item);
}
}
});
} else {
// override vanilla block loots
this.plugin.vanillaLootManager().getBlockLoot(stateId).ifPresent(it -> {
if (it.override()) {
event.getDrops().clear();
event.setExpToDrop(0);
}
Location location = block.getLocation();
Vec3d vec3d = new Vec3d(location.getBlockX() + 0.5, location.getBlockY() + 0.5, location.getBlockZ() + 0.5);
net.momirealms.craftengine.core.world.World world = new BukkitWorld(location.getWorld());
ContextHolder.Builder builder = ContextHolder.builder();
builder.withParameter(LootParameters.WORLD, world);
builder.withParameter(LootParameters.LOCATION, vec3d);
ContextHolder contextHolder = builder.build();
for (LootTable<?> lootTable : it.lootTables()) {
for (Item<?> item : lootTable.getRandomItems(contextHolder, world)) {
world.dropItemNaturally(vec3d, item);
}
}
});
}
}
@EventHandler(ignoreCancelled = true)
@@ -240,37 +255,38 @@ public class BlockEventListener implements Listener {
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
public void onPistonRetract(BlockPistonRetractEvent event) {
handlePistonEvent(event.getDirection(), event.getBlocks(), event.getBlock());
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
public void onPistonExtend(BlockPistonExtendEvent event) {
handlePistonEvent(event.getDirection(), event.getBlocks(), event.getBlock());
}
private void handlePistonEvent(BlockFace face, List<Block> blocksList, Block piston) {
int blocks = blocksList.size();
net.momirealms.craftengine.core.world.World world = new BukkitWorld(piston.getWorld());
for (int i = blocks - 1; i >= 0; --i) {
Location oldLocation = blocksList.get(i).getLocation();
BlockPos oldPos = new BlockPos(oldLocation.getBlockX(), oldLocation.getBlockY(), oldLocation.getBlockZ());
Block block = blocksList.get(i);
ImmutableBlockState blockState = manager.getImmutableBlockState(BlockStateUtils.blockDataToId(block.getBlockData()));
if (blockState != null && blockState.pushReaction() == PushReaction.DESTROY) {
// break actions
ContextHolder.Builder builder = ContextHolder.builder();
Vec3d vec3d = Vec3d.atCenterOf(oldPos);
builder.withParameter(LootParameters.LOCATION, vec3d);
builder.withParameter(LootParameters.WORLD, world);
for (Item<Object> item : blockState.getDrops(builder, world)) {
world.dropItemNaturally(vec3d, item);
}
world.playBlockSound(vec3d, blockState.sounds().breakSound());
}
}
}
// Use BlockBreakBlock event
// @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
// public void onPistonRetract(BlockPistonRetractEvent event) {
// handlePistonEvent(event.getDirection(), event.getBlocks(), event.getBlock());
// }
//
// @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
// public void onPistonExtend(BlockPistonExtendEvent event) {
// handlePistonEvent(event.getDirection(), event.getBlocks(), event.getBlock());
// }
//
// private void handlePistonEvent(BlockFace face, List<Block> blocksList, Block piston) {
// int blocks = blocksList.size();
// net.momirealms.craftengine.core.world.World world = new BukkitWorld(piston.getWorld());
// for (int i = blocks - 1; i >= 0; --i) {
// Location oldLocation = blocksList.get(i).getLocation();
// BlockPos oldPos = new BlockPos(oldLocation.getBlockX(), oldLocation.getBlockY(), oldLocation.getBlockZ());
// Block block = blocksList.get(i);
// ImmutableBlockState blockState = manager.getImmutableBlockState(BlockStateUtils.blockDataToId(block.getBlockData()));
// if (blockState != null && blockState.pushReaction() == PushReaction.DESTROY) {
// // break actions
// ContextHolder.Builder builder = ContextHolder.builder();
// Vec3d vec3d = Vec3d.atCenterOf(oldPos);
// builder.withParameter(LootParameters.LOCATION, vec3d);
// builder.withParameter(LootParameters.WORLD, world);
// for (Item<Object> item : blockState.getDrops(builder, world)) {
// world.dropItemNaturally(vec3d, item);
// }
// world.playBlockSound(vec3d, blockState.sounds().breakSound());
// }
// }
// }
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
public void onEntityExplode(EntityExplodeEvent event) {
@@ -316,10 +332,10 @@ public class BlockEventListener implements Listener {
Block sourceBlock = event.getSourceBlock();
BlockFace direction = sourceBlock.getFace(block);
if (direction == BlockFace.UP || direction == BlockFace.DOWN) {
Object serverLevel = Reflections.field$CraftWorld$ServerLevel.get(world);
Object chunkSource = Reflections.field$ServerLevel$chunkSource.get(serverLevel);
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());
Reflections.method$ServerChunkCache$blockChanged.invoke(chunkSource, blockPos);
FastNMS.INSTANCE.method$ServerChunkCache$blockChanged(chunkSource, blockPos);
if (direction == BlockFace.UP) {
NoteBlockChainUpdateUtils.noteBlockChainUpdate(serverLevel, chunkSource, Reflections.instance$Direction$UP, blockPos, 0);
} else {

View File

@@ -0,0 +1,121 @@
package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.MirrorUtils;
import net.momirealms.craftengine.bukkit.util.RotationUtils;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.behavior.AbstractBlockBehavior;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.util.Direction;
import net.momirealms.craftengine.core.util.HorizontalDirection;
import net.momirealms.craftengine.core.util.Mirror;
import net.momirealms.craftengine.core.util.Rotation;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.function.BiConsumer;
public class BukkitBlockBehavior extends AbstractBlockBehavior {
private static final Map<String, BiConsumer<BukkitBlockBehavior, Property<?>>> HARD_CODED_PROPERTY_DATA = new HashMap<>();
static {
HARD_CODED_PROPERTY_DATA.put("axis", (behavior, property) -> {
@SuppressWarnings("unchecked")
Property<Direction.Axis> axisProperty = (Property<Direction.Axis>) property;
behavior.rotateFunction = (thisBlock, blockState, rotation) -> {
Direction.Axis axis = blockState.get(axisProperty);
return switch (rotation) {
case COUNTERCLOCKWISE_90, CLOCKWISE_90 -> switch (axis) {
case X -> blockState.with(axisProperty, Direction.Axis.Z).customBlockState().handle();
case Z -> blockState.with(axisProperty, Direction.Axis.X).customBlockState().handle();
default -> blockState.customBlockState().handle();
};
default -> blockState.customBlockState().handle();
};
};
});
HARD_CODED_PROPERTY_DATA.put("facing", (behavior, property) -> {
if (property.valueClass() == HorizontalDirection.class) {
@SuppressWarnings("unchecked")
Property<HorizontalDirection> directionProperty = (Property<HorizontalDirection>) property;
behavior.rotateFunction = (thisBlock, blockState, rotation) ->
blockState.with(directionProperty, rotation.rotate(blockState.get(directionProperty).toDirection()).toHorizontalDirection())
.customBlockState().handle();
behavior.mirrorFunction = (thisBlock, blockState, mirror) -> {
Rotation rotation = mirror.getRotation(blockState.get(directionProperty).toDirection());
return behavior.rotateFunction.rotate(thisBlock, blockState, rotation);
};
} else if (property.valueClass() == Direction.class) {
@SuppressWarnings("unchecked")
Property<Direction> directionProperty = (Property<Direction>) property;
behavior.rotateFunction = (thisBlock, blockState, rotation) ->
blockState.with(directionProperty, rotation.rotate(blockState.get(directionProperty)))
.customBlockState().handle();
behavior.mirrorFunction = (thisBlock, blockState, mirror) -> {
Rotation rotation = mirror.getRotation(blockState.get(directionProperty));
return behavior.rotateFunction.rotate(thisBlock, blockState, rotation);
};
}
});
HARD_CODED_PROPERTY_DATA.put("facing_clockwise", (behavior, property) -> {
if (property.valueClass() == HorizontalDirection.class) {
@SuppressWarnings("unchecked")
Property<HorizontalDirection> directionProperty = (Property<HorizontalDirection>) property;
behavior.rotateFunction = (thisBlock, blockState, rotation) ->
blockState.with(directionProperty, rotation.rotate(blockState.get(directionProperty).toDirection()).toHorizontalDirection())
.customBlockState().handle();
behavior.mirrorFunction = (thisBlock, blockState, mirror) -> {
Rotation rotation = mirror.getRotation(blockState.get(directionProperty).toDirection());
return behavior.rotateFunction.rotate(thisBlock, blockState, rotation);
};
}
});
}
private MirrorFunction mirrorFunction;
private RotateFunction rotateFunction;
public BukkitBlockBehavior(CustomBlock customBlock) {
super(customBlock);
for (Property<?> property : customBlock.properties()) {
Optional.ofNullable(HARD_CODED_PROPERTY_DATA.get(property.name())).ifPresent(
c -> c.accept(this, property)
);
}
}
@Override
public Object mirror(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
if (this.mirrorFunction != null) {
int id = BlockStateUtils.blockStateToId(args[0]);
ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockState(id);
return this.mirrorFunction.mirror(thisBlock, state, MirrorUtils.fromNMSMirror(args[1]));
}
return super.mirror(thisBlock, args, superMethod);
}
@Override
public Object rotate(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
if (this.rotateFunction != null) {
int id = BlockStateUtils.blockStateToId(args[0]);
ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockState(id);
return this.rotateFunction.rotate(thisBlock, state, RotationUtils.fromNMSRotation(args[1]));
}
return super.rotate(thisBlock, args, superMethod);
}
@FunctionalInterface
interface MirrorFunction {
Object mirror(Object thisBlock, ImmutableBlockState state, Mirror mirror) throws Exception;
}
@FunctionalInterface
interface RotateFunction {
Object rotate(Object thisBlock, ImmutableBlockState state, Rotation rotation) throws Exception;
}
}

View File

@@ -9,7 +9,6 @@ import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.behavior.AbstractBlockBehavior;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.loot.parameter.LootParameters;
@@ -29,14 +28,15 @@ import org.bukkit.Registry;
import java.util.*;
import java.util.concurrent.Callable;
public class BushBlockBehavior extends AbstractBlockBehavior {
public class BushBlockBehavior extends BukkitBlockBehavior {
public static final Factory FACTORY = new Factory();
protected final List<Object> tagsCanSurviveOn;
protected final Set<Object> blocksCansSurviveOn;
protected final Set<String> customBlocksCansSurviveOn;
protected final boolean any;
public BushBlockBehavior(List<Object> tagsCanSurviveOn, Set<Object> blocksCansSurviveOn, Set<String> customBlocksCansSurviveOn) {
public BushBlockBehavior(CustomBlock block, List<Object> tagsCanSurviveOn, Set<Object> blocksCansSurviveOn, Set<String> customBlocksCansSurviveOn) {
super(block);
this.tagsCanSurviveOn = tagsCanSurviveOn;
this.blocksCansSurviveOn = blocksCansSurviveOn;
this.customBlocksCansSurviveOn = customBlocksCansSurviveOn;
@@ -63,7 +63,8 @@ public class BushBlockBehavior extends AbstractBlockBehavior {
blockPos = args[4];
}
if (!canSurvive(thisBlock, state, level, blockPos)) {
ImmutableBlockState previousState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(state));
int stateId = BlockStateUtils.blockStateToId(state);
ImmutableBlockState previousState = BukkitBlockManager.instance().getImmutableBlockState(stateId);
if (previousState != null && !previousState.isEmpty()) {
ContextHolder.Builder builder = ContextHolder.builder();
BlockPos pos = LocationUtils.fromBlockPos(blockPos);
@@ -74,6 +75,8 @@ public class BushBlockBehavior extends AbstractBlockBehavior {
for (Item<Object> item : previousState.getDrops(builder, world)) {
world.dropItemNaturally(vec3d, item);
}
world.playBlockSound(vec3d, previousState.sounds().breakSound());
FastNMS.INSTANCE.method$Level$levelEvent(level, 2001, blockPos, stateId);
}
return Reflections.method$Block$defaultBlockState.invoke(Reflections.instance$Blocks$AIR);
}
@@ -93,7 +96,7 @@ public class BushBlockBehavior extends AbstractBlockBehavior {
@Override
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
Tuple<List<Object>, Set<Object>, Set<String>> tuple = readTagsAndState(arguments);
return new BushBlockBehavior(tuple.left(), tuple.mid(), tuple.right());
return new BushBlockBehavior(block, tuple.left(), tuple.mid(), tuple.right());
}
}

View File

@@ -32,9 +32,9 @@ public class ConcretePowderBlockBehavior extends FallingBlockBehavior {
private Object defaultBlockState;
private ImmutableBlockState defaultImmutableBlockState;
public ConcretePowderBlockBehavior(float hurtAmount, int maxHurt, Key block) {
super(hurtAmount, maxHurt);
this.targetBlock = block;
public ConcretePowderBlockBehavior(CustomBlock block, float hurtAmount, int maxHurt, Key targetBlock) {
super(block, hurtAmount, maxHurt);
this.targetBlock = targetBlock;
}
public ImmutableBlockState defaultImmutableBlockState() {
@@ -161,7 +161,7 @@ public class ConcretePowderBlockBehavior extends FallingBlockBehavior {
if (solidBlock == null) {
throw new IllegalArgumentException("No `solid-block` specified for concrete powder block behavior");
}
return new ConcretePowderBlockBehavior(hurtAmount, hurtMax, Key.of(solidBlock));
return new ConcretePowderBlockBehavior(block, hurtAmount, hurtMax, Key.of(solidBlock));
}
}
}

View File

@@ -5,36 +5,51 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.ParticleUtils;
import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.UpdateOption;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.block.properties.IntegerProperty;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.loot.LootContext;
import net.momirealms.craftengine.core.loot.parameter.LootParameters;
import net.momirealms.craftengine.core.util.MiscUtils;
import net.momirealms.craftengine.core.util.RandomUtils;
import net.momirealms.craftengine.core.util.Tuple;
import net.momirealms.craftengine.core.loot.number.NumberProvider;
import net.momirealms.craftengine.core.loot.number.NumberProviders;
import net.momirealms.craftengine.core.util.context.ContextHolder;
import net.momirealms.craftengine.core.util.context.ContextKey;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.world.Vec3i;
import net.momirealms.craftengine.shared.block.BlockBehavior;
import org.bukkit.World;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ThreadLocalRandom;
public class CropBlockBehavior extends BushBlockBehavior {
public static final Factory FACTORY = new Factory();
private final IntegerProperty ageProperty;
private final float growSpeed;
private final int minGrowLight;
private final boolean isBoneMealTarget;
private final NumberProvider boneMealBonus;
public CropBlockBehavior(List<Object> tagsCanSurviveOn, Set<Object> blocksCansSurviveOn, Set<String> customBlocksCansSurviveOn,
Property<Integer> ageProperty, float growSpeed, int minGrowLight) {
super(tagsCanSurviveOn, blocksCansSurviveOn, customBlocksCansSurviveOn);
public CropBlockBehavior(CustomBlock block, List<Object> tagsCanSurviveOn, Set<Object> blocksCansSurviveOn, Set<String> customBlocksCansSurviveOn,
Property<Integer> ageProperty, float growSpeed, int minGrowLight, boolean isBoneMealTarget, NumberProvider boneMealBonus) {
super(block, tagsCanSurviveOn, blocksCansSurviveOn, customBlocksCansSurviveOn);
this.ageProperty = (IntegerProperty) ageProperty;
this.growSpeed = growSpeed;
this.minGrowLight = minGrowLight;
this.isBoneMealTarget = isBoneMealTarget;
this.boneMealBonus = boneMealBonus;
}
public final int getAge(ImmutableBlockState state) {
@@ -45,6 +60,22 @@ public class CropBlockBehavior extends BushBlockBehavior {
return state.get(ageProperty) == ageProperty.max;
}
public float growSpeed() {
return growSpeed;
}
public boolean isBoneMealTarget() {
return isBoneMealTarget;
}
public NumberProvider boneMealBonus() {
return boneMealBonus;
}
public int minGrowLight() {
return minGrowLight;
}
private static int getRawBrightness(Object level, Object pos) throws InvocationTargetException, IllegalAccessException {
return (int) Reflections.method$BlockAndTintGetter$getRawBrightness.invoke(level, pos, 0);
}
@@ -63,7 +94,7 @@ public class CropBlockBehavior extends BushBlockBehavior {
if (currentState != null && !currentState.isEmpty()) {
int age = this.getAge(currentState);
if (age < this.ageProperty.max && RandomUtils.generateRandomFloat(0, 1) < this.growSpeed) {
Reflections.method$Level$setBlock.invoke(level, pos, currentState.with(this.ageProperty, age + 1).customBlockState().handle(), UpdateOption.UPDATE_ALL.flags());
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, pos, currentState.with(this.ageProperty, age + 1).customBlockState().handle(), UpdateOption.UPDATE_ALL.flags());
}
}
}
@@ -81,6 +112,7 @@ public class CropBlockBehavior extends BushBlockBehavior {
@Override
public boolean isValidBoneMealTarget(Object thisBlock, Object[] args) {
if (!this.isBoneMealTarget) return false;
Object state = args[2];
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(state));
if (immutableBlockState != null && !immutableBlockState.isEmpty()) {
@@ -112,17 +144,22 @@ public class CropBlockBehavior extends BushBlockBehavior {
sendParticles = true;
}
int i = this.getAge(immutableBlockState) + RandomUtils.generateRandomInt(2, 5);
World world = FastNMS.INSTANCE.method$Level$getCraftWorld(level);
int x = FastNMS.INSTANCE.field$Vec3i$x(pos);
int y = FastNMS.INSTANCE.field$Vec3i$y(pos);
int z = FastNMS.INSTANCE.field$Vec3i$z(pos);
net.momirealms.craftengine.core.world.World wrappedWorld = new BukkitWorld(world);
int i = this.getAge(immutableBlockState) + this.boneMealBonus.getInt(new LootContext(wrappedWorld, ContextHolder.builder()
.withParameter(LootParameters.WORLD, wrappedWorld)
.withParameter(LootParameters.LOCATION, Vec3d.atCenterOf(new Vec3i(x, y, z)))
.build(), ThreadLocalRandom.current(), 1));
int maxAge = this.ageProperty.max;
if (i > maxAge) {
i = maxAge;
}
Reflections.method$Level$setBlock.invoke(level, pos, immutableBlockState.with(this.ageProperty, i).customBlockState().handle(), UpdateOption.UPDATE_NONE.flags());
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, pos, immutableBlockState.with(this.ageProperty, i).customBlockState().handle(), UpdateOption.UPDATE_NONE.flags());
if (sendParticles) {
World world = FastNMS.INSTANCE.method$Level$getCraftWorld(level);
int x = FastNMS.INSTANCE.field$Vec3i$x(pos);
int y = FastNMS.INSTANCE.field$Vec3i$y(pos);
int z = FastNMS.INSTANCE.field$Vec3i$z(pos);
world.spawnParticle(ParticleUtils.getParticle("HAPPY_VILLAGER"), x + 0.5, y + 0.5, z + 0.5, 12, 0.25, 0.25, 0.25);
}
}
@@ -137,10 +174,11 @@ public class CropBlockBehavior extends BushBlockBehavior {
if (ageProperty == null) {
throw new IllegalArgumentException("age property not set for crop");
}
// 存活条件是最小生长亮度-1
int minGrowLight = MiscUtils.getAsInt(arguments.getOrDefault("light-requirement", 9));
float growSpeed = MiscUtils.getAsFloat(arguments.getOrDefault("grow-speed", 0.25f));
return new CropBlockBehavior(tuple.left(), tuple.mid(), tuple.right(), ageProperty, growSpeed, minGrowLight);
float growSpeed = MiscUtils.getAsFloat(arguments.getOrDefault("grow-speed", 0.125f));
boolean isBoneMealTarget = (boolean) arguments.getOrDefault("is-bone-meal-target", true);
NumberProvider boneMealAgeBonus = NumberProviders.fromObject(arguments.getOrDefault("bone-meal-age-bonus", 1));
return new CropBlockBehavior(block, tuple.left(), tuple.mid(), tuple.right(), ageProperty, growSpeed, minGrowLight, isBoneMealTarget, boneMealAgeBonus);
}
}
}

View File

@@ -8,7 +8,6 @@ import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.behavior.AbstractBlockBehavior;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.loot.parameter.LootParameters;
@@ -21,12 +20,13 @@ import net.momirealms.craftengine.shared.block.BlockBehavior;
import java.util.Map;
import java.util.concurrent.Callable;
public class FallingBlockBehavior extends AbstractBlockBehavior {
public class FallingBlockBehavior extends BukkitBlockBehavior {
public static final Factory FACTORY = new Factory();
private final float hurtAmount;
private final int maxHurt;
public FallingBlockBehavior(float hurtAmount, int maxHurt) {
public FallingBlockBehavior(CustomBlock block, float hurtAmount, int maxHurt) {
super(block);
this.hurtAmount = hurtAmount;
this.maxHurt = maxHurt;
}
@@ -131,7 +131,7 @@ public class FallingBlockBehavior extends AbstractBlockBehavior {
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
float hurtAmount = MiscUtils.getAsFloat(arguments.getOrDefault("hurt-amount", -1f));
int hurtMax = MiscUtils.getAsInt(arguments.getOrDefault("max-hurt", -1));
return new FallingBlockBehavior(hurtAmount, hurtMax);
return new FallingBlockBehavior(block, hurtAmount, hurtMax);
}
}
}

View File

@@ -13,8 +13,8 @@ import java.util.Set;
public class HangingBlockBehavior extends BushBlockBehavior {
public static final Factory FACTORY = new Factory();
public HangingBlockBehavior(List<Object> tagsCanSurviveOn, Set<Object> blocksCansSurviveOn, Set<String> customBlocksCansSurviveOn) {
super(tagsCanSurviveOn, blocksCansSurviveOn, customBlocksCansSurviveOn);
public HangingBlockBehavior(CustomBlock block, List<Object> tagsCanSurviveOn, Set<Object> blocksCansSurviveOn, Set<String> customBlocksCansSurviveOn) {
super(block, tagsCanSurviveOn, blocksCansSurviveOn, customBlocksCansSurviveOn);
}
@Override
@@ -32,7 +32,7 @@ public class HangingBlockBehavior extends BushBlockBehavior {
@Override
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
Tuple<List<Object>, Set<Object>, Set<String>> tuple = readTagsAndState(arguments);
return new HangingBlockBehavior(tuple.left(), tuple.mid(), tuple.right());
return new HangingBlockBehavior(block, tuple.left(), tuple.mid(), tuple.right());
}
}
}

View File

@@ -39,8 +39,8 @@ public class LeavesBlockBehavior extends WaterLoggedBlockBehavior {
@Nullable
private final Property<Boolean> waterloggedProperty;
public LeavesBlockBehavior(int maxDistance, Property<Integer> distanceProperty, Property<Boolean> persistentProperty, @Nullable Property<Boolean> waterloggedProperty) {
super(waterloggedProperty);
public LeavesBlockBehavior(CustomBlock block, int maxDistance, Property<Integer> distanceProperty, Property<Boolean> persistentProperty, @Nullable Property<Boolean> waterloggedProperty) {
super(block, waterloggedProperty);
this.maxDistance = maxDistance;
this.distanceProperty = distanceProperty;
this.persistentProperty = persistentProperty;
@@ -97,7 +97,7 @@ public class LeavesBlockBehavior extends WaterLoggedBlockBehavior {
if (blockState == newState.customBlockState().handle()) {
Reflections.method$BlockStateBase$updateNeighbourShapes.invoke(blockState, level, blockPos, UpdateOption.UPDATE_ALL.flags(), 512);
} else {
Reflections.method$Level$setBlock.invoke(level, blockPos, newState.customBlockState().handle(), UpdateOption.UPDATE_ALL.flags());
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, newState.customBlockState().handle(), UpdateOption.UPDATE_ALL.flags());
}
}
}
@@ -182,7 +182,7 @@ public class LeavesBlockBehavior extends WaterLoggedBlockBehavior {
}
Property<Boolean> waterlogged = (Property<Boolean>) block.getProperty("waterlogged");
int actual = distance.possibleValues().get(distance.possibleValues().size() - 1);
return new LeavesBlockBehavior(actual, distance, persistent, waterlogged);
return new LeavesBlockBehavior(block, actual, distance, persistent, waterlogged);
}
}
}

View File

@@ -16,8 +16,8 @@ public class OnLiquidBlockBehavior extends BushBlockBehavior {
private final boolean onWater;
private final boolean onLava;
public OnLiquidBlockBehavior(boolean onWater, boolean onLava) {
super(List.of(), Set.of(), Set.of());
public OnLiquidBlockBehavior(CustomBlock block, boolean onWater, boolean onLava) {
super(block, List.of(), Set.of(), Set.of());
this.onWater = onWater;
this.onLava = onLava;
}
@@ -34,7 +34,7 @@ public class OnLiquidBlockBehavior extends BushBlockBehavior {
@Override
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
List<String> liquidTypes = MiscUtils.getAsStringList(arguments.getOrDefault("liquid-type", List.of("water")));
return new OnLiquidBlockBehavior(liquidTypes.contains("water"), liquidTypes.contains("lava"));
return new OnLiquidBlockBehavior(block, liquidTypes.contains("water"), liquidTypes.contains("lava"));
}
}

View File

@@ -31,8 +31,8 @@ public class SaplingBlockBehavior extends BushBlockBehavior {
private final double boneMealSuccessChance;
private final float growSpeed;
public SaplingBlockBehavior(Key feature, Property<Integer> stageProperty, List<Object> tagsCanSurviveOn, Set<Object> blocksCansSurviveOn, Set<String> customBlocksCansSurviveOn, double boneMealSuccessChance, float growSpeed) {
super(tagsCanSurviveOn, blocksCansSurviveOn, customBlocksCansSurviveOn);
public SaplingBlockBehavior(CustomBlock block, Key feature, Property<Integer> stageProperty, List<Object> tagsCanSurviveOn, Set<Object> blocksCansSurviveOn, Set<String> customBlocksCansSurviveOn, double boneMealSuccessChance, float growSpeed) {
super(block, tagsCanSurviveOn, blocksCansSurviveOn, customBlocksCansSurviveOn);
this.feature = feature;
this.stageProperty = stageProperty;
this.boneMealSuccessChance = boneMealSuccessChance;
@@ -79,18 +79,18 @@ public class SaplingBlockBehavior extends BushBlockBehavior {
CraftEngine.instance().logger().warn("Configured feature not found: " + treeFeature());
return;
}
Object chunkGenerator = Reflections.method$ServerChunkCache$getGenerator.invoke(Reflections.field$ServerLevel$chunkSource.get(world));
Object chunkGenerator = Reflections.method$ServerChunkCache$getGenerator.invoke(FastNMS.INSTANCE.method$ServerLevel$getChunkSource(world));
Object configuredFeature = Reflections.method$Holder$value.invoke(holder.get());
Object fluidState = Reflections.method$Level$getFluidState.invoke(world, blockPos);
Object legacyState = Reflections.method$FluidState$createLegacyBlock.invoke(fluidState);
Reflections.method$Level$setBlock.invoke(world, blockPos, legacyState, UpdateOption.UPDATE_NONE.flags());
FastNMS.INSTANCE.method$LevelWriter$setBlock(world, blockPos, legacyState, UpdateOption.UPDATE_NONE.flags());
if ((boolean) Reflections.method$ConfiguredFeature$place.invoke(configuredFeature, world, chunkGenerator, randomSource, blockPos)) {
if (FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, blockPos) == legacyState) {
Reflections.method$ServerLevel$sendBlockUpdated.invoke(world, blockPos, blockState, legacyState, 2);
}
} else {
// failed to place, rollback changes
Reflections.method$Level$setBlock.invoke(world, blockPos, blockState, UpdateOption.UPDATE_NONE.flags());
FastNMS.INSTANCE.method$LevelWriter$setBlock(world, blockPos, blockState, UpdateOption.UPDATE_NONE.flags());
}
}
@@ -150,7 +150,7 @@ public class SaplingBlockBehavior extends BushBlockBehavior {
}
double boneMealSuccessChance = MiscUtils.getAsDouble(arguments.getOrDefault("bone-meal-success-chance", 0.45));
Tuple<List<Object>, Set<Object>, Set<String>> tuple = readTagsAndState(arguments);
return new SaplingBlockBehavior(Key.of(feature), stageProperty, tuple.left(), tuple.mid(), tuple.right(), boneMealSuccessChance,
return new SaplingBlockBehavior(block, Key.of(feature), stageProperty, tuple.left(), tuple.mid(), tuple.right(), boneMealSuccessChance,
MiscUtils.getAsFloat(arguments.getOrDefault("grow-speed", 1.0 / 7.0)));
}
}

View File

@@ -1,18 +1,18 @@
package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.behavior.AbstractBlockBehavior;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.shared.block.BlockBehavior;
import java.util.Map;
public class StrippableBlockBehavior extends AbstractBlockBehavior {
public class StrippableBlockBehavior extends BukkitBlockBehavior {
public static final Factory FACTORY = new Factory();
private final Key stripped;
public StrippableBlockBehavior(Key stripped) {
public StrippableBlockBehavior(CustomBlock block, Key stripped) {
super(block);
this.stripped = stripped;
}
@@ -28,7 +28,7 @@ public class StrippableBlockBehavior extends AbstractBlockBehavior {
if (stripped == null) {
throw new IllegalArgumentException("stripped is null");
}
return new StrippableBlockBehavior(Key.of(stripped));
return new StrippableBlockBehavior(block, Key.of(stripped));
}
}
}

View File

@@ -38,17 +38,15 @@ public class SugarCaneBlockBehavior extends BushBlockBehavior {
private final boolean nearLava;
private final IntegerProperty ageProperty;
private final float growSpeed;
private final CustomBlock customBlock;
public SugarCaneBlockBehavior(CustomBlock customBlock, List<Object> tagsCanSurviveOn, Set<Object> blocksCansSurviveOn, Set<String> customBlocksCansSurviveOn, Property<Integer> ageProperty,
int maxHeight, boolean nearWater, boolean nearLava, float growSpeed) {
super(tagsCanSurviveOn, blocksCansSurviveOn, customBlocksCansSurviveOn);
super(customBlock, tagsCanSurviveOn, blocksCansSurviveOn, customBlocksCansSurviveOn);
this.nearWater = nearWater;
this.nearLava = nearLava;
this.maxHeight = maxHeight;
this.ageProperty = (IntegerProperty) ageProperty;
this.growSpeed = growSpeed;
this.customBlock = customBlock;
}
@Override
@@ -57,19 +55,21 @@ public class SugarCaneBlockBehavior extends BushBlockBehavior {
Object level = args[1];
Object blockPos = args[2];
if (!canSurvive(thisBlock, blockState, level, blockPos)) {
ImmutableBlockState currentState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState));
int stateId = BlockStateUtils.blockStateToId(blockState);
ImmutableBlockState currentState = BukkitBlockManager.instance().getImmutableBlockState(stateId);
if (currentState != null && !currentState.isEmpty()) {
// break the sugar cane
Reflections.method$Level$removeBlock.invoke(level, blockPos, false);
Vec3d vec3d = Vec3d.atCenterOf(LocationUtils.fromBlockPos(blockPos));
net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level));
// TODO client side particles?
ContextHolder.Builder builder = ContextHolder.builder()
.withParameter(LootParameters.LOCATION, vec3d)
.withParameter(LootParameters.WORLD, world);
for (Item<Object> item : currentState.getDrops(builder, world)) {
world.dropItemNaturally(vec3d, item);
}
world.playBlockSound(vec3d, currentState.sounds().breakSound());
FastNMS.INSTANCE.method$Level$levelEvent(level, 2001, blockPos, stateId);
}
}
}
@@ -120,13 +120,13 @@ public class SugarCaneBlockBehavior extends BushBlockBehavior {
if (age >= this.ageProperty.max || RandomUtils.generateRandomFloat(0, 1) < this.growSpeed) {
Object abovePos = LocationUtils.above(blockPos);
if (VersionHelper.isVersionNewerThan1_21_5()) {
Reflections.method$CraftEventFactory$handleBlockGrowEvent.invoke(null, level, abovePos, customBlock.defaultState().customBlockState().handle(), UpdateOption.UPDATE_ALL.flags());
Reflections.method$CraftEventFactory$handleBlockGrowEvent.invoke(null, level, abovePos, super.customBlock.defaultState().customBlockState().handle(), UpdateOption.UPDATE_ALL.flags());
} else {
Reflections.method$CraftEventFactory$handleBlockGrowEvent.invoke(null, level, abovePos, customBlock.defaultState().customBlockState().handle());
Reflections.method$CraftEventFactory$handleBlockGrowEvent.invoke(null, level, abovePos, super.customBlock.defaultState().customBlockState().handle());
}
Reflections.method$Level$setBlock.invoke(level, blockPos, currentState.with(this.ageProperty, this.ageProperty.min).customBlockState().handle(), UpdateOption.UPDATE_NONE.flags());
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, currentState.with(this.ageProperty, this.ageProperty.min).customBlockState().handle(), UpdateOption.UPDATE_NONE.flags());
} else if (RandomUtils.generateRandomFloat(0, 1) < this.growSpeed) {
Reflections.method$Level$setBlock.invoke(level, blockPos, currentState.with(this.ageProperty, age + 1).customBlockState().handle(), UpdateOption.UPDATE_NONE.flags());
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, currentState.with(this.ageProperty, age + 1).customBlockState().handle(), UpdateOption.UPDATE_NONE.flags());
}
}
}
@@ -143,7 +143,7 @@ public class SugarCaneBlockBehavior extends BushBlockBehavior {
// 如果下方是同种方块
if (!BlockStateUtils.isVanillaBlock(id)) {
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockStateUnsafe(id);
if (immutableBlockState.owner().value() == this.customBlock) {
if (immutableBlockState.owner().value() == super.customBlock) {
return true;
}
}

View File

@@ -1,7 +1,6 @@
package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.behavior.AbstractBlockBehavior;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.shared.block.BlockBehavior;
@@ -9,12 +8,13 @@ import org.jetbrains.annotations.Nullable;
import java.util.Map;
public class WaterLoggedBlockBehavior extends AbstractBlockBehavior {
public class WaterLoggedBlockBehavior extends BukkitBlockBehavior {
public static final Factory FACTORY = new Factory();
@Nullable
private final Property<Boolean> waterloggedProperty;
public WaterLoggedBlockBehavior(@Nullable Property<Boolean> waterloggedProperty) {
public WaterLoggedBlockBehavior(CustomBlock block, @Nullable Property<Boolean> waterloggedProperty) {
super(block);
this.waterloggedProperty = waterloggedProperty;
}
@@ -82,7 +82,7 @@ public class WaterLoggedBlockBehavior extends AbstractBlockBehavior {
@Override
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
Property<Boolean> waterlogged = (Property<Boolean>) block.getProperty("waterlogged");
return new WaterLoggedBlockBehavior(waterlogged);
return new WaterLoggedBlockBehavior(block, waterlogged);
}
}
}

View File

@@ -41,9 +41,8 @@ public class BukkitFurnitureManager implements FurnitureManager {
private final Map<Key, CustomFurniture> byId = new HashMap<>();
private final Map<Integer, LoadedFurniture> furnitureByBaseEntityId = new ConcurrentHashMap<>(256, 0.5f);
private final Map<Integer, LoadedFurniture> furnitureByRealEntityId = new ConcurrentHashMap<>(256, 0.5f);
private final Map<Integer, LoadedFurniture> furnitureByEntityId = new ConcurrentHashMap<>(512, 0.5f);
private final Map<Integer, LoadedFurniture> furnitureByCollisionEntitiesId = new ConcurrentHashMap<>(256, 0.5f);
// Event listeners
private final Listener dismountListener;
private final FurnitureEventListener furnitureEventListener;
@@ -78,7 +77,7 @@ public class BukkitFurnitureManager implements FurnitureManager {
SoundData data = furniture.settings().sounds().placeSound();
location.getWorld().playSound(location, data.id().toString(), SoundCategory.BLOCKS, data.volume(), data.pitch());
}
return getLoadedFurnitureByBaseEntityId(furnitureEntity.getEntityId());
return getLoadedFurnitureByRealEntityId(furnitureEntity.getEntityId());
}
@Override
@@ -258,13 +257,13 @@ public class BukkitFurnitureManager implements FurnitureManager {
}
@Override
public boolean isFurnitureBaseEntity(int entityId) {
return this.furnitureByBaseEntityId.containsKey(entityId);
public boolean isFurnitureRealEntity(int entityId) {
return this.furnitureByRealEntityId.containsKey(entityId);
}
@Nullable
public LoadedFurniture getLoadedFurnitureByBaseEntityId(int entityId) {
return this.furnitureByBaseEntityId.get(entityId);
public LoadedFurniture getLoadedFurnitureByRealEntityId(int entityId) {
return this.furnitureByRealEntityId.get(entityId);
}
@Nullable
@@ -272,19 +271,22 @@ public class BukkitFurnitureManager implements FurnitureManager {
return this.furnitureByEntityId.get(entityId);
}
@Nullable
public LoadedFurniture getLoadedFurnitureByCollisionEntityId(int entityId) {
return this.furnitureByCollisionEntitiesId.get(entityId);
}
protected void handleBaseFurnitureUnload(Entity entity) {
int id = entity.getEntityId();
LoadedFurniture furniture = this.furnitureByBaseEntityId.remove(id);
LoadedFurniture furniture = this.furnitureByRealEntityId.remove(id);
if (furniture != null) {
furniture.destroySeats();
Location location = entity.getLocation();
boolean isPreventing = FastNMS.INSTANCE.isPreventingStatusUpdates(location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4);
if (!isPreventing) {
furniture.destroySeats();
}
for (int sub : furniture.entityIds()) {
this.furnitureByEntityId.remove(sub);
}
for (CollisionEntity collision : furniture.collisionEntities()) {
this.furnitureByRealEntityId.remove(FastNMS.INSTANCE.method$Entity$getId(collision));
if (!isPreventing) collision.destroy();
}
}
}
@@ -297,7 +299,7 @@ public class BukkitFurnitureManager implements FurnitureManager {
Optional<CustomFurniture> optionalFurniture = getFurniture(key);
if (optionalFurniture.isEmpty()) return;
CustomFurniture customFurniture = optionalFurniture.get();
LoadedFurniture previous = this.furnitureByBaseEntityId.get(display.getEntityId());
LoadedFurniture previous = this.furnitureByRealEntityId.get(display.getEntityId());
if (previous != null) return;
Location location = entity.getLocation();
if (FastNMS.INSTANCE.isPreventingStatusUpdates(location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4)) {
@@ -319,7 +321,7 @@ public class BukkitFurnitureManager implements FurnitureManager {
Optional<CustomFurniture> optionalFurniture = getFurniture(key);
if (optionalFurniture.isPresent()) {
CustomFurniture customFurniture = optionalFurniture.get();
LoadedFurniture previous = this.furnitureByBaseEntityId.get(display.getEntityId());
LoadedFurniture previous = this.furnitureByRealEntityId.get(display.getEntityId());
if (previous != null) return;
addNewFurniture(display, customFurniture, getAnchorType(entity, customFurniture));
return;
@@ -351,13 +353,13 @@ public class BukkitFurnitureManager implements FurnitureManager {
private synchronized LoadedFurniture addNewFurniture(ItemDisplay display, CustomFurniture furniture, AnchorType anchorType) {
LoadedFurniture loadedFurniture = new LoadedFurniture(display, furniture, anchorType);
this.furnitureByBaseEntityId.put(loadedFurniture.baseEntityId(), loadedFurniture);
this.furnitureByRealEntityId.put(loadedFurniture.baseEntityId(), loadedFurniture);
for (int entityId : loadedFurniture.entityIds()) {
this.furnitureByEntityId.put(entityId, loadedFurniture);
}
for (CollisionEntity collisionEntity : loadedFurniture.collisionEntities()) {
int collisionEntityId = FastNMS.INSTANCE.method$Entity$getId(collisionEntity);
this.furnitureByCollisionEntitiesId.put(collisionEntityId, loadedFurniture);
this.furnitureByRealEntityId.put(collisionEntityId, loadedFurniture);
}
loadedFurniture.initializeColliders();
return loadedFurniture;
@@ -373,7 +375,7 @@ public class BukkitFurnitureManager implements FurnitureManager {
Integer baseFurniture = vehicle.getPersistentDataContainer().get(FURNITURE_SEAT_BASE_ENTITY_KEY, PersistentDataType.INTEGER);
if (baseFurniture == null) return;
vehicle.remove();
LoadedFurniture furniture = getLoadedFurnitureByBaseEntityId(baseFurniture);
LoadedFurniture furniture = getLoadedFurnitureByRealEntityId(baseFurniture);
if (furniture == null) {
return;
}

View File

@@ -112,9 +112,9 @@ public class LoadedFurniture {
});
}
try {
this.cachedSpawnPacket = Reflections.constructor$ClientboundBundlePacket.newInstance(packets);
this.cachedSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets);
if (this.minimized) {
this.cachedMinimizedSpawnPacket = Reflections.constructor$ClientboundBundlePacket.newInstance(minimizedPackets);
this.cachedMinimizedSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(minimizedPackets);
}
} catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to init spawn packets for furniture " + id, e);
@@ -126,6 +126,7 @@ public class LoadedFurniture {
if (colliderSize != 0) {
Object world = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(this.location.getWorld());
for (int i = 0; i < colliderSize; i++) {
// TODO better shulker hitbox
Collider collider = placement.colliders()[i];
Vector3f offset = conjugated.transform(new Vector3f(collider.position()));
Vector3d offset1 = collider.point1();

View File

@@ -44,7 +44,7 @@ public class ShulkerHitBox extends AbstractHitBox {
if (this.interactionEntity) {
// make it a litter bigger
InteractionEntityData.Height.addEntityDataIfNotDefaultValue(getPhysicalPeek(peek * 0.01F) * scale + 1.01f, cachedInteractionValues);
InteractionEntityData.Height.addEntityDataIfNotDefaultValue((getPhysicalPeek(peek * 0.01F) + 1) * scale + 0.01f, cachedInteractionValues);
InteractionEntityData.Width.addEntityDataIfNotDefaultValue(scale + 0.005f, cachedInteractionValues);
InteractionEntityData.Responsive.addEntityDataIfNotDefaultValue(interactive, cachedInteractionValues);
}

View File

@@ -150,7 +150,7 @@ public class BlockItemBehavior extends ItemBehavior {
Object blockState = state.customBlockState().handle();
Object blockPos = LocationUtils.toBlockPos(context.getClickedPos());
Object voxelShape = Reflections.method$CollisionContext$of.invoke(null, player);
Object world = Reflections.field$CraftWorld$ServerLevel.get(context.getLevel().platformWorld());
Object world = FastNMS.INSTANCE.field$CraftWorld$ServerLevel((World) context.getLevel().platformWorld());
boolean defaultReturn = ((!this.checkStatePlacement() || (boolean) Reflections.method$BlockStateBase$canSurvive.invoke(blockState, world, blockPos))
&& (boolean) Reflections.method$ServerLevel$checkEntityCollision.invoke(world, blockState, player, voxelShape, blockPos, true));
Block block = FastNMS.INSTANCE.method$CraftBlock$at(world, blockPos);

View File

@@ -3,6 +3,7 @@ package net.momirealms.craftengine.bukkit.item.recipe;
import com.destroystokyo.paper.event.inventory.PrepareResultEvent;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.injector.BukkitInjector;
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
@@ -347,7 +348,7 @@ public class RecipeEventListener implements Listener {
BukkitRecipeManager.minecraftRecipeManager(),
Reflections.instance$RecipeType$CAMPFIRE_COOKING,
Reflections.constructor$SingleRecipeInput.newInstance(Reflections.method$CraftItemStack$asNMSCopy.invoke(null, itemStack)),
Reflections.field$CraftWorld$ServerLevel.get(event.getPlayer().getWorld()),
FastNMS.INSTANCE.field$CraftWorld$ServerLevel(event.getPlayer().getWorld()),
null
);
if (optionalMCRecipe.isEmpty()) {

View File

@@ -1,9 +1,6 @@
package net.momirealms.craftengine.bukkit.plugin.command.feature;
import net.momirealms.craftengine.bukkit.nms.CollisionEntity;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
import org.bukkit.Location;
@@ -25,11 +22,7 @@ public class TestCommand extends BukkitCommandFeature<CommandSender> {
Player player = context.sender();
Location location = player.getLocation();
try {
Object level = Reflections.field$CraftWorld$ServerLevel.get(player.getWorld());
Object aabb = FastNMS.INSTANCE.constructor$AABB(location.getBlockX(), location.getBlockY(), location.getBlockZ(),
location.getBlockX() + 1, location.getBlockY() + 1, location.getBlockZ() + 1);
CollisionEntity nmsEntity = FastNMS.INSTANCE.createCollisionEntity(level, aabb, location.getBlockX() + 0.5, location.getBlockY(), location.getBlockZ() + 0.5, false);
FastNMS.INSTANCE.method$LevelWriter$addFreshEntity(level, nmsEntity);
} catch (Exception e) {
e.printStackTrace();
}

View File

@@ -174,6 +174,12 @@ public class BukkitInjector {
// getShape
.method(ElementMatchers.is(Reflections.method$BlockBehaviour$getShape))
.intercept(MethodDelegation.to(GetShapeInterceptor.INSTANCE))
// mirror
.method(ElementMatchers.is(Reflections.method$BlockBehaviour$mirror))
.intercept(MethodDelegation.to(MirrorInterceptor.INSTANCE))
// rotate
.method(ElementMatchers.is(Reflections.method$BlockBehaviour$rotate))
.intercept(MethodDelegation.to(RotateInterceptor.INSTANCE))
// tick
.method(ElementMatchers.is(Reflections.method$BlockBehaviour$tick))
.intercept(MethodDelegation.to(TickInterceptor.INSTANCE))
@@ -711,8 +717,8 @@ public class BukkitInjector {
int id = (int) Reflections.field$Direction$data3d.get(direction);
// Y axis
if (id == 0 || id == 1) {
Object chunkSource = Reflections.field$ServerLevel$chunkSource.get(serverLevel);
Reflections.method$ServerChunkCache$blockChanged.invoke(chunkSource, blockPos);
Object chunkSource = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(serverLevel);
FastNMS.INSTANCE.method$ServerChunkCache$blockChanged(chunkSource, blockPos);
if (id == 1) {
NoteBlockChainUpdateUtils.noteBlockChainUpdate(serverLevel, chunkSource, Reflections.instance$Direction$DOWN, blockPos, 0);
} else {
@@ -736,6 +742,36 @@ public class BukkitInjector {
}
}
public static class MirrorInterceptor {
public static final MirrorInterceptor INSTANCE = new MirrorInterceptor();
@RuntimeType
public Object intercept(@This Object thisObj, @AllArguments Object[] args, @SuperCall Callable<Object> superMethod) throws Exception {
ObjectHolder<BlockBehavior> holder = ((BehaviorHolder) thisObj).getBehaviorHolder();
try {
return holder.value().mirror(thisObj, args, superMethod);
} catch (Exception e) {
CraftEngine.instance().logger().severe("Failed to run mirror", e);
return superMethod.call();
}
}
}
public static class RotateInterceptor {
public static final RotateInterceptor INSTANCE = new RotateInterceptor();
@RuntimeType
public Object intercept(@This Object thisObj, @AllArguments Object[] args, @SuperCall Callable<Object> superMethod) throws Exception {
ObjectHolder<BlockBehavior> holder = ((BehaviorHolder) thisObj).getBehaviorHolder();
try {
return holder.value().rotate(thisObj, args, superMethod);
} catch (Exception e) {
CraftEngine.instance().logger().severe("Failed to run rotate", e);
return superMethod.call();
}
}
}
public static class RandomTickInterceptor {
public static final RandomTickInterceptor INSTANCE = new RandomTickInterceptor();

View File

@@ -92,31 +92,19 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
}
this.registerConsumers();
this.packetsConsumer = ((serverPlayer, packets) -> {
try {
Object bundle = Reflections.constructor$ClientboundBundlePacket.newInstance(packets);
Reflections.method$ServerGamePacketListenerImpl$sendPacket.invoke(
Reflections.field$ServerPlayer$connection.get(serverPlayer), bundle);
} catch (ReflectiveOperationException e) {
plugin.logger().warn("Failed to create bundle packet", e);
}
Object bundle = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets);
FastNMS.INSTANCE.sendPacket(serverPlayer, bundle);
});
this.delayedPacketConsumer = (serverPlayer, packet) -> {
try {
Reflections.method$ServerGamePacketListenerImpl$sendPacket.invoke(
Reflections.field$ServerPlayer$connection.get(serverPlayer), packet);
} catch (ReflectiveOperationException e) {
plugin.logger().warn("Failed to invoke send packet", e);
}
};
this.delayedPacketConsumer = FastNMS.INSTANCE::sendPacket;
this.immediatePacketConsumer = (serverPlayer, packet) -> {
try {
Reflections.method$Connection$sendPacketImmediate.invoke(Reflections.field$ServerCommonPacketListenerImpl$connection.get(Reflections.field$ServerPlayer$connection.get(serverPlayer)), packet, null, true);
Reflections.method$Connection$sendPacketImmediate.invoke(FastNMS.INSTANCE.field$Player$connection$connection(serverPlayer), packet, null, true);
} catch (ReflectiveOperationException e) {
plugin.logger().warn("Failed to invoke send packet", e);
}
};
this.active = true;
this.hasModelEngine = Bukkit.getPluginManager().getPlugin("ModelEngine") != null;
hasModelEngine = Bukkit.getPluginManager().getPlugin("ModelEngine") != null;
instance = this;
}
@@ -258,17 +246,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
}
public Channel getChannel(Player player) {
try {
return (Channel) Reflections.field$Channel.get(
Reflections.field$NetworkManager.get(
Reflections.field$ServerPlayer$connection.get(
FastNMS.INSTANCE.method$CraftPlayer$getHandle(player)
)
)
);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
return (Channel) FastNMS.INSTANCE.field$Player$connection$connection$channel(FastNMS.INSTANCE.method$CraftPlayer$getHandle(player));
}
public void sendPacket(@NotNull Player player, @NotNull Object packet) {

View File

@@ -70,7 +70,8 @@ public class PacketConsumers {
}
public static int remapMOD(int stateId) {
return mappingsMOD[stateId];
int modStateId = mappingsMOD[stateId];
return BlockStateUtils.isVanillaBlock(modStateId) ? remap(modStateId) : modStateId;
}
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> LEVEL_CHUNK_WITH_LIGHT = (user, event, packet) -> {
@@ -290,7 +291,7 @@ public class PacketConsumers {
private static void handlePlayerActionPacketOnMainThread(BukkitServerPlayer player, World world, BlockPos pos, Object packet) throws Exception {
Object action = Reflections.field$ServerboundPlayerActionPacket$action.get(packet);
if (action == Reflections.instance$ServerboundPlayerActionPacket$Action$START_DESTROY_BLOCK) {
Object serverLevel = Reflections.field$CraftWorld$ServerLevel.get(world);
Object serverLevel = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(world);
Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(serverLevel, LocationUtils.toBlockPos(pos));
int stateId = BlockStateUtils.blockStateToId(blockState);
// not a custom block
@@ -545,7 +546,7 @@ public class PacketConsumers {
};
private static void handlePickItemFromBlockPacketOnMainThread(Player player, Object pos) throws Exception {
Object serverLevel = Reflections.field$CraftWorld$ServerLevel.get(player.getWorld());
Object serverLevel = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(player.getWorld());
Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(serverLevel, pos);
ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState));
if (state == null) return;
@@ -617,7 +618,7 @@ public class PacketConsumers {
} else if (entityType == Reflections.instance$EntityType$ITEM_DISPLAY) {
// Furniture
int entityId = (int) Reflections.field$ClientboundAddEntityPacket$entityId.get(packet);
LoadedFurniture furniture = BukkitFurnitureManager.instance().getLoadedFurnitureByBaseEntityId(entityId);
LoadedFurniture furniture = BukkitFurnitureManager.instance().getLoadedFurnitureByRealEntityId(entityId);
if (furniture != null) {
user.furnitureView().computeIfAbsent(furniture.baseEntityId(), k -> new ArrayList<>()).addAll(furniture.fakeEntityIds());
user.sendPacket(furniture.spawnPacket((Player) user.platformPlayer()), false);
@@ -628,7 +629,7 @@ public class PacketConsumers {
} else if (entityType == Reflections.instance$EntityType$SHULKER) {
// Cancel collider entity packet
int entityId = (int) Reflections.field$ClientboundAddEntityPacket$entityId.get(packet);
LoadedFurniture furniture = BukkitFurnitureManager.instance().getLoadedFurnitureByCollisionEntityId(entityId);
LoadedFurniture furniture = BukkitFurnitureManager.instance().getLoadedFurnitureByRealEntityId(entityId);
if (furniture != null) {
event.setCancelled(true);
}
@@ -641,7 +642,7 @@ public class PacketConsumers {
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> SYNC_ENTITY_POSITION = (user, event, packet) -> {
try {
int entityId = (int) Reflections.field$ClientboundEntityPositionSyncPacket$id.get(packet);
if (BukkitFurnitureManager.instance().isFurnitureBaseEntity(entityId)) {
if (BukkitFurnitureManager.instance().isFurnitureRealEntity(entityId)) {
event.setCancelled(true);
}
} catch (Exception e) {
@@ -652,7 +653,7 @@ public class PacketConsumers {
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> MOVE_ENTITY = (user, event, packet) -> {
try {
int entityId = (int) Reflections.field$ClientboundMoveEntityPacket$entityId.get(packet);
if (BukkitFurnitureManager.instance().isFurnitureBaseEntity(entityId)) {
if (BukkitFurnitureManager.instance().isFurnitureRealEntity(entityId)) {
event.setCancelled(true);
}
} catch (Exception e) {

View File

@@ -40,4 +40,13 @@ public class DirectionUtils {
case EAST -> Reflections.instance$Direction$EAST;
};
}
public static Direction fromNMSDirection(Object direction) {
try {
int index = (int) Reflections.method$Direction$ordinal.invoke(direction);
return Direction.values()[index];
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -1,5 +1,6 @@
package net.momirealms.craftengine.bukkit.util;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import org.bukkit.World;
@@ -11,30 +12,25 @@ public class LightUtils {
private LightUtils() {}
@SuppressWarnings("unchecked")
public static void updateChunkLight(World world, Map<Long, BitSet> sectionPosSet) {
try {
Object serverLevel = Reflections.field$CraftWorld$ServerLevel.get(world);
Object chunkSource = Reflections.field$ServerLevel$chunkSource.get(serverLevel);
Object serverLevel = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(world);
Object chunkSource = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(serverLevel);
for (Map.Entry<Long, BitSet> entry : sectionPosSet.entrySet()) {
long chunkKey = entry.getKey();
Object chunkHolder = Reflections.method$ServerChunkCache$getVisibleChunkIfPresent.invoke(chunkSource, chunkKey);
Object chunkHolder = FastNMS.INSTANCE.method$ServerChunkCache$getVisibleChunkIfPresent(chunkSource, chunkKey);
if (chunkHolder == null) continue;
List<Object> players;
if (Reflections.method$ChunkHolder$getPlayers != null) {
players = (List<Object>) Reflections.method$ChunkHolder$getPlayers.invoke(chunkHolder, false);
} else {
Object chunkHolder$PlayerProvider = Reflections.field$ChunkHolder$playerProvider.get(chunkHolder);
players = (List<Object>) Reflections.method$ChunkHolder$PlayerProvider$getPlayers.invoke(chunkHolder$PlayerProvider, false);
}
List<Object> players = FastNMS.INSTANCE.method$ChunkHolder$getPlayers(chunkHolder);
if (players.isEmpty()) continue;
Object lightEngine = Reflections.field$ChunkHolder$lightEngine.get(chunkHolder);
BitSet blockChangedLightSectionFilter = (BitSet) Reflections.field$ChunkHolder$blockChangedLightSectionFilter.get(chunkHolder);
blockChangedLightSectionFilter.or(entry.getValue());
BitSet skyChangedLightSectionFilter = (BitSet) Reflections.field$ChunkHolder$skyChangedLightSectionFilter.get(chunkHolder);
Object chunkPos = Reflections.constructor$ChunkPos.newInstance((int) chunkKey, (int) (chunkKey >> 32));
Object lightPacket = Reflections.constructor$ClientboundLightUpdatePacket.newInstance(chunkPos, lightEngine, skyChangedLightSectionFilter, blockChangedLightSectionFilter);
Reflections.method$ChunkHolder$broadcast.invoke(chunkHolder, players, lightPacket);
Object chunkPos = FastNMS.INSTANCE.constructor$ChunkPos((int) chunkKey, (int) (chunkKey >> 32));
Object lightPacket = FastNMS.INSTANCE.constructor$ClientboundLightUpdatePacket(chunkPos, lightEngine, skyChangedLightSectionFilter, blockChangedLightSectionFilter);
for (Object player : players) {
FastNMS.INSTANCE.sendPacket(player, lightPacket);
}
blockChangedLightSectionFilter.clear();
skyChangedLightSectionFilter.clear();
}

View File

@@ -0,0 +1,31 @@
package net.momirealms.craftengine.bukkit.util;
import net.momirealms.craftengine.core.util.Mirror;
public class MirrorUtils {
private MirrorUtils() {}
public static Mirror fromNMSMirror(Object mirror) {
try {
int index = (int) Reflections.method$Mirror$ordinal.invoke(mirror);
return Mirror.values()[index];
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
public static Object toNMSMirror(Mirror mirror) {
switch (mirror) {
case FRONT_BACK -> {
return Reflections.instance$Mirror$FRONT_BACK;
}
case LEFT_RIGHT -> {
return Reflections.instance$Mirror$LEFT_RIGHT;
}
default -> {
return Reflections.instance$Mirror$NONE;
}
}
}
}

View File

@@ -12,7 +12,7 @@ public class NoteBlockChainUpdateUtils {
Object relativePos = Reflections.method$BlockPos$relative.invoke(blockPos, direction);
Object state = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, relativePos);
if (BlockStateUtils.isClientSideNoteBlock(state)) {
Reflections.method$ServerChunkCache$blockChanged.invoke(chunkSource, relativePos);
FastNMS.INSTANCE.method$ServerChunkCache$blockChanged(chunkSource, relativePos);
noteBlockChainUpdate(level, chunkSource, direction, relativePos, times+1);
}
}

View File

@@ -421,9 +421,9 @@ public class Reflections {
)
);
public static final Constructor<?> constructor$ClientboundBundlePacket = requireNonNull(
ReflectionUtils.getConstructor(clazz$ClientboundBundlePacket, Iterable.class)
);
// public static final Constructor<?> constructor$ClientboundBundlePacket = requireNonNull(
// ReflectionUtils.getConstructor(clazz$ClientboundBundlePacket, Iterable.class)
// );
public static final Class<?> clazz$Packet = requireNonNull(
ReflectionUtils.getClazz(
@@ -494,12 +494,6 @@ public class Reflections {
)
);
public static final Field field$NetworkManager = requireNonNull(
VersionHelper.isVersionNewerThan1_20_2() ?
ReflectionUtils.getDeclaredField(clazz$ServerGamePacketListenerImpl.getSuperclass(), clazz$Connection, 0) :
ReflectionUtils.getDeclaredField(clazz$ServerGamePacketListenerImpl, clazz$Connection, 0)
);
public static final Field field$Channel = requireNonNull(
ReflectionUtils.getDeclaredField(
clazz$Connection, Channel.class, 0
@@ -1053,11 +1047,12 @@ public class Reflections {
)
);
public static final Field field$CraftWorld$ServerLevel = requireNonNull(
ReflectionUtils.getDeclaredField(
clazz$CraftWorld, clazz$ServerLevel, 0
)
);
// @Deprecated
// public static final Field field$CraftWorld$ServerLevel = requireNonNull(
// ReflectionUtils.getDeclaredField(
// clazz$CraftWorld, clazz$ServerLevel, 0
// )
// );
public static final Method method$ServerLevel$getNoiseBiome = requireNonNull(
ReflectionUtils.getMethod(
@@ -1806,17 +1801,18 @@ public class Reflections {
)
);
public static final Field field$ServerLevel$chunkSource = requireNonNull(
ReflectionUtils.getDeclaredField(
clazz$ServerLevel, clazz$ServerChunkCache, 0
)
);
@Deprecated
// public static final Field field$ServerLevel$chunkSource = requireNonNull(
// ReflectionUtils.getDeclaredField(
// clazz$ServerLevel, clazz$ServerChunkCache, 0
// )
// );
public static final Method method$ServerChunkCache$blockChanged = requireNonNull(
ReflectionUtils.getMethod(
clazz$ServerChunkCache, void.class, clazz$BlockPos
)
);
// public static final Method method$ServerChunkCache$blockChanged = requireNonNull(
// ReflectionUtils.getMethod(
// clazz$ServerChunkCache, void.class, clazz$BlockPos
// )
// );
// public static final Method method$ServerChunkCache$getChunkAtIfLoadedMainThread = requireNonNull(
// ReflectionUtils.getMethod(
@@ -2644,6 +2640,7 @@ public class Reflections {
)
);
@Deprecated
public static final Constructor<?> constructor$ChunkPos = requireNonNull(
ReflectionUtils.getConstructor(
clazz$ChunkPos, int.class, int.class
@@ -2695,7 +2692,7 @@ public class Reflections {
)
);
// 1.20 ~ 1.21.4
// 1.20 ~ 1.21.4 moonrise
public static final Method method$ChunkHolder$getPlayers =
ReflectionUtils.getMethod(
clazz$ChunkHolder, List.class, boolean.class
@@ -2725,6 +2722,7 @@ public class Reflections {
)
);
@Deprecated
public static final Method method$ServerChunkCache$getVisibleChunkIfPresent = requireNonNull(
ReflectionUtils.getDeclaredMethod(
clazz$ServerChunkCache, clazz$ChunkHolder, long.class
@@ -3577,12 +3575,6 @@ public class Reflections {
)
);
public static final Method method$Level$setBlock = requireNonNull(
ReflectionUtils.getMethod(
clazz$Level, boolean.class, clazz$BlockPos, clazz$BlockState, int.class
)
);
public static final Method method$Block$updateFromNeighbourShapes = requireNonNull(
ReflectionUtils.getStaticMethod(
clazz$Block, clazz$BlockState, clazz$BlockState, clazz$LevelAccessor, clazz$BlockPos
@@ -3735,6 +3727,7 @@ public class Reflections {
)
);
@Deprecated
public static final Method method$LevelWriter$setBlock = requireNonNull(
ReflectionUtils.getMethod(
clazz$LevelWriter, boolean.class, clazz$BlockPos, clazz$BlockState, int.class
@@ -5930,4 +5923,131 @@ public class Reflections {
? ReflectionUtils.getMethod(clazz$Entity, int.class, new String[]{"af"})
: ReflectionUtils.getMethod(clazz$Entity, int.class, new String[]{"getId", "aj", "ah", "af"})
);
public static final Class<?> clazz$ClientboundMoveEntityPacket$PosRot = requireNonNull(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("network.protocol.game.ClientboundMoveEntityPacket$PosRot"),
BukkitReflectionUtils.assembleMCClass("network.protocol.game.PacketPlayOutEntity$PacketPlayOutRelEntityMoveLook")
)
);
public static final Class<?> clazz$ClientboundRotateHeadPacket = requireNonNull(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("network.protocol.game.ClientboundRotateHeadPacket"),
BukkitReflectionUtils.assembleMCClass("network.protocol.game.PacketPlayOutEntityHeadRotation")
)
);
public static final Field field$ClientboundRotateHeadPacket$entityId = requireNonNull(
ReflectionUtils.getDeclaredField(
clazz$ClientboundRotateHeadPacket, int.class, 0
)
);
public static final Class<?> clazz$ClientboundSetEntityMotionPacket = requireNonNull(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("network.protocol.game.ClientboundSetEntityMotionPacket"),
BukkitReflectionUtils.assembleMCClass("network.protocol.game.PacketPlayOutEntityVelocity")
)
);
public static final Field field$ClientboundSetEntityMotionPacket$id = requireNonNull(
ReflectionUtils.getDeclaredField(
clazz$ClientboundSetEntityMotionPacket, int.class, 0
)
);
public static final Class<?> clazz$Rotation = requireNonNull(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("world.level.block.Rotation"),
BukkitReflectionUtils.assembleMCClass("world.level.block.EnumBlockRotation")
)
);
public static final Method method$Rotation$values = requireNonNull(
ReflectionUtils.getStaticMethod(
clazz$Rotation, clazz$Rotation.arrayType()
)
);
public static final Object instance$Rotation$NONE;
public static final Object instance$Rotation$CLOCKWISE_90;
public static final Object instance$Rotation$CLOCKWISE_180;
public static final Object instance$Rotation$COUNTERCLOCKWISE_90;
static {
try {
Object[] values = (Object[]) method$Rotation$values.invoke(null);
instance$Rotation$NONE = values[0];
instance$Rotation$CLOCKWISE_90 = values[1];
instance$Rotation$CLOCKWISE_180 = values[2];
instance$Rotation$COUNTERCLOCKWISE_90 = values[3];
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
public static final Method method$Rotation$ordinal = requireNonNull(
ReflectionUtils.getMethod(
clazz$Rotation, int.class, new String[]{"ordinal"}
)
);
public static final Class<?> clazz$Mirror = requireNonNull(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("world.level.block.Mirror"),
BukkitReflectionUtils.assembleMCClass("world.level.block.EnumBlockMirror")
)
);
public static final Method method$Mirror$values = requireNonNull(
ReflectionUtils.getStaticMethod(
clazz$Mirror, clazz$Mirror.arrayType()
)
);
public static final Object instance$Mirror$NONE;
public static final Object instance$Mirror$LEFT_RIGHT;
public static final Object instance$Mirror$FRONT_BACK;
static {
try {
Object[] values = (Object[]) method$Mirror$values.invoke(null);
instance$Mirror$NONE = values[0];
instance$Mirror$LEFT_RIGHT = values[1];
instance$Mirror$FRONT_BACK = values[2];
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
public static final Method method$Mirror$ordinal = requireNonNull(
ReflectionUtils.getMethod(
clazz$Mirror, int.class, new String[]{"ordinal"}
)
);
public static final Method method$BlockBehaviour$rotate = requireNonNull(
ReflectionUtils.getDeclaredMethod(
clazz$BlockBehaviour, clazz$BlockState, clazz$BlockState, clazz$Rotation
)
);
public static final Method method$BlockBehaviour$mirror = requireNonNull(
ReflectionUtils.getDeclaredMethod(
clazz$BlockBehaviour, clazz$BlockState, clazz$BlockState, clazz$Mirror
)
);
public static final Method method$BlockStateBase$rotate = requireNonNull(
ReflectionUtils.getMethod(
clazz$BlockStateBase, clazz$BlockState, clazz$Rotation
)
);
public static final Method method$BlockStateBase$mirror = requireNonNull(
ReflectionUtils.getMethod(
clazz$BlockStateBase, clazz$BlockState, clazz$Mirror
)
);
}

View File

@@ -0,0 +1,34 @@
package net.momirealms.craftengine.bukkit.util;
import net.momirealms.craftengine.core.util.Rotation;
public class RotationUtils {
private RotationUtils() {}
public static Rotation fromNMSRotation(Object rotation) {
try {
int index = (int) Reflections.method$Rotation$ordinal.invoke(rotation);
return Rotation.values()[index];
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
public static Object toNMSRotation(Rotation rotation) {
switch (rotation) {
case CLOCKWISE_90 -> {
return Reflections.instance$Rotation$CLOCKWISE_90;
}
case CLOCKWISE_180 -> {
return Reflections.instance$Rotation$CLOCKWISE_180;
}
case COUNTERCLOCKWISE_90 -> {
return Reflections.instance$Rotation$COUNTERCLOCKWISE_90;
}
default -> {
return Reflections.instance$Rotation$NONE;
}
}
}
}

View File

@@ -1,8 +1,8 @@
package net.momirealms.craftengine.bukkit.world;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.util.EntityUtils;
import net.momirealms.craftengine.bukkit.util.ItemUtils;
import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.VersionHelper;
@@ -36,7 +36,7 @@ public class BukkitWorld implements World {
@Override
public Object serverWorld() {
try {
return Reflections.field$CraftWorld$ServerLevel.get(platformWorld());
return FastNMS.INSTANCE.field$CraftWorld$ServerLevel(platformWorld());
} catch (Exception e) {
throw new RuntimeException("Failed to get server world", e);
}

View File

@@ -3,6 +3,7 @@ package net.momirealms.craftengine.bukkit.world;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.item.behavior.BlockItemBehavior;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.bukkit.util.Reflections;
@@ -51,7 +52,7 @@ public class BukkitWorldBlock implements WorldBlock {
public boolean isWaterSource(BlockPlaceContext blockPlaceContext) {
try {
Location location = block.getLocation();
Object serverLevel = Reflections.field$CraftWorld$ServerLevel.get(block.getWorld());
Object serverLevel = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(block.getWorld());
Object fluidData = Reflections.method$Level$getFluidState.invoke(serverLevel, LocationUtils.toBlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()));
if (fluidData == null) return false;
return (boolean) Reflections.method$FluidState$isSource.invoke(fluidData);

View File

@@ -1,10 +1,16 @@
package net.momirealms.craftengine.core.block.behavior;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.item.context.BlockPlaceContext;
import net.momirealms.craftengine.shared.block.BlockBehavior;
public abstract class AbstractBlockBehavior extends BlockBehavior {
protected CustomBlock customBlock;
public AbstractBlockBehavior(CustomBlock customBlock) {
this.customBlock = customBlock;
}
public ImmutableBlockState updateStateForPlacement(BlockPlaceContext context, ImmutableBlockState state) {
return state;

View File

@@ -33,5 +33,5 @@ public interface FurnitureManager extends Reloadable, ConfigSectionParser {
Optional<CustomFurniture> getFurniture(Key id);
boolean isFurnitureBaseEntity(int entityId);
boolean isFurnitureRealEntity(int entityId);
}

View File

@@ -1,9 +0,0 @@
package net.momirealms.craftengine.core.font;
import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser;
public interface EmojiManager extends ConfigSectionParser {
String CONFIG_SECTION_NAME = "emojis";
}

View File

@@ -2,7 +2,7 @@ org.gradle.jvmargs=-Xmx1G
# Project settings
# Rule: [major update].[feature update].[bug fix]
project_version=0.0.41
project_version=0.0.42
config_version=19
lang_version=3
project_group=net.momirealms
@@ -49,7 +49,7 @@ mojang_brigadier_version=1.0.18
byte_buddy_version=1.15.11
snake_yaml_version=2.3
anti_grief_version=0.13
nms_helper_version=0.28
nms_helper_version=0.34
# Ignite Dependencies
mixinextras_version=0.4.1
mixin_version=0.15.2+mixin.0.8.7

View File

@@ -135,7 +135,7 @@ repositories {
```
```kotlin
dependencies {
compileOnly("net.momirealms:craft-engine-core:0.0.38")
compileOnly("net.momirealms:craft-engine-bukkit:0.0.38")
compileOnly("net.momirealms:craft-engine-core:0.0.42")
compileOnly("net.momirealms:craft-engine-bukkit:0.0.42")
}
```

View File

@@ -135,7 +135,7 @@ repositories {
```
```kotlin
dependencies {
compileOnly("net.momirealms:craft-engine-core:0.0.38")
compileOnly("net.momirealms:craft-engine-bukkit:0.0.38")
compileOnly("net.momirealms:craft-engine-core:0.0.42")
compileOnly("net.momirealms:craft-engine-bukkit:0.0.42")
}
```

View File

@@ -10,10 +10,7 @@ import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.ScheduledTickAccess;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.BonemealableBlock;
import net.minecraft.world.level.block.Fallable;
import net.minecraft.world.level.block.*;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
@@ -68,10 +65,30 @@ public class CraftEngineBlock
}
}
@Override
protected @NotNull BlockState rotate(@NotNull BlockState state, @NotNull Rotation rotation) {
try {
return (BlockState) this.behaviorHolder.value().rotate(this, new Object[]{state, rotation}, () -> super.rotate(state, rotation));
} catch (Exception e) {
e.printStackTrace();
return super.rotate(state, rotation);
}
}
@Override
protected @NotNull BlockState mirror(@NotNull BlockState state, @NotNull Mirror mirror) {
try {
return (BlockState) this.behaviorHolder.value().mirror(this, new Object[]{state, mirror}, () -> super.mirror(state, mirror));
} catch (Exception e) {
e.printStackTrace();
return super.mirror(state, mirror);
}
}
@Override
protected void tick(@NotNull BlockState state, @NotNull ServerLevel level, @NotNull BlockPos pos, @NotNull RandomSource random) {
try {
behaviorHolder.value().tick(this, new Object[]{state, level, pos, random}, () -> {
this.behaviorHolder.value().tick(this, new Object[]{state, level, pos, random}, () -> {
super.tick(state, level, pos, random);
return null;
});

View File

@@ -4,6 +4,14 @@ import java.util.concurrent.Callable;
public abstract class BlockBehavior {
public Object rotate(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
return superMethod.call();
}
public Object mirror(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
return superMethod.call();
}
public Object updateShape(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
return superMethod.call();
}