Skip to content

Commit a8ff3d1

Browse files
committed
Register Create block edits and add event hooks
Introduce a CreateBlockEdits registry to collect and apply BlockBuilder edits for Create blocks during AllBlocks bootstrapping (via NeoForge scan data and a @CreateBlockEdits.Registrator annotation). Add AllBlocksMixin to bootstrap the registrators and wrap CreateRegistrate.block invocations to apply edits by ID, and update azimuth.mixins.json to include the mixin. Extend AzimuthEvents to handle PlayerInteractEvent.RightClickBlock and BlockEvent.EntityPlaceEvent (and stop iterating behaviours if the event is canceled), and add corresponding no-op handlers on SuperBlockEntityBehaviour (onItemUse, onBlockPlaced). Also ensure existing block-broken behaviour respects event cancellation. These changes let mods modify Create block registrations and let super block behaviours react to item use and placement events.
1 parent f218502 commit a8ff3d1

5 files changed

Lines changed: 210 additions & 1 deletion

File tree

src/main/java/com/cake/azimuth/AzimuthEvents.java

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,52 @@
66
import net.minecraft.world.level.block.entity.BlockEntity;
77
import net.neoforged.bus.api.SubscribeEvent;
88
import net.neoforged.fml.common.EventBusSubscriber;
9+
import net.neoforged.neoforge.event.entity.player.PlayerInteractEvent;
910
import net.neoforged.neoforge.event.level.BlockEvent;
1011

1112
@EventBusSubscriber
1213
public class AzimuthEvents {
1314

1415
@SubscribeEvent
1516
public static void onPlayerWillDestroy(final BlockEvent.BreakEvent destroyEvent) {
16-
//Check if there is an asbee block entity there. Call onBlockBroken on each.
1717
final BlockPos pos = destroyEvent.getPos();
1818
final BlockEntity blockEntity = destroyEvent.getLevel().getBlockEntity(pos);
1919
if (blockEntity instanceof final AzimuthSmartBlockEntityExtension asbee) {
2020
for (final SuperBlockEntityBehaviour behaviour : asbee.azimuth$getSuperBehaviours()) {
2121
behaviour.onBlockBroken(destroyEvent);
22+
if (destroyEvent.isCanceled()) {
23+
break;
24+
}
2225
}
2326
}
2427
}
2528

29+
@SubscribeEvent
30+
public static void onItemUseOnBlock(final PlayerInteractEvent.RightClickBlock event) {
31+
final BlockPos pos = event.getPos();
32+
final BlockEntity blockEntity = event.getLevel().getBlockEntity(pos);
33+
if (blockEntity instanceof final AzimuthSmartBlockEntityExtension asbee) {
34+
for (final SuperBlockEntityBehaviour behaviour : asbee.azimuth$getSuperBehaviours()) {
35+
behaviour.onItemUse(event);
36+
if (event.isCanceled()) {
37+
break;
38+
}
39+
}
40+
}
41+
}
42+
43+
@SubscribeEvent
44+
public static void onBlockPlaced(final BlockEvent.EntityPlaceEvent event) {
45+
final BlockPos pos = event.getPos();
46+
final BlockEntity blockEntity = event.getLevel().getBlockEntity(pos);
47+
if (blockEntity instanceof final AzimuthSmartBlockEntityExtension asbee) {
48+
for (final SuperBlockEntityBehaviour behaviour : asbee.azimuth$getSuperBehaviours()) {
49+
behaviour.onBlockPlaced(event);
50+
if (event.isCanceled()) {
51+
break;
52+
}
53+
}
54+
}
55+
}
2656

2757
}

src/main/java/com/cake/azimuth/behaviour/SuperBlockEntityBehaviour.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import net.minecraft.world.level.Level;
1010
import net.minecraft.world.level.block.entity.BlockEntity;
1111
import net.minecraft.world.level.block.state.BlockState;
12+
import net.neoforged.neoforge.event.entity.player.PlayerInteractEvent;
1213
import net.neoforged.neoforge.event.level.BlockEvent;
1314
import org.jetbrains.annotations.NotNull;
1415
import org.jetbrains.annotations.Nullable;
@@ -276,6 +277,12 @@ public <T extends SuperBlockEntityBehaviour> T getSameBehaviourOrThrow(final Blo
276277
public void onBlockBroken(final BlockEvent.BreakEvent event) {
277278
}
278279

280+
public void onItemUse(final PlayerInteractEvent.RightClickBlock event) {
281+
}
282+
283+
public void onBlockPlaced(final BlockEvent.EntityPlaceEvent event) {
284+
}
285+
279286
//endregion
280287

281288
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.cake.azimuth.mixin;
2+
3+
import com.cake.azimuth.registration.CreateBlockEdits;
4+
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
5+
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
6+
import com.simibubi.create.AllBlocks;
7+
import com.simibubi.create.foundation.data.CreateRegistrate;
8+
import com.tterrag.registrate.builders.BlockBuilder;
9+
import com.tterrag.registrate.util.nullness.NonNullFunction;
10+
import org.spongepowered.asm.mixin.Mixin;
11+
import org.spongepowered.asm.mixin.injection.At;
12+
import org.spongepowered.asm.mixin.injection.Inject;
13+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
14+
15+
import java.util.function.Consumer;
16+
17+
@Mixin(AllBlocks.class)
18+
public class AllBlocksMixin {
19+
20+
@Inject(method = "<clinit>", at = @At("HEAD"))
21+
private static void azimuth$bootstrapBlockEdits(final CallbackInfo ci) {
22+
CreateBlockEdits.bootstrapRegistrators();
23+
}
24+
25+
@WrapOperation(method = "<clinit>", at = @At(value = "INVOKE", target = "Lcom/simibubi/create/foundation/data/CreateRegistrate;block(Ljava/lang/String;Lcom/tterrag/registrate/util/nullness/NonNullFunction;)Lcom/tterrag/registrate/builders/BlockBuilder;"))
26+
private static BlockBuilder azimuth$applyBlockEdits(final CreateRegistrate instance, final String s, final NonNullFunction nonNullFunction, final Operation<BlockBuilder> original) {
27+
final BlockBuilder builder = original.call(instance, s, nonNullFunction);
28+
29+
final Consumer<BlockBuilder<?, CreateRegistrate>> transform = CreateBlockEdits.getEditForId(s);
30+
if (transform != null) {
31+
transform.accept(builder);
32+
}
33+
34+
return builder;
35+
}
36+
37+
}
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
package com.cake.azimuth.registration;
2+
3+
import com.simibubi.create.foundation.data.CreateRegistrate;
4+
import com.tterrag.registrate.builders.BlockBuilder;
5+
import net.neoforged.fml.ModList;
6+
import net.neoforged.neoforgespi.language.ModFileScanData;
7+
8+
import java.lang.annotation.ElementType;
9+
import java.lang.annotation.Retention;
10+
import java.lang.annotation.RetentionPolicy;
11+
import java.lang.annotation.Target;
12+
import java.lang.reflect.Method;
13+
import java.lang.reflect.Modifier;
14+
import java.util.Arrays;
15+
import java.util.Comparator;
16+
import java.util.LinkedHashMap;
17+
import java.util.List;
18+
import java.util.Map;
19+
import java.util.Objects;
20+
import java.util.function.Consumer;
21+
22+
/**
23+
* Registry for block edits to be applied to Create blocks during registration.
24+
* Registrators are discovered and invoked via NeoForge scan data during Create's AllBlocks static initialization.
25+
*/
26+
public class CreateBlockEdits {
27+
28+
private static final Map<String, Consumer<BlockBuilder<?, CreateRegistrate>>> EDITS_BY_ID = new LinkedHashMap<>();
29+
private static RegistrationWindow registrationWindow = RegistrationWindow.NOT_STARTED;
30+
31+
public static synchronized void bootstrapRegistrators() {
32+
if (registrationWindow != RegistrationWindow.NOT_STARTED) {
33+
return;
34+
}
35+
36+
final ModList modList = ModList.get();
37+
if (modList == null) {
38+
throw new IllegalStateException("Cannot discover @CreateBlockEdits.Registrator methods before NeoForge ModList is available.");
39+
}
40+
41+
registrationWindow = RegistrationWindow.OPEN;
42+
try {
43+
discoverRegistrators(modList).forEach(CreateBlockEdits::invokeRegistrator);
44+
} finally {
45+
registrationWindow = RegistrationWindow.CLOSED;
46+
}
47+
}
48+
49+
public static synchronized void forBlock(final String id, final Consumer<BlockBuilder<?, CreateRegistrate>> edit) {
50+
if (registrationWindow != RegistrationWindow.OPEN) {
51+
throw new IllegalStateException("CreateBlockEdits.forBlock(...) can only be called from a @CreateBlockEdits.Registrator method while Create's AllBlocks are bootstrapping; current registration window is " + registrationWindow + ".");
52+
}
53+
54+
Objects.requireNonNull(id, "id");
55+
Objects.requireNonNull(edit, "edit");
56+
EDITS_BY_ID.merge(id, edit, (existing, additional) -> builder -> {
57+
existing.accept(builder);
58+
additional.accept(builder);
59+
});
60+
}
61+
62+
public static Consumer<BlockBuilder<?, CreateRegistrate>> getEditForId(final String id) {
63+
if (registrationWindow != RegistrationWindow.CLOSED) {
64+
throw new IllegalStateException("CreateBlockEdits.getEditForId(...) was called before registrators were fully discovered; current registration window is " + registrationWindow + ".");
65+
}
66+
67+
return EDITS_BY_ID.get(id);
68+
}
69+
70+
private static List<ModFileScanData.AnnotationData> discoverRegistrators(final ModList modList) {
71+
return modList.getAllScanData().stream()
72+
.flatMap(scanData -> scanData.getAnnotatedBy(Registrator.class, ElementType.METHOD))
73+
.sorted(Comparator.comparing((ModFileScanData.AnnotationData data) -> data.clazz().getClassName())
74+
.thenComparing(ModFileScanData.AnnotationData::memberName))
75+
.toList();
76+
}
77+
78+
private static void invokeRegistrator(final ModFileScanData.AnnotationData annotationData) {
79+
final Method registratorMethod = resolveRegistratorMethod(annotationData);
80+
try {
81+
registratorMethod.invoke(null);
82+
} catch (final ReflectiveOperationException e) {
83+
throw new IllegalStateException("Failed to invoke @CreateBlockEdits.Registrator method " + describe(annotationData) + ".", e);
84+
}
85+
}
86+
87+
private static Method resolveRegistratorMethod(final ModFileScanData.AnnotationData annotationData) {
88+
final Class<?> owner;
89+
try {
90+
final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
91+
owner = Class.forName(annotationData.clazz().getClassName(), false, contextClassLoader != null ? contextClassLoader : CreateBlockEdits.class.getClassLoader());
92+
} catch (final ClassNotFoundException e) {
93+
throw new IllegalStateException("Failed to load @CreateBlockEdits.Registrator owner " + describe(annotationData) + ".", e);
94+
}
95+
96+
final List<Method> registrators = Arrays.stream(owner.getDeclaredMethods())
97+
.filter(method -> method.getName().equals(annotationData.memberName()))
98+
.filter(method -> method.isAnnotationPresent(Registrator.class))
99+
.toList();
100+
if (registrators.size() != 1) {
101+
throw new IllegalStateException("Expected exactly one annotated @CreateBlockEdits.Registrator method for " + describe(annotationData) + ", but found " + registrators.size() + ".");
102+
}
103+
104+
final Method registratorMethod = registrators.get(0);
105+
if (!Modifier.isPublic(registratorMethod.getModifiers())
106+
|| !Modifier.isStatic(registratorMethod.getModifiers())
107+
|| registratorMethod.getParameterCount() != 0
108+
|| registratorMethod.getReturnType() != Void.TYPE
109+
|| !registratorMethod.getName().equals("register")) {
110+
throw new IllegalStateException("Invalid @CreateBlockEdits.Registrator method " + registratorMethod.toGenericString() + "; expected public static void register() with no arguments.");
111+
}
112+
113+
return registratorMethod;
114+
}
115+
116+
private static String describe(final ModFileScanData.AnnotationData annotationData) {
117+
return annotationData.clazz().getClassName() + "#" + annotationData.memberName();
118+
}
119+
120+
/**
121+
* Marks a public static void register() method with no arguments to be invoked while Create's AllBlocks are bootstrapping.
122+
*/
123+
@Retention(RetentionPolicy.RUNTIME)
124+
@Target(ElementType.METHOD)
125+
public @interface Registrator {
126+
}
127+
128+
private enum RegistrationWindow {
129+
NOT_STARTED,
130+
OPEN,
131+
CLOSED
132+
}
133+
134+
}

src/main/resources/azimuth.mixins.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"package": "com.cake.azimuth.mixin",
44
"compatibilityLevel": "JAVA_21",
55
"mixins": [
6+
"AllBlocksMixin",
67
"CachedRenderBBBlockEntityMixin",
78
"CreateAdvancementIdMixin",
89
"ItemRequirementMixin",

0 commit comments

Comments
 (0)