Skip to content

Commit f20987a

Browse files
committed
feat: improve compatibility with observable
In a part of working on #4165, there's several redirects and general incompatibilities that Sponge can introduce with other mods. Notably out of the box, there's Observable, which aims to provide per-chunk based observability of things that are occurring. It is not confirmed this alone is enough to make it work.
1 parent 60a813b commit f20987a

6 files changed

Lines changed: 200 additions & 179 deletions

File tree

src/main/java/org/spongepowered/common/event/tracking/TrackingUtil.java

Lines changed: 37 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
import net.minecraft.world.level.BlockEventData;
3434
import net.minecraft.world.level.block.Block;
3535
import net.minecraft.world.level.block.entity.BlockEntity;
36-
import net.minecraft.world.level.block.entity.TickingBlockEntity;
3736
import net.minecraft.world.level.material.FluidState;
3837
import org.apache.logging.log4j.Level;
3938
import org.apache.logging.log4j.Marker;
@@ -51,19 +50,13 @@
5150
import org.spongepowered.api.world.BlockChangeFlags;
5251
import org.spongepowered.api.world.LocatableBlock;
5352
import org.spongepowered.common.SpongeCommon;
54-
import org.spongepowered.common.accessor.world.level.chunk.LevelChunk$BoundTickingBlockEntityAccessor;
55-
import org.spongepowered.common.accessor.world.level.chunk.LevelChunk$RebindableTickingBlockEntityWrapperAccessor;
56-
import org.spongepowered.common.accessor.world.level.chunk.LevelChunkAccessor;
5753
import org.spongepowered.common.block.SpongeBlockSnapshot;
5854
import org.spongepowered.common.bridge.CreatorTrackedBridge;
5955
import org.spongepowered.common.bridge.TrackableBridge;
6056
import org.spongepowered.common.bridge.world.TrackedWorldBridge;
6157
import org.spongepowered.common.bridge.world.inventory.ViewableInventoryBridge;
6258
import org.spongepowered.common.bridge.world.level.TrackableBlockEventDataBridge;
63-
import org.spongepowered.common.bridge.world.level.block.entity.BlockEntityBridge;
64-
import org.spongepowered.common.bridge.world.level.chunk.ActiveChunkReferantBridge;
6559
import org.spongepowered.common.bridge.world.level.chunk.LevelChunkBridge;
66-
import org.spongepowered.common.bridge.world.level.chunk.TrackedLevelChunkBridge;
6760
import org.spongepowered.common.entity.PlayerTracker;
6861
import org.spongepowered.common.event.ShouldFire;
6962
import org.spongepowered.common.event.SpongeCommonEventFactory;
@@ -85,7 +78,7 @@
8578
import java.util.Optional;
8679
import java.util.Set;
8780
import java.util.UUID;
88-
import java.util.function.Consumer;
81+
import java.util.function.BooleanSupplier;
8982
import java.util.function.Supplier;
9083

9184
/**
@@ -103,7 +96,7 @@ public final class TrackingUtil {
10396

10497
public static final int WIDTH = 40;
10598

106-
public static void tickEntity(final Consumer<net.minecraft.world.entity.Entity> consumer, final net.minecraft.world.entity.Entity entity) {
99+
public static void tickEntity(final net.minecraft.world.entity.Entity entity, final Runnable tick) {
107100
Preconditions.checkArgument(entity instanceof Entity, () -> String.format("Entity %s is not an instance of SpongeAPI's Entity!", entity));
108101
Objects.requireNonNull(entity, "Cannot capture on a null ticking entity!");
109102
if (!((TrackableBridge) entity).bridge$shouldTick()) {
@@ -112,13 +105,13 @@ public static void tickEntity(final Consumer<net.minecraft.world.entity.Entity>
112105

113106
final EntityTickContext tickContext = TickPhase.Tick.ENTITY.createPhaseContext(PhaseTracker.SERVER).source(entity);
114107
try (final EntityTickContext context = tickContext) {
115-
if (entity instanceof CreatorTrackedBridge) {
116-
((CreatorTrackedBridge) entity).tracker$getNotifierUUID().ifPresent(context::notifier);
117-
((CreatorTrackedBridge) entity).tracker$getCreatorUUID().ifPresent(context::creator);
108+
if (entity instanceof CreatorTrackedBridge ctb) {
109+
ctb.tracker$getNotifierUUID().ifPresent(context::notifier);
110+
ctb.tracker$getCreatorUUID().ifPresent(context::creator);
118111
}
119112
context.buildAndSwitch();
120113
PhaseTracker.LOGGER.trace(TrackingUtil.ENTITY_TICK, () -> "Wrapping Entity Tick: " + entity.toString());
121-
consumer.accept(entity);
114+
tick.run();
122115
if (ShouldFire.MOVE_ENTITY_EVENT) {
123116
SpongeCommonEventFactory.callNaturalMoveEntityEvent(entity);
124117
}
@@ -130,78 +123,50 @@ public static void tickEntity(final Consumer<net.minecraft.world.entity.Entity>
130123
}
131124
}
132125

133-
private static Optional<net.minecraft.world.level.block.entity.BlockEntity> getTickingBlockEntity(
134-
final TickingBlockEntity ticker) {
135-
if (ticker instanceof LevelChunk$BoundTickingBlockEntityAccessor beAccessor) {
136-
return Optional.of(beAccessor.accessor$blockEntity());
137-
} else if (ticker instanceof LevelChunk$RebindableTickingBlockEntityWrapperAccessor beAccessor) {
138-
return getTickingBlockEntity(beAccessor.accessor$ticker());
139-
} else if (ticker == LevelChunkAccessor.accessor$NULL_TICKER()) {
140-
return Optional.empty();
141-
}
142-
return Optional.empty();
143-
}
144-
145126
@SuppressWarnings({"unused", "try"})
146-
public static void tickTileEntity(final TrackedWorldBridge mixinWorldServer, final TickingBlockEntity tile) {
147-
Objects.requireNonNull(tile, "Cannot capture on a null ticking tile entity!");
148-
final Optional<BlockEntity> tickingBlockEntity = getTickingBlockEntity(tile);
149-
if (!tickingBlockEntity.isPresent()) {
150-
return;
151-
}
152-
final net.minecraft.world.level.block.entity.BlockEntity blockEntity = tickingBlockEntity.get();
127+
public static void tickTileEntity(final BlockEntity blockEntity, final Runnable tick) {
128+
Objects.requireNonNull(blockEntity, "Cannot capture on a null ticking tile entity!");
153129
if (!((org.spongepowered.api.block.entity.BlockEntity) blockEntity).isTicking()) {
154130
return;
155131
}
156-
final BlockEntityBridge mixinTileEntity = (BlockEntityBridge) tickingBlockEntity.get();
157-
final BlockPos pos = blockEntity.getBlockPos();
158-
final @Nullable LevelChunkBridge chunk = ((ActiveChunkReferantBridge) blockEntity).bridge$getActiveChunk();
159132
if (!((TrackableBridge) blockEntity).bridge$shouldTick()) {
160133
return;
161134
}
162-
if (chunk == null) {
163-
((ActiveChunkReferantBridge) blockEntity).bridge$setActiveChunk((TrackedLevelChunkBridge) blockEntity.getLevel().getChunkAt(blockEntity.getBlockPos()));
164-
}
165135

166-
final TileEntityTickContext context = TickPhase.Tick.TILE_ENTITY.createPhaseContext(PhaseTracker.SERVER).source(mixinTileEntity);
136+
final TileEntityTickContext context = TickPhase.Tick.TILE_ENTITY.createPhaseContext(PhaseTracker.SERVER).source(blockEntity);
167137
try (final PhaseContext<@NonNull ?> phaseContext = context) {
168-
169-
if (blockEntity instanceof CreatorTrackedBridge) {
138+
if (blockEntity instanceof CreatorTrackedBridge ctb) {
170139
// Add notifier and owner so we don't have to perform lookups during the phases and other processing
171-
((CreatorTrackedBridge) blockEntity).tracker$getNotifierUUID().ifPresent(phaseContext::notifier);
140+
ctb.tracker$getNotifierUUID().ifPresent(phaseContext::notifier);
172141
// Allow the tile entity to validate the owner of itself. As long as the tile entity
173142
// chunk is already loaded and activated, and the tile entity has already loaded
174143
// the owner of itself.
175-
((CreatorTrackedBridge) blockEntity).tracker$getCreatorUUID().ifPresent(phaseContext::creator);
144+
ctb.tracker$getCreatorUUID().ifPresent(phaseContext::creator);
176145
}
177146

178147
// Finally, switch the context now that we have the owner and notifier
179148
phaseContext.buildAndSwitch();
180149

181-
PhaseTracker.LOGGER.trace(TrackingUtil.BLOCK_ENTITY_TICK, () -> "Wrapping Entity Tick: " + tile.toString());
182-
tile.tick();
150+
PhaseTracker.LOGGER.trace(TrackingUtil.BLOCK_ENTITY_TICK, () -> "Wrapping Entity Tick: " + blockEntity);
151+
tick.run();
183152

184153
// If we know the viewers force broadcast now to associate the inventory change with its blockentity
185154
// otherwise the viewing players update this during their ticking
186-
if (blockEntity instanceof ViewableInventoryBridge) {
187-
final Set<ServerPlayer> players = ((ViewableInventoryBridge) blockEntity).viewableBridge$getViewers();
188-
if (players.size() > 0) {
155+
if (blockEntity instanceof ViewableInventoryBridge vib) {
156+
final Set<ServerPlayer> players = vib.viewableBridge$getViewers();
157+
if (!players.isEmpty()) {
189158
players.forEach(player -> player.containerMenu.broadcastChanges());
190159
}
191160
}
192161

193162
} catch (final Exception e) {
194163
PhasePrinter.printExceptionFromPhase(PhaseTracker.getInstance().stack, e, context);
195164
}
196-
// We delay clearing active chunk if TE is invalidated during tick so we must remove it after
197-
if (blockEntity.isRemoved()) {
198-
((ActiveChunkReferantBridge) blockEntity).bridge$setActiveChunk(null);
199-
}
200165
}
201166

202-
@SuppressWarnings("rawtypes")
203167
public static void updateTickBlock(
204-
final TrackedWorldBridge mixinWorld, final net.minecraft.world.level.block.state.BlockState block, final BlockPos pos, final RandomSource random) {
168+
final TrackedWorldBridge mixinWorld, final net.minecraft.world.level.block.state.BlockState block,
169+
final BlockPos pos, final Runnable tick) {
205170
final ServerLevel world = (ServerLevel) mixinWorld;
206171
final org.spongepowered.api.world.server.ServerWorld apiWorld = (org.spongepowered.api.world.server.ServerWorld) world;
207172

@@ -225,15 +190,15 @@ public static void updateTickBlock(
225190
try (final PhaseContext<@NonNull ?> context = phaseContext) {
226191
context.buildAndSwitch();
227192
PhaseTracker.LOGGER.trace(TrackingUtil.BLOCK_TICK, () -> "Wrapping Block Tick: " + block.toString());
228-
block.tick(world, pos, random);
193+
tick.run();
229194
} catch (final Exception | NoClassDefFoundError e) {
230195
PhasePrinter.printExceptionFromPhase(PhaseTracker.getInstance().stack, e, phaseContext);
231196

232197
}
233198
}
234199

235200
public static void updateTickFluid(
236-
final TrackedWorldBridge mixinWorld, final FluidState fluidState, final BlockPos pos
201+
final TrackedWorldBridge mixinWorld, final FluidState fluidState, final BlockPos pos, final Runnable tick
237202
) {
238203
final ServerLevel world = (ServerLevel) mixinWorld;
239204
final org.spongepowered.api.world.server.ServerWorld apiWorld = (org.spongepowered.api.world.server.ServerWorld) world;
@@ -261,16 +226,18 @@ public static void updateTickFluid(
261226
try (final PhaseContext<?> context = phaseContext) {
262227
context.buildAndSwitch();
263228
PhaseTracker.LOGGER.trace(TrackingUtil.FLUID_TICK, () -> "Wrapping Fluid Tick: " + fluidState.toString());
264-
fluidState.tick(world, pos);
229+
tick.run();
265230
} catch (final Exception | NoClassDefFoundError e) {
266231
PhasePrinter.printExceptionFromPhase(PhaseTracker.getInstance().stack, e, phaseContext);
267232

268233
}
269234
}
270235

271236
@SuppressWarnings("rawtypes")
272-
public static void randomTickBlock(final TrackedWorldBridge mixinWorld,
273-
final net.minecraft.world.level.block.state.BlockState state, final BlockPos pos, final RandomSource random) {
237+
public static void randomTickBlock(
238+
final TrackedWorldBridge mixinWorld, final net.minecraft.world.level.block.state.BlockState state,
239+
final BlockPos pos, final RandomSource random, final Runnable tick
240+
) {
274241
final ServerLevel world = (ServerLevel) mixinWorld;
275242
final org.spongepowered.api.world.server.ServerWorld apiWorld = (org.spongepowered.api.world.server.ServerWorld) world;
276243

@@ -299,14 +266,17 @@ public static void randomTickBlock(final TrackedWorldBridge mixinWorld,
299266
try (final PhaseContext<@NonNull ?> context = phaseContext) {
300267
context.buildAndSwitch();
301268
PhaseTracker.LOGGER.trace(TrackingUtil.BLOCK_TICK, "Wrapping Random Block Tick: {}", state);
302-
state.randomTick(world, pos, random);
269+
tick.run();
303270
} catch (final Exception | NoClassDefFoundError e) {
304271
PhasePrinter.printExceptionFromPhase(PhaseTracker.getInstance().stack, e, phaseContext);
305272
}
306273
}
307274
@SuppressWarnings("rawtypes")
308-
public static void randomTickFluid(final TrackedWorldBridge mixinWorld,
309-
final FluidState state, final BlockPos pos, final RandomSource random) {
275+
public static void randomTickFluid(
276+
final TrackedWorldBridge mixinWorld,
277+
final FluidState state, final BlockPos pos, final RandomSource random,
278+
final Runnable tick
279+
) {
310280
final ServerLevel world = (ServerLevel) mixinWorld;
311281
final org.spongepowered.api.world.server.ServerWorld apiWorld = (org.spongepowered.api.world.server.ServerWorld) world;
312282

@@ -338,20 +308,20 @@ public static void randomTickFluid(final TrackedWorldBridge mixinWorld,
338308
try (final PhaseContext<@NonNull ?> context = phaseContext) {
339309
context.buildAndSwitch();
340310
PhaseTracker.LOGGER.trace(TrackingUtil.FLUID_TICK, () -> "Wrapping Random Fluid Tick: " + state.toString());
341-
state.randomTick(world, pos, random);
311+
tick.run();
342312
} catch (final Exception | NoClassDefFoundError e) {
343313
PhasePrinter.printExceptionFromPhase(PhaseTracker.getInstance().stack, e, phaseContext);
344314
}
345315
}
346316

347-
public static boolean fireMinecraftBlockEvent(final ServerLevel worldIn, final BlockEventData event,
348-
final net.minecraft.world.level.block.state.BlockState currentState
317+
public static boolean fireMinecraftBlockEvent(
318+
final BlockEventData event, final BooleanSupplier tick
349319
) {
350320
final TrackableBlockEventDataBridge blockEvent = (TrackableBlockEventDataBridge) (Object) event;
351321
final @Nullable Object source = blockEvent.bridge$getTileEntity() != null ? blockEvent.bridge$getTileEntity() : blockEvent.bridge$getTickingLocatable();
352322
if (source == null) {
353323
// No source present which means we are ignoring the phase state
354-
return currentState.triggerEvent(worldIn, event.pos(), event.paramA(), event.paramB());
324+
return tick.getAsBoolean();
355325
}
356326
final BlockEventTickContext phaseContext = TickPhase.Tick.BLOCK_EVENT.createPhaseContext(PhaseTracker.SERVER);
357327
phaseContext.source(source);
@@ -365,7 +335,7 @@ public static boolean fireMinecraftBlockEvent(final ServerLevel worldIn, final B
365335
boolean result = true;
366336
try (final BlockEventTickContext o = phaseContext) {
367337
o.buildAndSwitch();
368-
phaseContext.setEventSucceeded(currentState.triggerEvent(worldIn, event.pos(), event.paramA(), event.paramB()));
338+
phaseContext.setEventSucceeded(tick.getAsBoolean());
369339
// We need to grab the result here as the phase context close will trigger a reset
370340
result = phaseContext.wasNotCancelled();
371341
} // We can't return onBlockEventReceived because the phase state may have cancelled all transactions

src/main/java/org/spongepowered/common/event/tracking/phase/tick/TileEntityTickContext.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,7 @@ public TileEntityTickContext(final IPhaseState<TileEntityTickContext> phaseState
3636
@Override
3737
public TileEntityTickContext source(final Object owner) {
3838
super.source(owner);
39-
if (owner instanceof TrackableBridge) {
40-
final TrackableBridge mixinTileentity = (TrackableBridge) owner;
39+
if (owner instanceof TrackableBridge mixinTileentity) {
4140
this.setBlockEvents(mixinTileentity.bridge$allowsBlockEventCreation())
4241
.setBulkBlockCaptures(mixinTileentity.bridge$allowsBlockBulkCaptures())
4342
.setEntitySpawnEvents(mixinTileentity.bridge$allowsEntityEventCreation())

0 commit comments

Comments
 (0)