Merge fixes

This commit is contained in:
Sebastian Hartte
2020-08-11 00:09:08 +02:00
121 changed files with 1684 additions and 1995 deletions
+15 -13
View File
@@ -28,6 +28,7 @@ import net.minecraft.block.Blocks;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.network.packet.s2c.play.ChunkDataS2CPacket;
import net.minecraft.server.world.ServerChunkManager;
import net.minecraft.server.world.ServerLightingProvider;
import net.minecraft.server.world.ServerTickScheduler;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.Tickable;
@@ -46,6 +47,7 @@ import appeng.api.util.WorldCoord;
import appeng.core.AELog;
import appeng.core.Api;
import appeng.core.worlddata.WorldData;
import net.minecraft.world.chunk.light.LightingProvider;
public class CachedPlane {
private final int x_size;
@@ -60,13 +62,13 @@ public class CachedPlane {
private final Column[][] myColumns;
private final List<BlockEntity> tiles = new ArrayList<>();
private final List<ScheduledTick<Block>> ticks = new ArrayList<>();
private final World world;
private final ServerWorld world;
private final IMovableRegistry reg = Api.instance().registries().movable();
private final List<WorldCoord> updates = new ArrayList<>();
private int verticalBits;
private final BlockState matrixBlockState;
public CachedPlane(final World w, final int minX, final int minY, final int minZ, final int maxX, final int maxY,
public CachedPlane(final ServerWorld w, final int minX, final int minY, final int minZ, final int maxX, final int maxY,
final int maxZ) {
Block matrixFrameBlock = Api.instance().definitions().blocks().matrixFrame().maybeBlock().orElse(null);
@@ -301,12 +303,17 @@ public class CachedPlane {
private void updateChunks() {
LightingProvider lightManager = world.getLightingProvider();
// update shit..
for (int x = 0; x < this.cx_size; x++) {
for (int z = 0; z < this.cz_size; z++) {
final WorldChunk c = this.myChunks[x][z];
// FIXME: Light shit
c.markDirty();
if (lightManager instanceof ServerLightingProvider) {
ServerLightingProvider serverLightManager = (ServerLightingProvider) lightManager;
for (int x = 0; x < this.cx_size; x++) {
for (int z = 0; z < this.cz_size; z++) {
final WorldChunk c = this.myChunks[x][z];
serverLightManager.light(c, false);
c.markDirty();
}
}
}
@@ -320,7 +327,7 @@ public class CachedPlane {
// FIXME this was sending chunks to players...
ChunkDataS2CPacket cdp = new ChunkDataS2CPacket(c, verticalBits, false);
((ServerChunkManager) world.getChunkManager()).threadedAnvilChunkStorage
world.getChunkManager().threadedAnvilChunkStorage
.getPlayersWatchingChunk(c.getPos(), false).forEach(spe -> spe.networkHandler.sendPacket(cdp));
}
@@ -341,7 +348,6 @@ public class CachedPlane {
private static class BlockStorageData {
public BlockState state;
public int light;
}
private class Column {
@@ -374,8 +380,6 @@ public class CachedPlane {
final ChunkSection[] storage = this.c.getSectionArray();
final ChunkSection extendedBlockStorage = storage[y >> 4];
extendedBlockStorage.setBlockState(this.x, y & 15, this.z, data.state);
// FIXME extendedBlockStorage.setBlockLight( this.x, y & 15, this.z, data.light
// );
}
private void fillData(final int y, BlockStorageData data) {
@@ -383,8 +387,6 @@ public class CachedPlane {
final ChunkSection extendedblockstorage = storage[y >> 4];
data.state = extendedblockstorage.getBlockState(this.x, y & 15, this.z);
// FIXME data.light = extendedblockstorage.getBlockLight( this.x, y & 15, this.z
// );
}
private boolean doNotSkip(final int y) {
@@ -1,70 +0,0 @@
package appeng.spatial;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtHelper;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.PersistentState;
/**
* Helps with encoding and decoding the extra data we attach to each created
* storage dimension world as persistent state.
*/
public final class SpatialDimensionExtraData extends PersistentState {
/**
* ID of this data when it is attached to a world.
*/
public static final String ID = "ae2_spatial_info";
// Used to allow forward compatibility
private static final int CURRENT_FORMAT = 1;
private static final String TAG_FORMAT = "format";
private static final String TAG_SIZE = "size";
/**
* The storage size of this dimension. This is dicateted by the pylon structure
* size used to perform the first transfer into this dimension. Once it's set,
* it cannot be changed anymore.
*/
private BlockPos size = BlockPos.ORIGIN;
public SpatialDimensionExtraData() {
super(ID);
}
public SpatialDimensionExtraData(BlockPos size) {
super(ID);
this.size = size;
}
public BlockPos getSize() {
return size;
}
public void setSize(BlockPos size) {
this.size = size;
setDirty(true);
}
@Override
public void fromTag(CompoundTag tag) {
int version = tag.getInt(TAG_FORMAT);
if (version != CURRENT_FORMAT) {
// Currently no new format has been defined, as such anything but the current
// version is invalid
throw new IllegalStateException("Invalid AE2 spatial info version: " + version);
}
size = NbtHelper.toBlockPos(tag.getCompound(TAG_SIZE));
}
@Override
public CompoundTag toTag(CompoundTag tag) {
tag.putInt(TAG_FORMAT, CURRENT_FORMAT);
tag.put(TAG_SIZE, NbtHelper.fromBlockPos(size));
return tag;
}
}
@@ -1,200 +0,0 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2017, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.spatial;
import java.util.List;
import java.util.Locale;
import javax.annotation.Nullable;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.registry.Registry;
import net.minecraft.util.registry.RegistryKey;
import net.minecraft.world.World;
import net.minecraft.world.dimension.DimensionType;
import appeng.api.storage.ISpatialDimension;
import appeng.core.AELog;
import appeng.core.AppEng;
import appeng.core.localization.GuiText;
import appeng.hooks.DynamicDimensions;
public final class SpatialDimensionManager implements ISpatialDimension {
// A region file is 512x512 blocks (32x32 chunks),
// to avoid creating the 4 regions around 0,0,0,
// we move the origin to the middle of region 0,0
public static final BlockPos REGION_CENTER = new BlockPos(512 / 2, 64, 512 / 2);
public static final RegistryKey<DimensionType> STORAGE_DIMENSION_TYPE = RegistryKey.of(Registry.DIMENSION_TYPE_KEY,
AppEng.makeId("storage_cell"));
public static final ISpatialDimension INSTANCE = new SpatialDimensionManager();
private static final String DIM_ID_PREFIX = "spatial_";
private SpatialDimensionManager() {
}
@Override
public ServerWorld getWorld(RegistryKey<World> cellDim) {
MinecraftServer server = getServer();
return server.getWorld(cellDim);
}
@Override
public RegistryKey<World> createNewCellDimension(BlockPos size) {
Identifier dimKey = findFreeDimensionId();
AELog.info("Allocating storage cell dimension '%s'", dimKey);
DynamicDimensions dynamicDimensions = (DynamicDimensions) getServer();
RegistryKey<World> worldId = RegistryKey.of(Registry.DIMENSION, dimKey);
ServerWorld world = dynamicDimensions.addWorld(worldId, STORAGE_DIMENSION_TYPE, StorageChunkGenerator.INSTANCE);
SpatialDimensionExtraData spatialExtraData = world.getPersistentStateManager()
.getOrCreate(SpatialDimensionExtraData::new, SpatialDimensionExtraData.ID);
spatialExtraData.setSize(size);
return worldId;
}
/**
* Tries finding the next free storage cell dimension ID based on the currently
* registered storage cell dimensions.
*/
private Identifier findFreeDimensionId() {
int maxId = 0;
for (RegistryKey<World> worldId : getServer().getWorldRegistryKeys()) {
Identifier regName = worldId.getValue();
if (regName == null || !AppEng.MOD_ID.equals(regName.getNamespace())) {
continue;
}
String path = regName.getPath();
if (!path.startsWith(DIM_ID_PREFIX)) {
continue;
}
try {
String numericIdPart = path.substring(DIM_ID_PREFIX.length());
maxId = Math.max(Integer.parseUnsignedInt(numericIdPart), maxId);
} catch (NumberFormatException e) {
AELog.warn("Unparsable storage cell dimension id '%s'", path, e);
}
}
++maxId;
return new Identifier(AppEng.MOD_ID, DIM_ID_PREFIX + maxId);
}
@Override
public void deleteCellDimension(RegistryKey<World> worldId) {
AELog.info("Unregistering storage cell dimension %s", worldId.getValue());
MinecraftServer server = getServer();
// FIXME FABRIC ServerWorld world = DimensionManager.getWorld(server, worldId, false, false);
// FIXME FABRIC if (world != null) {
// FIXME FABRIC DimensionManager.unloadWorld(world);
// FIXME FABRIC }
// FIXME FABRIC DimensionManager.unloadWorlds(server, true);
// FIXME FABRIC DimensionManager.unregisterDimension(worldId.getId());
throw new IllegalStateException();
}
@Override
public boolean isCellDimension(RegistryKey<World> worldId) {
Identifier id = worldId.getValue();
if (!id.getNamespace().equals(AppEng.MOD_ID)) {
return false; // World belongs to a different mod
}
if (!id.getPath().startsWith(DIM_ID_PREFIX)) {
return false;
}
// Check that the world has the right dimension type
ServerWorld world = getServer().getWorld(worldId);
if (world == null) {
return false; // Non-existent world
}
return world.getDimensionRegistryKey().equals(STORAGE_DIMENSION_TYPE);
}
@Override
public BlockPos getCellDimensionOrigin(RegistryKey<World> worldId) {
return REGION_CENTER;
}
@Override
public BlockPos getCellDimensionSize(RegistryKey<World> worldId) {
SpatialDimensionExtraData extraData = getExtraData(worldId);
return extraData != null ? extraData.getSize() : BlockPos.ORIGIN;
}
@Override
public void addCellDimensionTooltip(RegistryKey<World> worldId, List<Text> lines) {
// Check if the cell dimension type is even registered
Identifier registryName = worldId.getValue();
if (registryName == null || !AppEng.MOD_ID.equals(registryName.getNamespace())) {
return;
}
if (!registryName.getPath().startsWith(DIM_ID_PREFIX)) {
return;
}
// Add the actual stored size
BlockPos size = SpatialDimensionManager.INSTANCE.getCellDimensionSize(worldId);
lines.add(GuiText.StoredSize.text(size.getX(), size.getY(), size.getZ()));
// Add a serial number to allows players to keep different cells apart
int dimId;
try {
String numericIdPart = registryName.getPath().substring(DIM_ID_PREFIX.length());
dimId = Integer.parseUnsignedInt(numericIdPart);
} catch (NumberFormatException ignored) {
return;
}
// Try to make this a little more flavorful.
String serialNumber = String.format(Locale.ROOT, "SP-%04d", dimId);
lines.add(GuiText.SerialNumber.text(serialNumber));
}
@Nullable
private SpatialDimensionExtraData getExtraData(RegistryKey<World> worldId) {
ServerWorld world = getWorld(worldId);
if (world == null) {
return null;
}
return world.getPersistentStateManager().get(SpatialDimensionExtraData::new, SpatialDimensionExtraData.ID);
}
private static MinecraftServer getServer() {
return AppEng.instance().getServer();
}
}
@@ -25,7 +25,10 @@ import net.minecraft.world.biome.SpawnSettings;
import net.minecraft.world.gen.surfacebuilder.ConfiguredSurfaceBuilder;
import net.minecraft.world.gen.surfacebuilder.SurfaceBuilder;
public class StorageCellBiome {
/**
* The single biome used within the spatial storage world.
*/
public class SpatialStorageBiome {
public static final Biome INSTANCE = new Biome.Settings()
.generationSettings(new GenerationSettings.Builder()
@@ -43,18 +43,21 @@ import net.minecraft.world.gen.chunk.VerticalBlockSample;
import appeng.core.Api;
public class StorageChunkGenerator extends ChunkGenerator {
/**
* Chunk generator the spatial storage world.
*/
public class SpatialStorageChunkGenerator extends ChunkGenerator {
private final VerticalBlockSample columnSample;
public static final StorageChunkGenerator INSTANCE = new StorageChunkGenerator();
public static final SpatialStorageChunkGenerator INSTANCE = new SpatialStorageChunkGenerator();
public static final Codec<StorageChunkGenerator> CODEC = RecordCodecBuilder
public static final Codec<SpatialStorageChunkGenerator> CODEC = RecordCodecBuilder
.create((instance) -> instance.stable(INSTANCE));
private final BlockState defaultBlockState;
private StorageChunkGenerator() {
private SpatialStorageChunkGenerator() {
super(createBiomeProvider(), createSettings());
this.defaultBlockState = Api.instance().definitions().blocks().matrixFrame().block().getDefaultState();
@@ -71,7 +74,7 @@ public class StorageChunkGenerator extends ChunkGenerator {
}
private static BiomeSource createBiomeProvider() {
return new FixedBiomeSource(StorageCellBiome.INSTANCE);
return new FixedBiomeSource(SpatialStorageBiome.INSTANCE);
}
private static StructuresConfig createSettings() {
@@ -0,0 +1,55 @@
package appeng.spatial;
import appeng.core.AppEng;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry;
import net.minecraft.util.registry.RegistryKey;
import net.minecraft.world.World;
import net.minecraft.world.dimension.DimensionOptions;
import net.minecraft.world.dimension.DimensionType;
/**
* IDs for the spatial storage world related dimension objects.
*/
public final class SpatialStorageDimensionIds {
/**
* ID of the {@link DimensionType} used for the spatial storage world.
* <p>
* This is defined in {@link appeng.mixins.spatial.DimensionTypeMixin}.
*/
public static final RegistryKey<DimensionType> DIMENSION_TYPE_ID = RegistryKey
.of(Registry.DIMENSION_TYPE_KEY, AppEng.makeId("spatial_storage"));
/**
* ID of the {@link net.minecraft.world.gen.chunk.ChunkGenerator} used for the spatial
* storage world.
*/
public static final Identifier CHUNK_GENERATOR_ID = AppEng.makeId("spatial_storage");
/**
* ID of the {@link net.minecraft.world.biome.Biome} used for the spatial
* storage world.
*/
public static final Identifier BIOME_ID = AppEng.makeId("spatial_storage");
/**
* ID of the {@link DimensionOptions} used for the spatial storage dimension.
* <p>
* This is defined in
* <code>data/minecraft/dimension/appliedenergistics2/spatial_storage.json</code>
*/
public static final RegistryKey<DimensionOptions> DIMENSION_ID = RegistryKey.of(Registry.DIMENSION_OPTIONS,
AppEng.makeId("spatial_storage"));
/**
* ID of the {@link World} that is instantiated from the dimension/dimension
* type.
*/
public static final RegistryKey<World> WORLD_ID = RegistryKey.of(Registry.DIMENSION,
AppEng.makeId("spatial_storage"));
private SpatialStorageDimensionIds() {
}
}
@@ -40,13 +40,13 @@ import appeng.api.util.WorldCoord;
import appeng.core.Api;
import appeng.core.AppEng;
public class StorageHelper {
public class SpatialStorageHelper {
private static StorageHelper instance;
private static SpatialStorageHelper instance;
public static StorageHelper getInstance() {
public static SpatialStorageHelper getInstance() {
if (instance == null) {
instance = new StorageHelper();
instance = new SpatialStorageHelper();
}
return instance;
}
@@ -99,7 +99,7 @@ public class StorageHelper {
ChunkStatus.FULL, true);
if (entity instanceof ServerPlayerEntity
&& link.dim.getDimensionRegistryKey() == SpatialDimensionManager.STORAGE_DIMENSION_TYPE) {
&& link.dim.getDimensionRegistryKey() == SpatialStorageDimensionIds.DIMENSION_TYPE_ID) {
AppEng.instance().getAdvancementTriggers().getSpatialExplorer().trigger((ServerPlayerEntity) entity);
}
@@ -0,0 +1,189 @@
package appeng.spatial;
import java.util.Locale;
import javax.annotation.Nullable;
import net.fabricmc.fabric.api.util.NbtType;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtHelper;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
/**
* A plot inside the storage cell world that is assigned to a specific storage
* cell.
*/
public class SpatialStoragePlot {
private static final String TAG_ID = "id";
private static final String TAG_SIZE = "size";
private static final String TAG_OWNER = "owner";
private static final String TAG_LAST_TRANSITION = "last_transition";
private static final int REGION_SIZE = 512;
public static final int MAX_SIZE = 128;
/**
* Id of the plot.
*/
private final int id;
/**
* The storage size of this dimension. This is dictated by the pylon structure
* size used to perform the first transfer into this dimension. Once it's set,
* it cannot be changed anymore.
*/
private final BlockPos size;
/**
* AE2 player id of who primarily owned the network when the storage plot was
* allocated.
*/
private final int owner;
/**
* Information about the last transition into this plot.
*/
@Nullable
private TransitionInfo lastTransition;
public SpatialStoragePlot(int id, BlockPos size, int owner) {
this.id = id;
this.size = size;
this.owner = owner;
if (size.getX() < 1 || size.getY() < 1 || size.getZ() < 1) {
throw new IllegalArgumentException("Plot size " + size + " is smaller than minimum size.");
}
if (size.getX() > MAX_SIZE || size.getY() > MAX_SIZE || size.getZ() > MAX_SIZE) {
throw new IllegalArgumentException("Plot size " + size + " exceeds maximum size of " + MAX_SIZE);
}
}
public int getId() {
return id;
}
/**
* The size in blocks of the plot in the storage dimension.
*
* @see #getOrigin()
*/
public BlockPos getSize() {
return size;
}
/**
* Returns the AE player id of the owner of this plot, or -1 if unknown.
*
* @see appeng.core.worlddata.IWorldPlayerData
*/
public int getOwner() {
return owner;
}
/**
* Returns information about the last transition into this plot (if any).
*/
@Nullable
public TransitionInfo getLastTransition() {
return lastTransition;
}
void setLastTransition(TransitionInfo info) {
this.lastTransition = info;
}
/**
* The origin of this plot within the spatial storage dimension.
* <p>
* To map an integer to a specific position, it uses the following algorithm.
*
* The 2 least significant bits determine the sign for the x and z axis. Every
* other pack of 2 bits locate the plot within a quadrant of a increasing area
* by the bit position.
*
* The first 2 bits after the sign address a quadrant within 1024x1024 blocks
* (or 4 region files)
*
* Every further will continue to double both x and z values. E.g. 2048x2048 for
* the 3rd pack and 4096x4096 for the 4th.
*
* @see #getSize()
*/
public BlockPos getOrigin() {
int signBits = id & 0b11;
int offsetBits = id >> 2;
int offsetScale = 1;
int posx = REGION_SIZE / 2;
int posz = REGION_SIZE / 2;
// find quadrant
while (offsetBits != 0) {
posx += REGION_SIZE * offsetScale * (offsetBits & 0b01);
posz += REGION_SIZE * offsetScale * (offsetBits >> 1 & 0b01);
offsetBits >>= 2;
offsetScale <<= 1;
}
// mirror in one of 4 directions
// First flip the z axis on every increment and then every two the x axis
if ((signBits & 0b01) != 0) {
posz *= -1;
}
if ((signBits & 0b10) != 0) {
posx *= -1;
}
// offset from cell center
//
// This is to ensure a 128x128x128 block plot (or 8x8 chunks) is centered within
// the region.
// This provides 12 chunks around it, so the default chunk loading radius on
// servers of 10 will prevent adjacent regions to be generated. As well as a 24
// chunks to the next plot.
posx -= 64;
posz -= 64;
return new BlockPos(posx, 64, posz);
}
/**
* Returns the filename of the region in the world save containing this plot.
*/
public String getRegionFilename() {
BlockPos origin = this.getOrigin();
ChunkPos originChunk = new ChunkPos(origin);
return String.format(Locale.ROOT, "r.%d.%d.mca", originChunk.getRegionX(), originChunk.getRegionZ());
}
public CompoundTag toTag() {
CompoundTag tag = new CompoundTag();
tag.putInt(TAG_ID, id);
tag.put(TAG_SIZE, NbtHelper.fromBlockPos(size));
tag.putInt(TAG_OWNER, owner);
if (lastTransition != null) {
tag.put(TAG_LAST_TRANSITION, lastTransition.toTag());
}
return tag;
}
public static SpatialStoragePlot fromTag(CompoundTag tag) {
int id = tag.getInt(TAG_ID);
BlockPos size = NbtHelper.toBlockPos(tag.getCompound(TAG_SIZE));
int ownerId = tag.getInt(TAG_OWNER);
SpatialStoragePlot plot = new SpatialStoragePlot(id, size, ownerId);
if (tag.contains(TAG_LAST_TRANSITION, NbtType.COMPOUND)) {
plot.lastTransition = TransitionInfo.fromTag(tag.getCompound(TAG_LAST_TRANSITION));
}
return plot;
}
}
@@ -0,0 +1,118 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2017, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.spatial;
import java.util.List;
import javax.annotation.Nullable;
import appeng.core.AppEng;
import net.minecraft.block.BlockState;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.BlockPos;
import appeng.core.AELog;
import appeng.core.Api;
/**
* Allocates and manages plots for spatial storage in the spatial storage world.
*/
public final class SpatialStoragePlotManager {
public static final SpatialStoragePlotManager INSTANCE = new SpatialStoragePlotManager();
private SpatialStoragePlotManager() {
}
/**
* Gets the world used to store spatial storage cell's content.
*/
public ServerWorld getWorld() {
MinecraftServer server = getServer();
ServerWorld world = server.getWorld(SpatialStorageDimensionIds.WORLD_ID);
if (world == null) {
throw new IllegalStateException("The storage cell world is missing.");
}
return world;
}
private SpatialStorageWorldData getWorldData() {
return getWorld().getChunkManager().getPersistentStateManager().getOrCreate(SpatialStorageWorldData::new,
SpatialStorageWorldData.ID);
}
@Nullable
public SpatialStoragePlot getPlot(int plotId) {
if (plotId == -1) {
return null;
}
return getWorldData().getPlotById(plotId);
}
public SpatialStoragePlot allocatePlot(BlockPos size, int ownerId) {
SpatialStoragePlot plot = getWorldData().allocatePlot(size, ownerId);
AELog.info("Allocating storage cell plot %d with size %s for %d", plot.getId(), size, ownerId);
return plot;
}
/**
* Sets the last source for a spatial storage transition into the given plot.
* This is used to allow the server-admin commands to know where the content of
* a given plot came from.
*/
public void setLastTransition(int plotId, TransitionInfo info) {
getWorldData().setLastTransition(plotId, info);
}
/**
* Returns an immutable list of all plots.
*/
public List<SpatialStoragePlot> getPlots() {
return getWorldData().getPlots();
}
public void freePlot(int plotId, boolean resetBlocks) {
SpatialStoragePlot plot = getPlot(plotId);
if (plot == null) {
return; // Already removed apparently
}
if (resetBlocks) {
BlockPos from = plot.getOrigin();
BlockPos to = from.add(plot.getSize()).add(-1, -1, -1);
AELog.info("Clearing spatial storage plot %s (%s -> %s)", plotId, from, to);
// This is slow, but it should usually be just an admin-command
ServerWorld world = getWorld();
BlockState matrixFrame = Api.instance().definitions().blocks().matrixFrame().block().getDefaultState();
for (BlockPos blockPos : BlockPos.iterate(from, to)) {
world.setBlockState(blockPos, matrixFrame);
}
}
getWorldData().removePlot(plotId);
}
private static MinecraftServer getServer() {
return AppEng.instance().getServer();
}
}
@@ -7,8 +7,11 @@ import net.fabricmc.api.Environment;
import net.minecraft.client.render.SkyProperties;
import net.minecraft.util.math.Vec3d;
/**
* Defines properties for how the sky in the spatial storage world is rendered.
*/
@Environment(EnvType.CLIENT)
public class StorageSkyProperties {
public class SpatialStorageSkyProperties {
// See the fabric version of this to get any idea what its doing
public static final SkyProperties INSTANCE = new SkyProperties(Float.NaN /* disables clouds */, false,
@@ -0,0 +1,110 @@
package appeng.spatial;
import java.util.List;
import com.google.common.collect.ImmutableList;
import net.fabricmc.fabric.api.util.NbtType;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.PersistentState;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import appeng.core.AELog;
/**
* Extra data attached to the spatial storage world.
*/
public class SpatialStorageWorldData extends PersistentState {
/**
* ID of this data when it is attached to a world.
*/
public static final String ID = "ae2_spatial_storage";
// Used to allow forward compatibility
private static final int CURRENT_FORMAT = 2;
private static final String TAG_FORMAT = "format";
private static final String TAG_PLOTS = "plots";
private final Int2ObjectOpenHashMap<SpatialStoragePlot> plots = new Int2ObjectOpenHashMap<>();
public SpatialStorageWorldData() {
super(ID);
}
public SpatialStoragePlot getPlotById(int id) {
return plots.get(id);
}
public List<SpatialStoragePlot> getPlots() {
return ImmutableList.copyOf(plots.values());
}
public SpatialStoragePlot allocatePlot(BlockPos size, int owner) {
int nextId = 1;
for (int id : plots.keySet()) {
if (id >= nextId) {
nextId = id + 1;
}
}
SpatialStoragePlot plot = new SpatialStoragePlot(nextId, size, owner);
plots.put(nextId, plot);
markDirty();
return plot;
}
public void removePlot(int plotId) {
plots.remove(plotId);
markDirty();
}
public void setLastTransition(int plotId, TransitionInfo info) {
SpatialStoragePlot plot = plots.get(plotId);
if (plot != null) {
plot.setLastTransition(info);
}
markDirty();
}
@Override
public void fromTag(CompoundTag tag) {
int version = tag.getInt(TAG_FORMAT);
if (version != CURRENT_FORMAT) {
// Currently no new format has been defined, as such anything but the current
// version is invalid
throw new IllegalStateException("Invalid AE2 spatial info version: " + version);
}
ListTag plotsTag = tag.getList(TAG_PLOTS, NbtType.COMPOUND);
for (Tag plotTag : plotsTag) {
SpatialStoragePlot plot = SpatialStoragePlot.fromTag((CompoundTag) plotTag);
if (plots.containsKey(plot.getId())) {
AELog.warn("Overwriting duplicate plot id %s", plot.getId());
}
plots.put(plot.getId(), plot);
}
}
@Override
public CompoundTag toTag(CompoundTag tag) {
tag.putInt(TAG_FORMAT, CURRENT_FORMAT);
ListTag plotTags = new ListTag();
for (SpatialStoragePlot plot : plots.values()) {
plotTags.add(plot.toTag());
}
tag.put(TAG_PLOTS, plotTags);
return tag;
}
}
@@ -0,0 +1,69 @@
package appeng.spatial;
import java.time.Instant;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtHelper;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
/**
* Defines the source world and area of a transition into the spatial storage
* plot.
*/
public final class TransitionInfo {
public static final String TAG_WORLD_ID = "world_id";
public static final String TAG_MIN = "min";
public static final String TAG_MAX = "max";
public static final String TAG_TIMESTAMP = "timestamp";
private final Identifier worldId;
private final BlockPos min;
private final BlockPos max;
private final Instant timestamp;
public TransitionInfo(Identifier worldId, BlockPos min, BlockPos max, Instant timestamp) {
this.worldId = worldId;
this.min = min.toImmutable();
this.max = max.toImmutable();
this.timestamp = timestamp;
}
public Identifier getWorldId() {
return worldId;
}
public BlockPos getMin() {
return min;
}
public BlockPos getMax() {
return max;
}
public Instant getTimestamp() {
return timestamp;
}
public CompoundTag toTag() {
CompoundTag tag = new CompoundTag();
tag.putString(TAG_WORLD_ID, worldId.toString());
tag.put(TAG_MIN, NbtHelper.fromBlockPos(min));
tag.put(TAG_MAX, NbtHelper.fromBlockPos(max));
tag.putLong(TAG_TIMESTAMP, timestamp.toEpochMilli());
return tag;
}
public static TransitionInfo fromTag(CompoundTag tag) {
Identifier worldId = new Identifier(tag.getString(TAG_WORLD_ID));
BlockPos min = NbtHelper.toBlockPos(tag.getCompound(TAG_MIN));
BlockPos max = NbtHelper.toBlockPos(tag.getCompound(TAG_MAX));
Instant timestamp = Instant.ofEpochMilli(tag.getLong(TAG_TIMESTAMP));
return new TransitionInfo(worldId, min, max, timestamp);
}
}