Add util record for describing chunk pos in a level.
Adjust block pos one to match (also use a weak level ref).
This commit is contained in:
@@ -4,23 +4,21 @@ package li.cil.oc2.common.bus;
|
||||
|
||||
import li.cil.oc2.api.bus.BlockDeviceBusElement;
|
||||
import li.cil.oc2.api.bus.DeviceBusElement;
|
||||
import li.cil.oc2.common.util.ChunkLocation;
|
||||
import li.cil.oc2.common.util.ChunkUtils;
|
||||
import li.cil.oc2.common.util.ServerScheduler;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
|
||||
public final class BlockEntityDeviceBusController extends CommonDeviceBusController {
|
||||
private final Runnable onBusChunkLoadedStateChanged = this::scheduleBusScan;
|
||||
private final HashSet<TrackedChunk> trackedChunks = new HashSet<>();
|
||||
private final HashSet<ChunkLocation> trackedChunks = new HashSet<>();
|
||||
private final BlockEntity blockEntity;
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
@@ -35,10 +33,9 @@ public final class BlockEntityDeviceBusController extends CommonDeviceBusControl
|
||||
@Override
|
||||
public void setDeviceContainersChanged() {
|
||||
super.setDeviceContainersChanged();
|
||||
for (final TrackedChunk trackedChunk : trackedChunks) {
|
||||
trackedChunk.tryGetLevel().ifPresent(level -> {
|
||||
ChunkUtils.setLazyUnsaved(level, trackedChunk.position());
|
||||
});
|
||||
for (final ChunkLocation trackedChunk : trackedChunks) {
|
||||
trackedChunk.tryGetLevel().ifPresent(level ->
|
||||
ChunkUtils.setLazyUnsaved(level, trackedChunk.position()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,30 +58,30 @@ public final class BlockEntityDeviceBusController extends CommonDeviceBusControl
|
||||
return;
|
||||
}
|
||||
|
||||
final HashSet<TrackedChunk> newTrackedChunks = new HashSet<>();
|
||||
final HashSet<ChunkLocation> newTrackedChunks = new HashSet<>();
|
||||
for (final DeviceBusElement element : getElements()) {
|
||||
if (element instanceof final BlockDeviceBusElement blockElement) {
|
||||
final LevelAccessor elementLevel = blockElement.getLevel();
|
||||
final BlockPos elementPosition = blockElement.getPosition();
|
||||
if (elementLevel != null) {
|
||||
newTrackedChunks.add(TrackedChunk.of(elementLevel, elementPosition));
|
||||
newTrackedChunks.add(TrackedChunk.of(elementLevel, elementPosition.relative(Direction.NORTH)));
|
||||
newTrackedChunks.add(TrackedChunk.of(elementLevel, elementPosition.relative(Direction.EAST)));
|
||||
newTrackedChunks.add(TrackedChunk.of(elementLevel, elementPosition.relative(Direction.SOUTH)));
|
||||
newTrackedChunks.add(TrackedChunk.of(elementLevel, elementPosition.relative(Direction.WEST)));
|
||||
newTrackedChunks.add(ChunkLocation.of(elementLevel, elementPosition));
|
||||
newTrackedChunks.add(ChunkLocation.of(elementLevel, elementPosition.relative(Direction.NORTH)));
|
||||
newTrackedChunks.add(ChunkLocation.of(elementLevel, elementPosition.relative(Direction.EAST)));
|
||||
newTrackedChunks.add(ChunkLocation.of(elementLevel, elementPosition.relative(Direction.SOUTH)));
|
||||
newTrackedChunks.add(ChunkLocation.of(elementLevel, elementPosition.relative(Direction.WEST)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do not track the chunk the controller itself is in -- this is unneeded because
|
||||
// we expect the controller to be disposed if its chunk is unloaded.
|
||||
newTrackedChunks.remove(TrackedChunk.of(level, blockEntity.getBlockPos()));
|
||||
newTrackedChunks.remove(ChunkLocation.of(level, blockEntity.getBlockPos()));
|
||||
|
||||
final HashSet<TrackedChunk> removedChunks = new HashSet<>(trackedChunks);
|
||||
final HashSet<ChunkLocation> removedChunks = new HashSet<>(trackedChunks);
|
||||
removedChunks.removeAll(newTrackedChunks);
|
||||
removeListeners(removedChunks);
|
||||
|
||||
final HashSet<TrackedChunk> addedChunks = new HashSet<>(newTrackedChunks);
|
||||
final HashSet<ChunkLocation> addedChunks = new HashSet<>(newTrackedChunks);
|
||||
newTrackedChunks.removeAll(trackedChunks);
|
||||
addListeners(addedChunks);
|
||||
|
||||
@@ -94,31 +91,21 @@ public final class BlockEntityDeviceBusController extends CommonDeviceBusControl
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private void addListeners(final Collection<TrackedChunk> trackedChunks) {
|
||||
for (final TrackedChunk trackedChunk : trackedChunks) {
|
||||
private void addListeners(final Collection<ChunkLocation> trackedChunks) {
|
||||
for (final ChunkLocation trackedChunk : trackedChunks) {
|
||||
trackedChunk.tryGetLevel().ifPresent(level -> {
|
||||
ServerScheduler.scheduleOnLoad(level, trackedChunk.position, onBusChunkLoadedStateChanged);
|
||||
ServerScheduler.scheduleOnUnload(level, trackedChunk.position, onBusChunkLoadedStateChanged);
|
||||
ServerScheduler.scheduleOnLoad(level, trackedChunk.position(), onBusChunkLoadedStateChanged);
|
||||
ServerScheduler.scheduleOnUnload(level, trackedChunk.position(), onBusChunkLoadedStateChanged);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void removeListeners(final Collection<TrackedChunk> trackedChunks) {
|
||||
for (final TrackedChunk trackedChunk : trackedChunks) {
|
||||
private void removeListeners(final Collection<ChunkLocation> trackedChunks) {
|
||||
for (final ChunkLocation trackedChunk : trackedChunks) {
|
||||
trackedChunk.tryGetLevel().ifPresent(level -> {
|
||||
ServerScheduler.cancelOnLoad(level, trackedChunk.position, onBusChunkLoadedStateChanged);
|
||||
ServerScheduler.cancelOnUnload(level, trackedChunk.position, onBusChunkLoadedStateChanged);
|
||||
ServerScheduler.cancelOnLoad(level, trackedChunk.position(), onBusChunkLoadedStateChanged);
|
||||
ServerScheduler.cancelOnUnload(level, trackedChunk.position(), onBusChunkLoadedStateChanged);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private record TrackedChunk(WeakReference<LevelAccessor> level, ChunkPos position) {
|
||||
public static TrackedChunk of(final LevelAccessor level, final BlockPos position) {
|
||||
return new TrackedChunk(new WeakReference<>(level), new ChunkPos(position));
|
||||
}
|
||||
|
||||
public Optional<LevelAccessor> tryGetLevel() {
|
||||
return Optional.ofNullable(level.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,14 +4,13 @@ package li.cil.oc2.common.bus.device.rpc.item;
|
||||
|
||||
import li.cil.oc2.api.bus.device.object.Callback;
|
||||
import li.cil.oc2.api.bus.device.object.Parameter;
|
||||
import li.cil.oc2.common.util.Location;
|
||||
import li.cil.oc2.common.util.BlockLocation;
|
||||
import li.cil.oc2.common.util.TickUtils;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@@ -27,12 +26,12 @@ public final class SoundCardItemDevice extends AbstractItemRPCDevice {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private final Supplier<Optional<Location>> location;
|
||||
private final Supplier<Optional<BlockLocation>> location;
|
||||
private long gameTimeCooldownExpiresAt;
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public SoundCardItemDevice(final ItemStack identity, final Supplier<Optional<Location>> location) {
|
||||
public SoundCardItemDevice(final ItemStack identity, final Supplier<Optional<BlockLocation>> location) {
|
||||
super(identity, "sound");
|
||||
this.location = location;
|
||||
}
|
||||
@@ -43,8 +42,7 @@ public final class SoundCardItemDevice extends AbstractItemRPCDevice {
|
||||
public void playSound(@Nullable @Parameter("name") final String name) {
|
||||
if (name == null) throw new IllegalArgumentException();
|
||||
|
||||
location.get().ifPresent(location -> {
|
||||
final LevelAccessor level = location.level();
|
||||
location.get().ifPresent(location -> location.tryGetLevel().ifPresent(level -> {
|
||||
if (!(level instanceof final ServerLevel serverLevel)) {
|
||||
return;
|
||||
}
|
||||
@@ -58,8 +56,8 @@ public final class SoundCardItemDevice extends AbstractItemRPCDevice {
|
||||
|
||||
final SoundEvent soundEvent = ForgeRegistries.SOUND_EVENTS.getValue(new ResourceLocation(name));
|
||||
if (soundEvent == null) throw new IllegalArgumentException("Sound not found.");
|
||||
level.playSound(null, location.pos(), soundEvent, SoundSource.BLOCKS, 1, 1);
|
||||
});
|
||||
level.playSound(null, location.blockPos(), soundEvent, SoundSource.BLOCKS, 1, 1);
|
||||
}));
|
||||
}
|
||||
|
||||
@Callback
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
package li.cil.oc2.common.bus.device.vm.item;
|
||||
|
||||
import li.cil.oc2.common.serialization.BlobStorage;
|
||||
import li.cil.oc2.common.util.Location;
|
||||
import li.cil.oc2.common.util.BlockLocation;
|
||||
import li.cil.oc2.common.util.SoundEvents;
|
||||
import li.cil.oc2.common.util.ThrottledSoundEmitter;
|
||||
import li.cil.sedna.device.block.ByteBufferBlockDevice;
|
||||
@@ -23,7 +23,7 @@ public class HardDriveDevice extends AbstractBlockStorageDevice<ByteBufferBlockD
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public HardDriveDevice(final ItemStack identity, final int size, final boolean readonly, final Supplier<Optional<Location>> location) {
|
||||
public HardDriveDevice(final ItemStack identity, final int size, final boolean readonly, final Supplier<Optional<BlockLocation>> location) {
|
||||
super(identity, readonly);
|
||||
this.size = size;
|
||||
this.soundEmitter = new ThrottledSoundEmitter(location, SoundEvents.HDD_ACCESS.get())
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
package li.cil.oc2.common.bus.device.vm.item;
|
||||
|
||||
import com.google.common.io.ByteStreams;
|
||||
import li.cil.oc2.common.util.Location;
|
||||
import li.cil.oc2.common.util.BlockLocation;
|
||||
import li.cil.sedna.api.device.BlockDevice;
|
||||
import li.cil.sedna.device.block.ByteBufferBlockDevice;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
@@ -20,7 +20,7 @@ public final class HardDriveDeviceWithInitialData extends HardDriveDevice {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public HardDriveDeviceWithInitialData(final ItemStack identity, final BlockDevice base, final boolean readonly, final Supplier<Optional<Location>> location) {
|
||||
public HardDriveDeviceWithInitialData(final ItemStack identity, final BlockDevice base, final boolean readonly, final Supplier<Optional<BlockLocation>> location) {
|
||||
super(identity, (int) base.getCapacity(), readonly, location);
|
||||
this.base = base;
|
||||
}
|
||||
|
||||
49
src/main/java/li/cil/oc2/common/util/BlockLocation.java
Normal file
49
src/main/java/li/cil/oc2/common/util/BlockLocation.java
Normal file
@@ -0,0 +1,49 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
package li.cil.oc2.common.util;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
public record BlockLocation(WeakReference<LevelAccessor> level, BlockPos blockPos) {
|
||||
public static Optional<BlockLocation> of(final Entity entity) {
|
||||
if (entity.isAlive()) {
|
||||
return Optional.of(new BlockLocation(new WeakReference<>(entity.level), entity.blockPosition()));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public static Optional<BlockLocation> of(final BlockEntity blockEntity) {
|
||||
if (!blockEntity.isRemoved()) {
|
||||
return Optional.of(new BlockLocation(new WeakReference<>(blockEntity.getLevel()), blockEntity.getBlockPos()));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public Optional<LevelAccessor> tryGetLevel() {
|
||||
return Optional.ofNullable(level.get());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (obj instanceof BlockLocation that) {
|
||||
final LevelAccessor thisLevel = level.get();
|
||||
final LevelAccessor thatLevel = that.level.get();
|
||||
return Objects.equals(thisLevel, thatLevel) && Objects.equals(blockPos, that.blockPos);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(level.get(), blockPos);
|
||||
}
|
||||
}
|
||||
40
src/main/java/li/cil/oc2/common/util/ChunkLocation.java
Normal file
40
src/main/java/li/cil/oc2/common/util/ChunkLocation.java
Normal file
@@ -0,0 +1,40 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
package li.cil.oc2.common.util;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
public record ChunkLocation(WeakReference<LevelAccessor> level, ChunkPos position) {
|
||||
public static ChunkLocation of(final LevelAccessor level, final BlockPos position) {
|
||||
return new ChunkLocation(new WeakReference<>(level), new ChunkPos(position));
|
||||
}
|
||||
|
||||
public static ChunkLocation of(final LevelAccessor level, final ChunkPos position) {
|
||||
return new ChunkLocation(new WeakReference<>(level), position);
|
||||
}
|
||||
|
||||
public Optional<LevelAccessor> tryGetLevel() {
|
||||
return Optional.ofNullable(level.get());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (obj instanceof ChunkLocation that) {
|
||||
final LevelAccessor thisLevel = level.get();
|
||||
final LevelAccessor thatLevel = that.level.get();
|
||||
return Objects.equals(thisLevel, thatLevel) && Objects.equals(position, that.position);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(level.get(), position);
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
package li.cil.oc2.common.util;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
public record Location(LevelAccessor level, BlockPos pos) {
|
||||
public static Optional<Location> of(final Entity entity) {
|
||||
if (entity.isAlive()) {
|
||||
return Optional.of(new Location(entity.level, entity.blockPosition()));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public static Optional<Location> of(final BlockEntity blockEntity) {
|
||||
if (!blockEntity.isRemoved()) {
|
||||
return Optional.of(new Location(Objects.requireNonNull(blockEntity.getLevel()), blockEntity.getBlockPos()));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,32 +7,33 @@ import li.cil.oc2.api.bus.device.provider.ItemDeviceQuery;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public final class LocationSupplierUtils {
|
||||
public static Supplier<Optional<Location>> of(final BlockEntity blockEntity) {
|
||||
return () -> Location.of(blockEntity);
|
||||
public static Supplier<Optional<BlockLocation>> of(final BlockEntity blockEntity) {
|
||||
return () -> BlockLocation.of(blockEntity);
|
||||
}
|
||||
|
||||
public static Supplier<Optional<Location>> of(final Entity entity) {
|
||||
return () -> Location.of(entity);
|
||||
public static Supplier<Optional<BlockLocation>> of(final Entity entity) {
|
||||
return () -> BlockLocation.of(entity);
|
||||
}
|
||||
|
||||
public static Supplier<Optional<Location>> of(final BlockDeviceQuery query) {
|
||||
final Optional<Location> location = Optional.of(new Location(query.getLevel(), query.getQueryPosition()));
|
||||
public static Supplier<Optional<BlockLocation>> of(final BlockDeviceQuery query) {
|
||||
final Optional<BlockLocation> location = Optional.of(new BlockLocation(new WeakReference<>(query.getLevel()), query.getQueryPosition()));
|
||||
return () -> location;
|
||||
}
|
||||
|
||||
public static Supplier<Optional<Location>> of(final ItemDeviceQuery query) {
|
||||
public static Supplier<Optional<BlockLocation>> of(final ItemDeviceQuery query) {
|
||||
final Optional<BlockEntity> blockEntity = query.getContainerBlockEntity();
|
||||
if (blockEntity.isPresent()) {
|
||||
return () -> Location.of(blockEntity.get());
|
||||
return () -> BlockLocation.of(blockEntity.get());
|
||||
}
|
||||
|
||||
final Optional<Entity> entity = query.getContainerEntity();
|
||||
if (entity.isPresent()) {
|
||||
return () -> Location.of(entity.get());
|
||||
return () -> BlockLocation.of(entity.get());
|
||||
}
|
||||
|
||||
return Optional::empty;
|
||||
|
||||
@@ -45,7 +45,9 @@ public final class ServerScheduler {
|
||||
}
|
||||
|
||||
public static void scheduleOnUnload(final LevelAccessor level, final Runnable listener) {
|
||||
levelUnloadSchedulers.computeIfAbsent(level, unused -> new SimpleScheduler()).add(listener);
|
||||
levelUnloadSchedulers
|
||||
.computeIfAbsent(level, unused -> new SimpleScheduler())
|
||||
.add(listener);
|
||||
}
|
||||
|
||||
public static void cancelOnUnload(@Nullable final LevelAccessor level, final Runnable listener) {
|
||||
|
||||
@@ -5,7 +5,6 @@ package li.cil.oc2.common.util;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Optional;
|
||||
@@ -13,7 +12,7 @@ import java.util.Random;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public final class ThrottledSoundEmitter {
|
||||
private final Supplier<Optional<Location>> location;
|
||||
private final Supplier<Optional<BlockLocation>> location;
|
||||
private final SoundEvent sound;
|
||||
private long minInterval;
|
||||
private SoundSource category;
|
||||
@@ -26,7 +25,7 @@ public final class ThrottledSoundEmitter {
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
public ThrottledSoundEmitter(final Supplier<Optional<Location>> location, final SoundEvent sound) {
|
||||
public ThrottledSoundEmitter(final Supplier<Optional<BlockLocation>> location, final SoundEvent sound) {
|
||||
this.location = location;
|
||||
this.sound = sound;
|
||||
this.category = SoundSource.BLOCKS;
|
||||
@@ -39,12 +38,11 @@ public final class ThrottledSoundEmitter {
|
||||
final long now = System.currentTimeMillis();
|
||||
if (now - lastEmittedTime > minInterval) {
|
||||
lastEmittedTime = now;
|
||||
this.location.get().ifPresent(location -> {
|
||||
final LevelAccessor level = location.level();
|
||||
this.location.get().ifPresent(location -> location.tryGetLevel().ifPresent(level -> {
|
||||
final float volume = sampleVolume(level.getRandom());
|
||||
final float pitch = samplePitch(level.getRandom());
|
||||
LevelUtils.playSound(level, location.pos(), sound, category, volume, pitch);
|
||||
});
|
||||
LevelUtils.playSound(level, location.blockPos(), sound, category, volume, pitch);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user