Skip to content

Commit dbd061d

Browse files
committed
Finish game and entity components
1 parent 4c313f1 commit dbd061d

File tree

13 files changed

+236
-86
lines changed

13 files changed

+236
-86
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.noxcrew.noxesium.api.fabric.component;
2+
3+
import org.jetbrains.annotations.Nullable;
4+
5+
/**
6+
* Stores the old and new values of a component being changed along with
7+
* the receiver of the new value.
8+
*/
9+
public record ComponentChangeContext<T, R>(@Nullable T oldValue, @Nullable T newValue, R receiver) {}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package com.noxcrew.noxesium.api.fabric.component;
2+
3+
import java.lang.ref.WeakReference;
4+
import java.util.Set;
5+
import java.util.concurrent.ConcurrentHashMap;
6+
import java.util.function.BiConsumer;
7+
import org.apache.commons.lang3.tuple.Pair;
8+
9+
/**
10+
* An object that can be listened to when a component updates.
11+
*/
12+
public class NoxesiumComponentListener<T, R> {
13+
14+
/**
15+
* All listeners registered to this payload type.
16+
*/
17+
private final Set<Pair<WeakReference<?>, BiConsumer<?, ComponentChangeContext<T, R>>>> listeners =
18+
ConcurrentHashMap.newKeySet();
19+
20+
/**
21+
* Registers a new listener to a change of this component type. Garbage collection
22+
* of this listener is tied to the lifetime of the reference. The reference object should
23+
* only ever be referenced from within the listener using the passed instance. This prevents
24+
* the listener from holding its own reference captive. If you do this the listener
25+
* will never be properly garbage collected.
26+
*/
27+
public <F> void addListener(F reference, BiConsumer<F, ComponentChangeContext<T, R>> listener) {
28+
listeners.removeIf((it) -> it.getKey().get() == null);
29+
listeners.add(Pair.of(new WeakReference<>(reference), listener));
30+
}
31+
32+
/**
33+
* Returns whether this type has listeners.
34+
*/
35+
public boolean hasListeners() {
36+
return !listeners.isEmpty();
37+
}
38+
39+
/**
40+
* Triggers this listener with the given receiver and values.
41+
*/
42+
public void trigger(Object receiver, Object oldValue, Object newValue) {
43+
var iterator = listeners.iterator();
44+
var context = new ComponentChangeContext<T, R>((T) oldValue, (T) newValue, (R) receiver);
45+
while (iterator.hasNext()) {
46+
var pair = iterator.next();
47+
var obj = pair.getKey().get();
48+
if (obj == null) {
49+
iterator.remove();
50+
continue;
51+
}
52+
acceptAny(pair.getValue(), obj, context);
53+
}
54+
}
55+
56+
/**
57+
* Casts [reference] to type [F] of [consumer].
58+
*/
59+
private <F> void acceptAny(
60+
BiConsumer<F, ComponentChangeContext<T, R>> consumer,
61+
Object reference,
62+
ComponentChangeContext<T, R> context) {
63+
consumer.accept((F) reference, context);
64+
}
65+
}

fabric/api/src/main/java/com/noxcrew/noxesium/api/fabric/component/NoxesiumComponentType.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@
2121
* Component types are defined in a custom registry similar to vanilla components.
2222
*/
2323
public record NoxesiumComponentType<T>(
24-
ResourceLocation id, Codec<T> codec, @Nullable StreamCodec<? super RegistryFriendlyByteBuf, T> streamCodec) {
24+
ResourceLocation id,
25+
Codec<T> codec,
26+
@Nullable StreamCodec<? super RegistryFriendlyByteBuf, T> streamCodec,
27+
@Nullable NoxesiumComponentListener<T, ?> listener) {
2528

2629
/**
2730
* Creates a new component type automatically for the given namespace and key with the default encoder cache enabled.
@@ -30,10 +33,12 @@ public NoxesiumComponentType(
3033
String namespace,
3134
String key,
3235
Codec<T> codec,
33-
@Nullable StreamCodec<? super RegistryFriendlyByteBuf, T> streamCodec) {
36+
@Nullable StreamCodec<? super RegistryFriendlyByteBuf, T> streamCodec,
37+
@Nullable NoxesiumComponentListener<T, ?> listener) {
3438
this(
3539
ResourceLocation.fromNamespaceAndPath(namespace, key),
3640
DataComponents.ENCODER_CACHE.wrap(codec),
37-
streamCodec);
41+
streamCodec,
42+
listener);
3843
}
3944
}

fabric/api/src/main/java/com/noxcrew/noxesium/api/fabric/network/NoxesiumPacketHandler.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ public class NoxesiumPacketHandler<T extends CustomPacketPayload>
1717
@Override
1818
public void receive(T payload, ClientPlayNetworking.Context context) {
1919
if (payload instanceof NoxesiumPacket noxesiumPacket) {
20-
noxesiumPacket.noxesiumType().handle(new PacketContext(context.client(), context.player()), payload);
20+
if (noxesiumPacket.noxesiumType().hasListeners()) {
21+
noxesiumPacket.noxesiumType().handle(new PacketContext(context.client(), context.player()), payload);
22+
}
2123
if (NoxesiumNetworking.dumpIncomingPackets) {
2224
Minecraft.getInstance()
2325
.player

fabric/api/src/main/java/com/noxcrew/noxesium/api/fabric/network/NoxesiumPayloadType.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,13 @@ private static void unregisterPacket(PayloadTypeRegistry<RegistryFriendlyByteBuf
8888
((PayloadTypeRegistryExt) registry).getPacketTypes().remove(id);
8989
}
9090

91+
/**
92+
* Returns whether this type has listeners.
93+
*/
94+
public boolean hasListeners() {
95+
return !listeners.isEmpty();
96+
}
97+
9198
/**
9299
* Handles a new packet [payload] of this type being received with
93100
* [context].

fabric/api/src/main/resources/noxesium.accesswidener

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@ accessible method net/minecraft/client/renderer/RenderPipelines register (Lcom/m
2020
accessible field net/minecraft/client/renderer/RenderPipelines TEXT_SNIPPET Lcom/mojang/blaze3d/pipeline/RenderPipeline$Snippet;
2121
accessible field net/minecraft/client/renderer/RenderPipelines LINES_SNIPPET Lcom/mojang/blaze3d/pipeline/RenderPipeline$Snippet;
2222

23-
accessible field net/minecraft/core/component/DataComponents ENCODER_CACHE Lnet/minecraft/util/EncoderCache;
23+
accessible field net/minecraft/core/component/DataComponents ENCODER_CACHE Lnet/minecraft/util/EncoderCache;
24+
accessible method net/minecraft/world/entity/Entity makeBoundingBox ()Lnet/minecraft/world/phys/AABB;

fabric/common/src/main/java/com/noxcrew/noxesium/fabric/CommonNoxesiumEntrypoint.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import com.noxcrew.noxesium.fabric.feature.sounds.NoxesiumSoundModule;
1414
import com.noxcrew.noxesium.fabric.network.CommonPackets;
1515
import com.noxcrew.noxesium.fabric.network.NoxesiumPacketHandling;
16+
import com.noxcrew.noxesium.fabric.registry.ComponentChangeListeners;
1617
import java.net.URL;
1718
import java.util.ArrayList;
1819
import java.util.Collection;
@@ -50,10 +51,11 @@ public Collection<NoxesiumFeature> getAllFeatures() {
5051
features.add(new SkullFontModule());
5152
features.add(new NoxesiumSoundModule());
5253
features.add(new TeamGlowHotkeys());
53-
features.add(new NoxesiumPacketHandling());
5454
features.add(new QibBehaviorModule());
5555
features.add(new SpatialDebuggingRenderer());
5656
features.add(new CustomServerCreativeItems());
57+
features.add(new NoxesiumPacketHandling());
58+
features.add(new ComponentChangeListeners());
5759
return features;
5860
}
5961

fabric/common/src/main/java/com/noxcrew/noxesium/fabric/mixin/rules/entity/EntityComponentMixin.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ public abstract class EntityComponentMixin implements NoxesiumComponentHolder {
3333

3434
@Override
3535
public void noxesium$loadComponent(NoxesiumComponentType<?> component, Object value) {
36+
if (value == null) {
37+
noxesium$unsetComponent(component);
38+
return;
39+
}
3640
if (noxesium$components == null) noxesium$components = new ConcurrentHashMap<>();
3741
noxesium$components.put(component, value);
3842
}

fabric/common/src/main/java/com/noxcrew/noxesium/fabric/mixin/rules/server/MinecraftComponentMixin.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ public class MinecraftComponentMixin implements NoxesiumComponentHolder {
3434

3535
@Override
3636
public void noxesium$loadComponent(NoxesiumComponentType<?> component, Object value) {
37+
if (value == null) {
38+
noxesium$unsetComponent(component);
39+
return;
40+
}
3741
if (noxesium$components == null) noxesium$components = new ConcurrentHashMap<>();
3842
noxesium$components.put(component, value);
3943
}

fabric/common/src/main/java/com/noxcrew/noxesium/fabric/network/NoxesiumComponentPatch.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,9 +182,22 @@ public void apply(NoxesiumComponentHolder holder) {
182182
var key = entry.getKey();
183183
var value = entry.getValue();
184184
if (value.isEmpty()) {
185-
holder.noxesium$unsetComponent(key);
185+
if (key.listener() != null && key.listener().hasListeners()) {
186+
var oldValue = holder.noxesium$getComponent(key);
187+
holder.noxesium$unsetComponent(key);
188+
key.listener().trigger(holder, oldValue, null);
189+
} else {
190+
holder.noxesium$unsetComponent(key);
191+
}
186192
} else {
187-
holder.noxesium$loadComponent(key, value.get());
193+
if (key.listener() != null && key.listener().hasListeners()) {
194+
var oldValue = holder.noxesium$getComponent(key);
195+
var newValue = value.orElse(null);
196+
holder.noxesium$loadComponent(key, newValue);
197+
key.listener().trigger(holder, oldValue, newValue);
198+
} else {
199+
holder.noxesium$loadComponent(key, value.get());
200+
}
188201
}
189202
}
190203
}

0 commit comments

Comments
 (0)