Skip to content

Commit 86945a8

Browse files
Add registry to map custom blocks to unused block states
1 parent e163bb2 commit 86945a8

5 files changed

Lines changed: 158 additions & 67 deletions

File tree

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44
import com.github.retrooper.packetevents.event.PacketListenerPriority;
55
import org.bukkit.plugin.java.JavaPlugin;
66
import parallelmc.parallelworlds.events.BlockPacketListener;
7+
import parallelmc.parallelworlds.registry.ParallelBlockRegistry;
78

89
public final class ParallelWorlds extends JavaPlugin {
910

1011
@Override
1112
public void onLoad() {
1213

13-
PacketEvents.getAPI().getEventManager().registerListener(new BlockPacketListener(ParallelWorldsBootstrapper.getFirstCustomId()), PacketListenerPriority.HIGHEST);
14+
PacketEvents.getAPI().getEventManager().registerListener(new BlockPacketListener(ParallelBlockRegistry.getFirstCustomId()), PacketListenerPriority.HIGHEST);
1415
}
1516

1617
@Override

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

Lines changed: 9 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -4,57 +4,39 @@
44
import io.papermc.paper.plugin.bootstrap.PluginBootstrap;
55
import io.papermc.paper.plugin.bootstrap.PluginProviderContext;
66
import net.minecraft.core.*;
7-
import net.minecraft.core.registries.BuiltInRegistries;
87
import net.minecraft.core.registries.Registries;
98
import net.minecraft.resources.Identifier;
109
import net.minecraft.resources.ResourceKey;
1110
import net.minecraft.world.level.block.Block;
1211
import net.minecraft.world.level.block.Blocks;
12+
import net.minecraft.world.level.block.NoteBlock;
1313
import net.minecraft.world.level.block.SoundType;
1414
import net.minecraft.world.level.block.state.BlockBehaviour;
15-
import net.minecraft.world.level.block.state.BlockState;
15+
import net.minecraft.world.level.block.state.properties.NoteBlockInstrument;
1616
import net.minecraft.world.level.material.MapColor;
1717
import org.bukkit.plugin.java.JavaPlugin;
18-
import org.jetbrains.annotations.NotNull;
19-
import org.jetbrains.annotations.Nullable;
2018
import parallelmc.parallelworlds.blocks.QuicksandBlock;
2119
import parallelmc.parallelworlds.blocks.TestBlock;
22-
23-
import java.util.Map;
24-
import java.util.logging.Level;
25-
import java.util.logging.Logger;
26-
27-
import static parallelmc.parallelworlds.ReflectionHelper.getPrivateField;
20+
import parallelmc.parallelworlds.registry.ParallelBlockRegistry;
2821

2922
public class ParallelWorldsBootstrapper implements PluginBootstrap {
3023

31-
private static int firstCustomId = 0;
32-
3324
@Override
3425
public void bootstrap(BootstrapContext context) {
3526

36-
// This is a ridiculous hack to force internal blocks to register first
37-
Block dummy = Blocks.DIRT;
38-
Logger.getGlobal().log(Level.WARNING, dummy.toString());
39-
40-
firstCustomId = Block.BLOCK_STATE_REGISTRY.size();
41-
42-
43-
WritableRegistry<Block> blockRegistry = getWritableRegistry(Registries.BLOCK);
44-
45-
if (blockRegistry == null) {
46-
return;
47-
}
48-
27+
ParallelBlockRegistry registry = ParallelBlockRegistry.getInstance();
4928

5029
ResourceKey<Block> testBlockKey = ResourceKey.create(Registries.BLOCK, Identifier.fromNamespaceAndPath("parallelutils", "testblock"));
5130
Block testBlock = new TestBlock(BlockBehaviour.Properties.of().mapColor(MapColor.STONE).strength(0.5f).sound(SoundType.AMETHYST).setId(testBlockKey));
52-
registerBlock(blockRegistry, testBlockKey, testBlock);
31+
registry.registerBlock(testBlockKey, testBlock, Blocks.NOTE_BLOCK.getStateDefinition().any().setValue(NoteBlock.INSTRUMENT, NoteBlockInstrument.BANJO).setValue(NoteBlock.NOTE, 0));
5332

5433
ResourceKey<Block> quicksandKey = ResourceKey.create(Registries.BLOCK, Identifier.fromNamespaceAndPath("parallelutils", "quicksand"));
5534
Block quicksandBlock = new QuicksandBlock(BlockBehaviour.Properties.of().mapColor(MapColor.SAND).strength(0.25F).sound(SoundType.SAND)
5635
.dynamicShape().noOcclusion().isRedstoneConductor((blockState, blockGetter, blockPos) -> false).setId(quicksandKey));
57-
registerBlock(blockRegistry, quicksandKey, quicksandBlock);
36+
registry.registerBlock(quicksandKey, quicksandBlock, Blocks.NOTE_BLOCK.getStateDefinition().any().setValue(NoteBlock.INSTRUMENT, NoteBlockInstrument.BANJO).setValue(NoteBlock.NOTE, 1));
37+
38+
39+
registry.freeze();
5840
}
5941

6042
@Override
@@ -64,35 +46,4 @@ public JavaPlugin createPlugin(PluginProviderContext context) {
6446

6547

6648

67-
// This is private since it can ONLY be run during bootstrap or things break real bad
68-
@Nullable
69-
private static <T> WritableRegistry<T> getWritableRegistry(ResourceKey<Registry<T>> registryKey) {
70-
71-
// Get the global WRITABLE_REGISTRY registry
72-
MappedRegistry<WritableRegistry<?>> writable_registry = (MappedRegistry<WritableRegistry<?>>) getPrivateField("WRITABLE_REGISTRY", BuiltInRegistries.class, null, WritableRegistry.class);
73-
74-
// Search registry byValue (since byKey doesn't work for whatever reason...)
75-
Map<WritableRegistry<?>, Holder.Reference<WritableRegistry<?>>> byValue = getPrivateField("byValue", MappedRegistry.class, writable_registry, Map.class);
76-
77-
for (WritableRegistry i : byValue.keySet()) {
78-
if (String.valueOf(i.key().identifier()).equals(String.valueOf(registryKey.identifier()))) {
79-
return i;
80-
}
81-
}
82-
83-
return null;
84-
}
85-
86-
private static void registerBlock(WritableRegistry<Block> blockRegistry, ResourceKey<@NotNull Block> key, Block block) {
87-
Holder.Reference<Block> registeredBlock = blockRegistry.register(key, block, RegistrationInfo.BUILT_IN);
88-
89-
for (BlockState blockState : registeredBlock.value().getStateDefinition().getPossibleStates()) {
90-
Block.BLOCK_STATE_REGISTRY.add(blockState);
91-
blockState.initCache();
92-
}
93-
}
94-
95-
public static int getFirstCustomId() {
96-
return firstCustomId;
97-
}
9849
}

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

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import net.minecraft.world.level.block.Blocks;
1717
import net.minecraft.world.level.block.state.BlockState;
1818
import parallelmc.parallelworlds.ParallelWorldsBootstrapper;
19+
import parallelmc.parallelworlds.registry.ParallelBlockRegistry;
1920

2021
import java.util.logging.Level;
2122
import java.util.logging.Logger;
@@ -27,11 +28,14 @@ public class BlockPacketListener implements PacketListener {
2728

2829
private final int firstCustomId;
2930

30-
private final int replace_state;
31+
private final int defaultReplaceState;
32+
33+
private final ParallelBlockRegistry registry;
3134

3235
public BlockPacketListener(int firstCustomId) {
3336
this.firstCustomId = firstCustomId;
34-
this.replace_state = Block.BLOCK_STATE_REGISTRY.getId(Blocks.NOTE_BLOCK.defaultBlockState());;
37+
this.defaultReplaceState = Block.BLOCK_STATE_REGISTRY.getId(Blocks.NOTE_BLOCK.defaultBlockState());;
38+
this.registry = ParallelBlockRegistry.getInstance();
3539
}
3640

3741
@Override
@@ -48,8 +52,13 @@ public void onPacketSend(PacketSendEvent event) {
4852
for (int z = 0; z < 16; z++) {
4953
int id = c.getBlockId(x, y, z);
5054
if (id >= firstCustomId) {
51-
BlockState state = Block.BLOCK_STATE_REGISTRY.byId(id);
52-
Logger.getGlobal().log(Level.WARNING, state.toString());
55+
//BlockState state = Block.BLOCK_STATE_REGISTRY.byId(id);
56+
//Logger.getGlobal().log(Level.WARNING, state.toString());
57+
Integer replace_state = registry.getMappedState(id);
58+
if (replace_state == null) {
59+
replace_state = defaultReplaceState;
60+
Logger.getGlobal().log(Level.WARNING, "Could not find mapping for id" + id);
61+
}
5362

5463
c.set(x, y, z, replace_state);
5564

@@ -79,7 +88,14 @@ public void onPacketSend(PacketSendEvent event) {
7988
} else if (event.getPacketType() == PacketType.Play.Server.BLOCK_CHANGE) {
8089
WrapperPlayServerBlockChange packet = new WrapperPlayServerBlockChange(event);
8190

82-
if (packet.getBlockId() >= firstCustomId) {
91+
int id = packet.getBlockId();
92+
if (id >= firstCustomId) {
93+
Integer replace_state = registry.getMappedState(id);
94+
if (replace_state == null) {
95+
replace_state = defaultReplaceState;
96+
Logger.getGlobal().log(Level.WARNING, "Could not find mapping for id" + id);
97+
}
98+
8399
packet.setBlockID(replace_state);
84100
event.markForReEncode(true);
85101
}
@@ -88,7 +104,14 @@ public void onPacketSend(PacketSendEvent event) {
88104
} else if (event.getPacketType() == PacketType.Play.Server.BLOCK_ACTION) {
89105
WrapperPlayServerBlockAction packet = new WrapperPlayServerBlockAction(event);
90106

91-
if (packet.getBlockTypeId() >= firstCustomId) {
107+
int id = packet.getBlockTypeId();
108+
if (id >= firstCustomId) {
109+
Integer replace_state = registry.getMappedState(id);
110+
if (replace_state == null) {
111+
replace_state = defaultReplaceState;
112+
Logger.getGlobal().log(Level.WARNING, "Could not find mapping for id" + id);
113+
}
114+
92115
packet.setBlockTypeId(replace_state);
93116
event.markForReEncode(true);
94117
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import net.minecraft.world.item.Items;
44
import net.minecraft.world.level.block.Block;
5+
import net.minecraft.world.level.block.ScaffoldingBlock;
56
import net.minecraft.world.level.block.state.BlockState;
67
import org.bukkit.craftbukkit.block.CraftBlock;
78
import org.bukkit.event.EventHandler;
Lines changed: 117 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,133 @@
11
package parallelmc.parallelworlds.registry;
22

3+
import net.minecraft.core.*;
4+
import net.minecraft.core.registries.BuiltInRegistries;
5+
import net.minecraft.core.registries.Registries;
6+
import net.minecraft.resources.ResourceKey;
7+
import net.minecraft.world.level.block.Block;
8+
import net.minecraft.world.level.block.Blocks;
9+
import net.minecraft.world.level.block.NoteBlock;
10+
import net.minecraft.world.level.block.state.BlockState;
11+
import net.minecraft.world.level.block.state.properties.NoteBlockInstrument;
12+
import org.jetbrains.annotations.NotNull;
13+
import org.jetbrains.annotations.Nullable;
14+
15+
import java.util.ArrayDeque;
16+
import java.util.HashMap;
17+
import java.util.Map;
18+
import java.util.Queue;
19+
import java.util.logging.Level;
20+
import java.util.logging.Logger;
21+
22+
import static parallelmc.parallelworlds.ReflectionHelper.getPrivateField;
23+
324
public class ParallelBlockRegistry {
425

526
private static ParallelBlockRegistry instance = null;
27+
28+
private static int firstCustomId = 0;
29+
private final WritableRegistry<Block> blockRegistry;
30+
private final HashMap<Integer, Integer> stateMap;
31+
32+
// NOTE: If the resource pack can be automatically generated and served
33+
// this can be converted into map of BlockType -> Queue<Integer> since we can choose values entirely server side
34+
private final HashMap<BlockState, Integer> availableStates;
35+
36+
private boolean frozen = false;
637

7-
private ParallelBlockRegistry() {
38+
private ParallelBlockRegistry() throws RuntimeException {
39+
// This is a ridiculous hack to force internal blocks to register first
40+
Block dummy = Blocks.DIRT;
41+
Logger.getGlobal().log(Level.INFO, dummy.toString());
42+
43+
firstCustomId = Block.BLOCK_STATE_REGISTRY.size();
44+
45+
blockRegistry = getWritableRegistry(Registries.BLOCK);
46+
47+
if (blockRegistry == null) {
48+
throw new RuntimeException("Cannot find block registry");
49+
}
50+
51+
stateMap = new HashMap<>();
52+
availableStates = new HashMap<>();
53+
54+
// TODO: Fill available states
55+
for (BlockState state : Blocks.NOTE_BLOCK.getStateDefinition().getPossibleStates()) {
56+
if (state.equals(Blocks.NOTE_BLOCK.defaultBlockState())) continue; // This is the one actually used for rendering
57+
58+
addFullBlock(state);
59+
}
860

961
}
1062

63+
private void addFullBlock(BlockState state) {
64+
65+
if (availableStates.containsKey(state)) throw new IllegalStateException("Cannot add state that already exists");
66+
67+
availableStates.put(state, Block.BLOCK_STATE_REGISTRY.getId(state));
68+
}
69+
1170
public static ParallelBlockRegistry getInstance() {
1271
if (instance == null) {
1372
instance = new ParallelBlockRegistry();
1473
}
15-
74+
1675
return instance;
1776
}
77+
78+
// Call this at end of bootstrap to prevent adding new blocks to the registry
79+
public void freeze() {
80+
frozen = true;
81+
}
82+
83+
84+
// This is private since it can ONLY be run during bootstrap or things break real bad
85+
@Nullable
86+
private static <T> WritableRegistry<@NotNull T> getWritableRegistry(ResourceKey<@NotNull Registry<@NotNull T>> registryKey) {
87+
88+
// Get the global WRITABLE_REGISTRY registry
89+
MappedRegistry<@NotNull WritableRegistry<?>> writable_registry = (MappedRegistry<WritableRegistry<?>>) getPrivateField("WRITABLE_REGISTRY", BuiltInRegistries.class, null, WritableRegistry.class);
90+
91+
// Search registry byValue (since byKey doesn't work for whatever reason...)
92+
Map<WritableRegistry<?>, Holder.Reference<@NotNull WritableRegistry<?>>> byValue = getPrivateField("byValue", MappedRegistry.class, writable_registry, Map.class);
93+
94+
for (WritableRegistry i : byValue.keySet()) {
95+
if (String.valueOf(i.key().identifier()).equals(String.valueOf(registryKey.identifier()))) {
96+
return i;
97+
}
98+
}
99+
100+
return null;
101+
}
102+
103+
public boolean registerBlock(ResourceKey<@NotNull Block> key, Block block, BlockState targetBlockstate) {
104+
if (frozen) return false;
105+
106+
if (!availableStates.containsKey(targetBlockstate)) throw new IllegalStateException("Block state is already used or does not exist");
107+
108+
Holder.Reference<Block> registeredBlock = blockRegistry.register(key, block, RegistrationInfo.BUILT_IN);
109+
110+
// TODO: This technically doesn't work since several blockstates are mapped to a single one, but it's easier for now
111+
for (BlockState blockState : registeredBlock.value().getStateDefinition().getPossibleStates()) {
112+
113+
Integer stateId = availableStates.remove(targetBlockstate);
114+
115+
stateMap.put(Block.BLOCK_STATE_REGISTRY.size(), stateId); // Map the new block state to an unused state
116+
117+
Block.BLOCK_STATE_REGISTRY.add(blockState);
118+
119+
blockState.initCache();
120+
}
121+
122+
return true;
123+
}
124+
125+
public static int getFirstCustomId() {
126+
return firstCustomId;
127+
}
128+
129+
@Nullable
130+
public Integer getMappedState(int state) {
131+
return stateMap.get(state);
132+
}
18133
}

0 commit comments

Comments
 (0)