Skip to content

Commit 47d7ba2

Browse files
Add block drops and Material API support
I'm so so so sorry for the Java crimes I have caused to make this work. Spigot/Paper, please stop making Material an Enum
1 parent 8079d9a commit 47d7ba2

7 files changed

Lines changed: 139 additions & 27 deletions

File tree

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package parallelmc.parallelworlds;
2+
3+
import net.minecraft.core.particles.ParticleOptions;
4+
import net.minecraft.world.item.ItemStack;
5+
import org.jetbrains.annotations.NotNull;
6+
7+
import java.util.List;
8+
9+
public record ParallelBlockData(@NotNull List<ItemStack> drops, @NotNull ParticleOptions particles) {
10+
// TODO: Maybe replace this so you getDrops passing in an item, similar to base game
11+
}

parallelworlds/src/main/java/parallelmc/parallelworlds/ParallelWorlds.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public void onEnable() {
1919
super.onEnable();
2020

2121
getServer().getPluginManager().registerEvents(new BlockBreakPlaceListener(), this);
22+
2223
}
2324

2425

parallelworlds/src/main/java/parallelmc/parallelworlds/ParallelWorldsBootstrapper.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,13 @@ public void bootstrap(BootstrapContext context) {
3737
register(registry, "polished_sandstone", Block::new,
3838
BlockBehaviour.Properties.of().mapColor(MapColor.SAND).requiresCorrectToolForDrops().strength(0.8F).sound(SoundType.STONE),
3939
Blocks.NOTE_BLOCK.getStateDefinition().any().setValue(NoteBlock.INSTRUMENT, NoteBlockInstrument.BANJO).setValue(NoteBlock.NOTE, 0),
40+
Blocks.SANDSTONE.defaultBlockState(),
4041
Component.literal("Polished Sandstone").setStyle(Style.EMPTY));
4142

4243
register(registry, "quicksand", QuicksandBlock::new,
4344
BlockBehaviour.Properties.of().mapColor(MapColor.SAND).strength(0.25F).sound(SoundType.SAND).dynamicShape().noOcclusion().isRedstoneConductor((blockState, blockGetter, blockPos) -> false),
4445
Blocks.NOTE_BLOCK.getStateDefinition().any().setValue(NoteBlock.INSTRUMENT, NoteBlockInstrument.BANJO).setValue(NoteBlock.NOTE, 1),
46+
Blocks.SAND.defaultBlockState(),
4547
Component.literal("Quicksand").setStyle(Style.EMPTY));
4648

4749
registry.freeze();
@@ -52,10 +54,12 @@ public JavaPlugin createPlugin(PluginProviderContext context) {
5254
return PluginBootstrap.super.createPlugin(context);
5355
}
5456

55-
private static void register(ParallelBlockRegistry registry, String name, Function<BlockBehaviour.Properties, Block> factory, BlockBehaviour.Properties properties, BlockState targetBlockstate, Component itemName) {
57+
private static void register(ParallelBlockRegistry registry, String name,
58+
Function<BlockBehaviour.Properties, Block> factory, BlockBehaviour.Properties properties,
59+
BlockState targetBlockstate, BlockState particleState, Component itemName) {
5660
ResourceKey<Block> blockKey = ResourceKey.create(Registries.BLOCK, Identifier.fromNamespaceAndPath("parallelutils", name));
5761
Block block = factory.apply(properties.setId(blockKey));
58-
registry.registerBlock(blockKey, block, targetBlockstate, itemName);
62+
registry.registerBlock(blockKey, block, targetBlockstate, particleState, itemName);
5963
}
6064

6165

parallelworlds/src/main/java/parallelmc/parallelworlds/ReflectionHelper.java

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
package parallelmc.parallelworlds;
22

33
import org.jetbrains.annotations.Nullable;
4+
import sun.misc.Unsafe;
45

6+
import java.lang.invoke.MethodHandle;
7+
import java.lang.invoke.MethodHandles;
8+
import java.lang.reflect.Array;
9+
import java.lang.reflect.Constructor;
510
import java.lang.reflect.Field;
11+
import java.lang.reflect.Modifier;
612

713
public class ReflectionHelper {
814

@@ -23,4 +29,59 @@ public static <T, U> U getPrivateField(String fieldName, Class<T> clazz, T objec
2329
return null;
2430
}
2531
}
32+
33+
/*
34+
Jank Enum code from https://notes.highlysuspect.agency/blog/enum_reflection/
35+
*/
36+
@SafeVarargs
37+
private static <T> T[] concat(Class<T> t, T[] a, T... b) {
38+
T[] result = (T[]) Array.newInstance(t, a.length + b.length); //i love java
39+
System.arraycopy(a, 0, result, 0, a.length);
40+
System.arraycopy(b, 0, result, a.length, b.length);
41+
return result;
42+
}
43+
44+
// private static Field definalizeField(Field f) throws Throwable {
45+
// f.setAccessible(true);
46+
//
47+
// Field modifiersField = Field.class.getDeclaredField("modifiers");
48+
// modifiersField.setAccessible(true);
49+
// modifiersField.set(f, (f.getModifiers() | Modifier.PUBLIC) & ~(Modifier.FINAL | Modifier.PRIVATE | Modifier.PROTECTED));
50+
// return f;
51+
// }
52+
53+
@SuppressWarnings("unchecked")
54+
public static <T extends Enum<T>> T makeEnum(Class<T> clazz, String name, int ord, Class<?>[] argTypes, Object... argValues) throws Throwable {
55+
//prepend the (String, int) arguments and find the constructor
56+
argTypes = concat(Class.class, new Class<?>[]{String.class, int.class}, argTypes);
57+
argValues = concat(Object.class, new Object[]{name, ord}, argValues);
58+
59+
//find the constructor
60+
Constructor<T> cons = clazz.getDeclaredConstructor(argTypes);
61+
cons.setAccessible(true);
62+
MethodHandle consHandle = MethodHandles.lookup().unreflectConstructor(cons);
63+
64+
//instantiate the enum (!!!)
65+
T result = (T) consHandle.invokeWithArguments(argValues);
66+
67+
// //append to Enum.$VALUES
68+
// Field valuesField = definalizeField(clazz.getDeclaredField("$VALUES")); //hmm...
69+
// valuesField.set(null, concat(clazz, (T[]) valuesField.get(null), result));
70+
71+
//fix up the caches in Class
72+
// Field enumConstantsField = Class.class.getDeclaredField("enumConstants");
73+
// enumConstantsField.setAccessible(true);
74+
// enumConstantsField.set(clazz, null);
75+
// Field enumConstantDirectoryField = Class.class.getDeclaredField("enumConstantDirectory");
76+
// enumConstantDirectoryField.setAccessible(true);
77+
// enumConstantDirectoryField.set(clazz, null);
78+
Field uField = Unsafe.class.getDeclaredField("theUnsafe");
79+
uField.setAccessible(true);
80+
Unsafe unsafe = (Unsafe) uField.get(null);
81+
82+
unsafe.putObject(clazz, unsafe.objectFieldOffset(Class.class.getDeclaredField("enumConstants")), null);
83+
unsafe.putObject(clazz, unsafe.objectFieldOffset(Class.class.getDeclaredField("enumConstantDirectory")), null);
84+
85+
return result;
86+
}
2687
}

parallelworlds/src/main/java/parallelmc/parallelworlds/events/BlockBreakPlaceListener.java

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import net.minecraft.server.level.ServerLevel;
55
import net.minecraft.world.item.ItemStack;
66
import net.minecraft.world.level.block.Block;
7+
import net.minecraft.world.level.block.Blocks;
78
import net.minecraft.world.level.block.state.BlockState;
89
import org.bukkit.GameMode;
910
import org.bukkit.block.data.BlockData;
@@ -14,9 +15,12 @@
1415
import org.bukkit.event.Listener;
1516
import org.bukkit.event.block.BlockBreakEvent;
1617
import org.bukkit.event.block.BlockPlaceEvent;
18+
import parallelmc.parallelworlds.ParallelBlockData;
1719
import parallelmc.parallelworlds.registry.ParallelBlockRegistry;
1820

1921
import java.util.List;
22+
import java.util.logging.Level;
23+
import java.util.logging.Logger;
2024

2125
public class BlockBreakPlaceListener implements Listener {
2226

@@ -28,26 +32,32 @@ public BlockBreakPlaceListener() {
2832

2933
@EventHandler(priority = EventPriority.HIGHEST)
3034
public void handleBlockBreak(BlockBreakEvent event) {
31-
if (event.getPlayer().getGameMode() == GameMode.CREATIVE) return;
3235
CraftBlock cb = (CraftBlock)event.getBlock();
3336

3437
BlockState blockState = cb.getNMS();
3538

3639
int id = Block.getId(blockState);
3740

38-
List<ItemStack> drops = blockRegistry.getDrops(id);
41+
ParallelBlockData blockData = blockRegistry.getBlockData(id);
42+
43+
if (blockData == null) return;
44+
45+
event.setDropItems(false);
46+
47+
ServerLevel level = cb.getCraftWorld().getHandle();
48+
BlockPos pos = cb.getPosition();
3949

40-
if (drops != null) {
41-
// This is a registered Parallel Block
42-
event.setDropItems(false); // Stop normal drops
4350

44-
ServerLevel level = cb.getCraftWorld().getHandle();
45-
BlockPos pos = cb.getPosition();
51+
if (event.getPlayer().getGameMode() == GameMode.CREATIVE) return;
52+
53+
// TODO: Need special cases for silk touch, etc.
4654

47-
for (ItemStack item : drops) {
48-
Block.popResource(level, pos, item);
49-
}
55+
List<ItemStack> drops = blockData.drops();
56+
57+
for (ItemStack item : drops) {
58+
Block.popResource(level, pos, item);
5059
}
60+
5161
}
5262

5363
@EventHandler(priority = EventPriority.HIGHEST)

parallelworlds/src/main/java/parallelmc/parallelworlds/events/BlockPacketListener.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import com.github.retrooper.packetevents.protocol.world.chunk.palette.SingletonPalette;
1515
import com.github.retrooper.packetevents.util.Vector3i;
1616
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerBlockPlacement;
17+
import com.github.retrooper.packetevents.wrapper.play.client.WrapperPlayClientPlayerDigging;
1718
import com.github.retrooper.packetevents.wrapper.play.server.*;
1819
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
1920
import net.minecraft.world.level.block.Block;
@@ -58,6 +59,7 @@ public void onPacketReceive(PacketReceiveEvent event) {
5859
public void onPacketSend(PacketSendEvent event) {
5960

6061
PacketTypeCommon type = event.getPacketType();
62+
6163

6264
if (type == PacketType.Play.Server.CHUNK_DATA || type == PacketType.Play.Server.MAP_CHUNK_BULK) {
6365

@@ -201,13 +203,14 @@ public void onPacketSend(PacketSendEvent event) {
201203
WrapperPlayServerDeclareCommands packet = new WrapperPlayServerDeclareCommands(event);
202204

203205
packet.getNodes();
206+
207+
} else if (type != PacketType.Play.Server.ENTITY_HEAD_LOOK &&
208+
type != PacketType.Play.Server.ENTITY_RELATIVE_MOVE &&
209+
type != PacketType.Play.Server.ENTITY_RELATIVE_MOVE_AND_ROTATION &&
210+
type != PacketType.Play.Server.ENTITY_VELOCITY &&
211+
type != PacketType.Play.Server.ENTITY_POSITION_SYNC){
212+
//Logger.getGlobal().log(Level.WARNING, type.getName());
204213
}
205-
// } else if (type != PacketType.Play.Server.ENTITY_HEAD_LOOK &&
206-
// type != PacketType.Play.Server.ENTITY_RELATIVE_MOVE &&
207-
// type != PacketType.Play.Server.ENTITY_RELATIVE_MOVE_AND_ROTATION &&
208-
// type != PacketType.Play.Server.ENTITY_VELOCITY){
209-
// Logger.getGlobal().log(Level.WARNING, type.getName());
210-
// }
211214
}
212215

213216

parallelworlds/src/main/java/parallelmc/parallelworlds/registry/ParallelBlockRegistry.java

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
import net.minecraft.core.component.DataComponentPatch;
55
import net.minecraft.core.component.DataComponents;
66
import net.minecraft.core.component.TypedDataComponent;
7+
import net.minecraft.core.particles.BlockParticleOption;
8+
import net.minecraft.core.particles.ParticleOptions;
9+
import net.minecraft.core.particles.ParticleTypes;
710
import net.minecraft.core.registries.BuiltInRegistries;
811
import net.minecraft.core.registries.Registries;
912
import net.minecraft.network.chat.Component;
@@ -15,8 +18,12 @@
1518
import net.minecraft.world.level.block.Blocks;
1619
import net.minecraft.world.level.block.state.BlockState;
1720
import net.minecraft.world.level.storage.loot.functions.*;
21+
import org.bukkit.Material;
22+
import org.bukkit.craftbukkit.block.CraftBlock;
1823
import org.jetbrains.annotations.NotNull;
1924
import org.jetbrains.annotations.Nullable;
25+
import parallelmc.parallelworlds.ParallelBlockData;
26+
import parallelmc.parallelworlds.ReflectionHelper;
2027

2128
import java.util.*;
2229
import java.util.logging.Level;
@@ -36,8 +43,10 @@ public class ParallelBlockRegistry {
3643
// this can be converted into map of BlockType -> Queue<Integer> since we can choose values entirely server side
3744
private final HashMap<BlockState, Integer> availableStates;
3845

39-
private final HashMap<Integer, List<ItemStack>> dropMap;
46+
private final HashMap<Integer, ParallelBlockData> blockDataMap;
4047
private final HashMap<Integer, BlockState> placeMap;
48+
private int nextMaterialIndex = Material.values().length;
49+
private final Map<String, Material> BY_NAME;
4150

4251
private boolean frozen = false;
4352

@@ -50,13 +59,15 @@ private ParallelBlockRegistry() throws RuntimeException, NoSuchMethodException {
5059

5160
blockRegistry = getWritableRegistry(Registries.BLOCK);
5261

62+
BY_NAME = getPrivateField("BY_NAME", Material.class, null, Map.class);
63+
5364
if (blockRegistry == null) {
5465
throw new RuntimeException("Cannot find block registry");
5566
}
5667

5768
stateMap = new HashMap<>();
5869
availableStates = new HashMap<>();
59-
dropMap = new HashMap<>();
70+
blockDataMap = new HashMap<>();
6071
placeMap = new HashMap<>();
6172

6273
// TODO: Fill available states
@@ -75,7 +86,6 @@ private void addFullBlock(Block block) {
7586

7687
availableStates.put(state, Block.BLOCK_STATE_REGISTRY.getId(state));
7788
}
78-
7989
}
8090

8191
public static ParallelBlockRegistry getInstance() {
@@ -116,22 +126,34 @@ public void freeze() {
116126
return null;
117127
}
118128

119-
public boolean registerBlock(ResourceKey<@NotNull Block> key, Block block, BlockState targetBlockstate, Component name) {
129+
public boolean registerBlock(ResourceKey<@NotNull Block> key, Block block, BlockState targetBlockstate, BlockState particleState, Component name) {
120130
ItemStack stack = Items.BARRIER.getDefaultInstance();
121131

122132
stack.applyComponentsAndValidate(
123133
DataComponentPatch.builder()
124134
.set(TypedDataComponent.createUnchecked(DataComponents.ITEM_MODEL, key.identifier()))
125135
.set(TypedDataComponent.createUnchecked(DataComponents.ITEM_NAME, name)).build());
126136

127-
return registerBlock(key, block, targetBlockstate, List.of(stack), stack);
137+
return registerBlock(key, block, targetBlockstate, List.of(stack), new BlockParticleOption(ParticleTypes.BLOCK, particleState),stack);
128138
}
129139

130-
public boolean registerBlock(ResourceKey<@NotNull Block> key, Block block, BlockState targetBlockstate, List<ItemStack> item, ItemStack placeBlock) {
140+
public boolean registerBlock(ResourceKey<@NotNull Block> key, Block block, BlockState targetBlockstate, List<ItemStack> drops, ParticleOptions particle, ItemStack placeBlock) {
131141
if (frozen) return false;
132142

133143
if (!availableStates.containsKey(targetBlockstate)) throw new IllegalStateException("Block state is already used or does not exist");
134144

145+
try {
146+
Material newMat = ReflectionHelper.makeEnum(Material.class,
147+
key.identifier().getPath().toUpperCase(Locale.ROOT), nextMaterialIndex++, new Class[]{int.class}, -1);
148+
149+
BY_NAME.put(newMat.name(), newMat);
150+
151+
} catch (Throwable t) {
152+
Logger.getGlobal().log(Level.SEVERE, "Unable to create new Material!");
153+
t.printStackTrace();
154+
155+
}
156+
135157
Holder.Reference<Block> registeredBlock = blockRegistry.register(key, block, RegistrationInfo.BUILT_IN);
136158

137159
// TODO: This technically doesn't work since several blockstates are mapped to a single one, but it's easier for now
@@ -140,7 +162,7 @@ public boolean registerBlock(ResourceKey<@NotNull Block> key, Block block, Block
140162
Integer stateId = availableStates.remove(targetBlockstate);
141163

142164
stateMap.put(Block.BLOCK_STATE_REGISTRY.size(), stateId); // Map the new block state to an unused state
143-
dropMap.put(Block.BLOCK_STATE_REGISTRY.size(), item);
165+
blockDataMap.put(Block.BLOCK_STATE_REGISTRY.size(), new ParallelBlockData(drops, particle));
144166
placeMap.put(ItemStack.hashItemAndComponents(placeBlock), blockState);
145167

146168
Block.BLOCK_STATE_REGISTRY.add(blockState);
@@ -161,8 +183,8 @@ public Integer getMappedState(int state) {
161183
}
162184

163185
@Nullable
164-
public List<ItemStack> getDrops(int state) {
165-
return dropMap.get(state);
186+
public ParallelBlockData getBlockData(int state) {
187+
return blockDataMap.get(state);
166188
}
167189

168190
@Nullable

0 commit comments

Comments
 (0)