Moving to source sets

This commit is contained in:
Sebastian Hartte
2020-07-01 23:36:51 +02:00
parent f2e3d81fd7
commit 2642ced86b
2924 changed files with 794 additions and 796 deletions
@@ -1,409 +0,0 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2015, 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.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.network.play.server.SChunkDataPacket;
import net.minecraft.util.Tickable;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.EmptyBlockView;
import net.minecraft.world.ITickList;
import net.minecraft.world.NextTickListEntry;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.ChunkSection;
import net.minecraft.world.server.ServerChunkProvider;
import net.minecraft.world.server.ServerTickList;
import appeng.api.AEApi;
import appeng.api.movable.IMovableHandler;
import appeng.api.movable.IMovableRegistry;
import appeng.api.util.AEPartLocation;
import appeng.api.util.WorldCoord;
import appeng.core.AELog;
import appeng.core.worlddata.WorldData;
public class CachedPlane {
private final int x_size;
private final int z_size;
private final int cx_size;
private final int cz_size;
private final int x_offset;
private final int y_offset;
private final int z_offset;
private final int y_size;
private final Chunk[][] myChunks;
private final Column[][] myColumns;
private final List<BlockEntity> tiles = new ArrayList<>();
private final List<NextTickListEntry<Block>> ticks = new ArrayList<>();
private final World world;
private final IMovableRegistry reg = AEApi.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,
final int maxZ) {
Block matrixFrameBlock = AEApi.instance().definitions().blocks().matrixFrame().maybeBlock().orElse(null);
if (matrixFrameBlock != null) {
this.matrixBlockState = matrixFrameBlock.getDefaultState();
} else {
this.matrixBlockState = null;
}
this.world = w;
this.x_size = maxX - minX + 1;
this.y_size = maxY - minY + 1;
this.z_size = maxZ - minZ + 1;
this.x_offset = minX;
this.y_offset = minY;
this.z_offset = minZ;
final int minCX = minX >> 4;
final int minCY = minY >> 4;
final int minCZ = minZ >> 4;
final int maxCX = maxX >> 4;
final int maxCY = maxY >> 4;
final int maxCZ = maxZ >> 4;
this.cx_size = maxCX - minCX + 1;
final int cy_size = maxCY - minCY + 1;
this.cz_size = maxCZ - minCZ + 1;
this.myChunks = new Chunk[this.cx_size][this.cz_size];
this.myColumns = new Column[this.x_size][this.z_size];
this.verticalBits = 0;
for (int cy = 0; cy < cy_size; cy++) {
this.verticalBits |= 1 << (minCY + cy);
}
for (int x = 0; x < this.x_size; x++) {
for (int z = 0; z < this.z_size; z++) {
this.myColumns[x][z] = new Column(w.getChunk((minX + x) >> 4, (minZ + z) >> 4), (minX + x) & 0xF,
(minZ + z) & 0xF, minCY, cy_size);
}
}
final IMovableRegistry mr = AEApi.instance().registries().movable();
for (int cx = 0; cx < this.cx_size; cx++) {
for (int cz = 0; cz < this.cz_size; cz++) {
final List<BlockPos> deadTiles = new ArrayList<>();
final Chunk c = w.getChunk(minCX + cx, minCZ + cz);
this.myChunks[cx][cz] = c;
final List<Entry<BlockPos, BlockEntity>> rawTiles = new ArrayList<>(c.getBlockEntityMap().entrySet());
for (final Entry<BlockPos, BlockEntity> tx : rawTiles) {
final BlockPos cp = tx.getKey();
final BlockEntity te = tx.getValue();
final BlockPos tePOS = te.getPos();
if (tePOS.getX() >= minX && tePOS.getX() <= maxX && tePOS.getY() >= minY && tePOS.getY() <= maxY
&& tePOS.getZ() >= minZ && tePOS.getZ() <= maxZ) {
if (mr.askToMove(te)) {
this.tiles.add(te);
deadTiles.add(cp);
} else {
final BlockStorageData details = new BlockStorageData();
this.myColumns[tePOS.getX() - minX][tePOS.getZ() - minZ].fillData(tePOS.getY(), details);
// don't skip air, just let the code replace it...
if (details.state.isAir(EmptyBlockView.INSTANCE, tePOS)) {
w.removeBlock(tePOS, false);
} else {
this.myColumns[tePOS.getX() - minX][tePOS.getZ() - minZ].setSkip(tePOS.getY());
}
}
}
}
for (final BlockPos cp : deadTiles) {
c.getBlockEntityMap().remove(cp);
}
final long gameTime = this.getWorld().getGameTime();
final ITickList<Block> pendingBlockTicks = this.getWorld().getPendingBlockTicks();
if (pendingBlockTicks instanceof ServerTickList) {
List<NextTickListEntry<Block>> pending = ((ServerTickList<Block>) pendingBlockTicks)
.getPending(c.getPos(), false, true);
for (final NextTickListEntry<Block> entry : pending) {
final BlockPos tePOS = entry.position;
if (tePOS.getX() >= minX && tePOS.getX() <= maxX && tePOS.getY() >= minY && tePOS.getY() <= maxY
&& tePOS.getZ() >= minZ && tePOS.getZ() <= maxZ) {
this.ticks.add(new NextTickListEntry<>(tePOS, entry.getTarget(),
entry.scheduledTime - gameTime, entry.priority));
}
}
}
}
}
for (final BlockEntity te : this.tiles) {
try {
this.getWorld().loadedTileEntityList.remove(te);
if (te instanceof Tickable) {
this.getWorld().tickableTileEntities.remove(te);
}
} catch (final Exception e) {
AELog.debug(e);
}
}
}
private IMovableHandler getHandler(final BlockEntity te) {
final IMovableRegistry mr = AEApi.instance().registries().movable();
return mr.getHandler(te);
}
void swap(final CachedPlane dst) {
final IMovableRegistry mr = AEApi.instance().registries().movable();
if (dst.x_size == this.x_size && dst.y_size == this.y_size && dst.z_size == this.z_size) {
AELog.info("Block Copy Scale: " + this.x_size + ", " + this.y_size + ", " + this.z_size);
long startTime = System.nanoTime();
final BlockStorageData aD = new BlockStorageData();
final BlockStorageData bD = new BlockStorageData();
for (int x = 0; x < this.x_size; x++) {
for (int z = 0; z < this.z_size; z++) {
final Column a = this.myColumns[x][z];
final Column b = dst.myColumns[x][z];
for (int y = 0; y < this.y_size; y++) {
final int src_y = y + this.y_offset;
final int dst_y = y + dst.y_offset;
if (a.doNotSkip(src_y) && b.doNotSkip(dst_y)) {
a.fillData(src_y, aD);
b.fillData(dst_y, bD);
a.setBlockState(src_y, bD);
b.setBlockState(dst_y, aD);
} else {
this.markForUpdate(x + this.x_offset, src_y, z + this.z_offset);
dst.markForUpdate(x + dst.x_offset, dst_y, z + dst.z_offset);
}
}
}
}
long endTime = System.nanoTime();
long duration = endTime - startTime;
AELog.info("Block Copy Time: " + duration);
for (final BlockEntity te : this.tiles) {
final BlockPos tePOS = te.getPos();
dst.addTile(tePOS.getX() - this.x_offset, tePOS.getY() - this.y_offset, tePOS.getZ() - this.z_offset,
te, this, mr);
}
for (final BlockEntity te : dst.tiles) {
final BlockPos tePOS = te.getPos();
this.addTile(tePOS.getX() - dst.x_offset, tePOS.getY() - dst.y_offset, tePOS.getZ() - dst.z_offset, te,
dst, mr);
}
for (final NextTickListEntry<Block> entry : this.ticks) {
final BlockPos tePOS = entry.position;
dst.addTick(tePOS.getX() - this.x_offset, tePOS.getY() - this.y_offset, tePOS.getZ() - this.z_offset,
entry);
}
for (final NextTickListEntry<Block> entry : dst.ticks) {
final BlockPos tePOS = entry.position;
this.addTick(tePOS.getX() - dst.x_offset, tePOS.getY() - dst.y_offset, tePOS.getZ() - dst.z_offset,
entry);
}
startTime = System.nanoTime();
this.updateChunks();
dst.updateChunks();
endTime = System.nanoTime();
duration = endTime - startTime;
AELog.info("Update Time: " + duration);
}
}
private void markForUpdate(final int x, final int y, final int z) {
this.updates.add(new WorldCoord(x, y, z));
for (final AEPartLocation d : AEPartLocation.SIDE_LOCATIONS) {
this.updates.add(new WorldCoord(x + d.xOffset, y + d.yOffset, z + d.zOffset));
}
}
private void addTick(final int x, final int y, final int z, final NextTickListEntry<Block> entry) {
BlockPos where = new BlockPos(x + this.x_offset, y + this.y_offset, z + this.z_offset);
this.world.getPendingBlockTicks().scheduleTick(where, entry.getTarget(), (int) entry.scheduledTime,
entry.priority);
}
private void addTile(final int x, final int y, final int z, final BlockEntity te,
final CachedPlane alternateDestination, final IMovableRegistry mr) {
try {
final Column c = this.myColumns[x][z];
if (c.doNotSkip(y + this.y_offset) || alternateDestination == null) {
final IMovableHandler handler = this.getHandler(te);
try {
handler.moveTile(te, this.world,
new BlockPos(x + this.x_offset, y + this.y_offset, z + this.z_offset));
} catch (final Throwable e) {
AELog.debug(e);
final BlockPos pos = new BlockPos(x, y, z);
// attempt recovery...
c.c.addBlockEntity(te);
this.world.updateListeners(pos, this.world.getBlockState(pos), this.world.getBlockState(pos), z);
}
mr.doneMoving(te);
} else {
alternateDestination.addTile(x, y, z, te, null, mr);
}
} catch (final Throwable e) {
AELog.debug(e);
}
}
private void updateChunks() {
// update shit..
for (int x = 0; x < this.cx_size; x++) {
for (int z = 0; z < this.cz_size; z++) {
final Chunk c = this.myChunks[x][z];
// FIXME: Light shit
c.markDirty();
}
}
// send shit...
for (int x = 0; x < this.cx_size; x++) {
for (int z = 0; z < this.cz_size; z++) {
final Chunk c = this.myChunks[x][z];
for (int y = 1; y < 255; y += 32) {
WorldData.instance().compassData().service().updateArea(this.getWorld(), c.getPos(), y);
}
// FIXME this was sending chunks to players...
SChunkDataPacket cdp = new SChunkDataPacket(c, verticalBits);
((ServerChunkProvider) world.getChunkManager()).chunkManager.getTrackingPlayers(c.getPos(), false)
.forEach(spe -> spe.connection.sendPacket(cdp));
}
}
// FIXME check if this makes any sense at all to send changes to players asap
ServerChunkProvider serverChunkProvider = (ServerChunkProvider) world.getChunkManager();
serverChunkProvider.tick(() -> false);
}
List<WorldCoord> getUpdates() {
return this.updates;
}
World getWorld() {
return this.world;
}
private static class BlockStorageData {
public BlockState state;
public int light;
}
private class Column {
private final int x;
private final int z;
private final Chunk c;
private List<Integer> skipThese = null;
public Column(final Chunk chunk, final int x, final int z, final int chunkY, final int chunkHeight) {
this.x = x;
this.z = z;
this.c = chunk;
final ChunkSection[] storage = this.c.getSections();
// make sure storage exists before hand...
for (int ay = 0; ay < chunkHeight; ay++) {
final int by = (ay + chunkY);
ChunkSection extendedblockstorage = storage[by];
if (extendedblockstorage == null) {
extendedblockstorage = storage[by] = new ChunkSection(by << 4);
}
}
}
private void setBlockState(final int y, BlockStorageData data) {
if (data.state == CachedPlane.this.matrixBlockState) {
data.state = Blocks.AIR.getDefaultState();
}
final ChunkSection[] storage = this.c.getSections();
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) {
final ChunkSection[] storage = this.c.getSections();
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) {
final ChunkSection[] storage = this.c.getSections();
final ChunkSection extendedblockstorage = storage[y >> 4];
if (CachedPlane.this.reg
.isBlacklisted(extendedblockstorage.getBlockState(this.x, y & 15, this.z).getBlock())) {
return false;
}
return this.skipThese == null || !this.skipThese.contains(y);
}
private void setSkip(final int yCoord) {
if (this.skipThese == null) {
this.skipThese = new ArrayList<>();
}
this.skipThese.add(yCoord);
}
}
}
@@ -0,0 +1,58 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, 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 net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import appeng.api.movable.IMovableHandler;
public class DefaultSpatialHandler implements IMovableHandler {
/**
* never called for the default.
*
* @param tile block entity
*
* @return true
*/
@Override
public boolean canHandle(final Class<? extends BlockEntity> myClass, final BlockEntity tile) {
return true;
}
@Override
public void moveTile(final BlockEntity te, final World w, final BlockPos newPosition) {
te.setLocation(w, newPosition);
final Chunk c = w.getChunk(newPosition);
c.setBlockEntity(newPosition, te);
ChunkPos chunkPos = c.getPos();
if (w.getChunkManager().isChunkLoaded(chunkPos.x, chunkPos.z)) {
final BlockState state = w.getBlockState(newPosition);
w.addBlockEntity(te);
w.updateListeners(newPosition, state, state, 1);
}
}
}
@@ -1,26 +0,0 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, 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 net.minecraft.util.math.BlockPos;
public interface ISpatialVisitor {
void visit(BlockPos pos);
}
@@ -1,70 +0,0 @@
package appeng.spatial;
import javax.annotation.Nullable;
import io.netty.buffer.Unpooled;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.util.math.BlockPos;
import appeng.core.AELog;
/**
* Helps with encoding and decoding the extra data we attach to the spatial
* {@link net.minecraft.world.dimension.DimensionType} as "extra data". Keep in
* mind this data will also be sent to the client unless
* {@link net.minecraftforge.common.ModDimension#write(PacketByteBuf, boolean)}
* is overridden.
*/
public final class SpatialDimensionExtraData {
// Used to allow forward compatibility
private static final int CURRENT_FORMAT = 1;
/**
* 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 final BlockPos size;
public SpatialDimensionExtraData(BlockPos size) {
this.size = size;
}
public PacketByteBuf write() {
PacketByteBuf buf = new PacketByteBuf(Unpooled.buffer());
buf.writeByte(CURRENT_FORMAT);
buf.writeBlockPos(size);
buf.capacity(buf.writerIndex()); // This cuts the backing buffer to the required size
return buf;
}
public BlockPos getSize() {
return size;
}
@Nullable
public static SpatialDimensionExtraData read(@Nullable PacketByteBuf buf) {
if (buf == null) {
return null;
}
try {
buf.readerIndex(0);
byte version = buf.readByte();
if (version != CURRENT_FORMAT) {
// Currently no new format has been defined, as such anything but the current
// version is invalid
return null;
}
BlockPos size = buf.readBlockPos();
return new SpatialDimensionExtraData(size);
} catch (IndexOutOfBoundsException e) {
AELog.warn(e, "Failed to read spatial storage dimension data.");
return null;
}
}
}
@@ -1,170 +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.network.PacketByteBuf;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.text.Text;
import net.minecraft.world.dimension.DimensionType;
import net.minecraft.server.world.ServerWorld;
import net.minecraftforge.common.DimensionManager;
import net.minecraftforge.fml.server.ServerLifecycleHooks;
import appeng.api.storage.ISpatialDimension;
import appeng.core.AELog;
import appeng.core.AppEng;
import appeng.core.localization.GuiText;
public final class SpatialDimensionManager implements ISpatialDimension {
public static final ISpatialDimension INSTANCE = new SpatialDimensionManager();
private static final String DIM_ID_PREFIX = "spatial_";
private SpatialDimensionManager() {
}
@Override
public ServerWorld getWorld(DimensionType cellDim) {
return DimensionManager.getWorld(getServer(), cellDim, true, true);
}
@Override
public DimensionType createNewCellDimension(BlockPos size) {
Identifier dimKey = findFreeDimensionId();
AELog.info("Allocating storage cell dimension '%s'", dimKey);
PacketByteBuf extraData = new SpatialDimensionExtraData(size).write();
return DimensionManager.registerDimension(dimKey, StorageCellModDimension.INSTANCE, extraData, true);
}
/**
* Tries finding the next free storage cell dimension ID based on the currently
* registered storage cell dimensions.
*/
private Identifier findFreeDimensionId() {
int maxId = 0;
for (DimensionType dimensionType : DimensionType.getAll()) {
Identifier regName = dimensionType.getRegistryName();
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(DimensionType cellDim) {
AELog.info("Unregistering storage cell dimension %s", cellDim.getRegistryName());
MinecraftServer server = getServer();
ServerWorld world = DimensionManager.getWorld(server, cellDim, false, false);
if (world != null) {
DimensionManager.unloadWorld(world);
}
DimensionManager.unloadWorlds(server, true);
DimensionManager.unregisterDimension(cellDim.getId());
}
@Override
public boolean isCellDimension(DimensionType cellDim) {
// Check if the cell dimension type is even registered
if (cellDim.getRegistryName() == null || !cellDim.getRegistryName().equals(DimensionType.getKey(cellDim))) {
return false;
}
return cellDim.getModType() instanceof StorageCellModDimension;
}
@Override
public BlockPos getCellDimensionOrigin(DimensionType cellDim) {
return StorageCellDimension.REGION_CENTER;
}
@Override
public BlockPos getCellDimensionSize(DimensionType cellDim) {
SpatialDimensionExtraData extraData = getExtraData(cellDim);
return extraData != null ? extraData.getSize() : BlockPos.ORIGIN;
}
@Override
public void addCellDimensionTooltip(DimensionType cellDim, List<Text> lines) {
// Check if the cell dimension type is even registered
Identifier registryName = cellDim.getRegistryName();
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(cellDim);
lines.add(GuiText.StoredSize.textComponent(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.textComponent(serialNumber));
}
@Nullable
private SpatialDimensionExtraData getExtraData(DimensionType cellDim) {
if (!(cellDim.getModType() instanceof StorageCellModDimension)) {
return null;
}
return SpatialDimensionExtraData.read(cellDim.getData());
}
private static MinecraftServer getServer() {
return ServerLifecycleHooks.getCurrentServer();
}
}
@@ -1,74 +0,0 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, 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 net.minecraft.util.SharedSeedRandom;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.WorldAccess;
import net.minecraft.world.WorldView;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.gen.ChunkGenerator;
import net.minecraft.world.gen.GenerationSettings;
import net.minecraft.world.gen.GenerationStage;
import net.minecraft.world.gen.surfacebuilders.SurfaceBuilder;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
public class StorageCellBiome extends Biome {
public static final StorageCellBiome INSTANCE = new StorageCellBiome();
static {
INSTANCE.setRegistryName("appliedenergistics2:storage");
}
public StorageCellBiome() {
super(new Biome.Builder().surfaceBuilder(SurfaceBuilder.NOPE, SurfaceBuilder.STONE_STONE_GRAVEL_CONFIG)
.precipitation(RainType.NONE).category(Category.NONE).depth(0).scale(1)
// Copied from the vanilla void biome
.temperature(0.5F).downfall(0.5F).waterColor(4159204).waterFogColor(329011).parent(null));
}
@Override
@Environment(EnvType.CLIENT)
public int getSkyColor() {
return 0x111111;
}
@Override
public boolean doesWaterFreeze(WorldView worldIn, BlockPos pos) {
return false;
}
@Override
public boolean doesWaterFreeze(WorldView worldIn, BlockPos water, boolean mustBeAtEdge) {
return false;
}
@Override
public boolean doesSnowGenerate(WorldView worldIn, BlockPos pos) {
return false;
}
@Override
public void decorate(GenerationStage.Decoration stage, ChunkGenerator<? extends GenerationSettings> chunkGenerator,
WorldAccess worldIn, long seed, SharedSeedRandom random, BlockPos pos) {
// Nothing should ever generate here...
}
}
@@ -1,132 +0,0 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2015, 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 javax.annotation.Nullable;
import net.fabricmc.api.Environment;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.dimension.DimensionType;
import net.minecraft.world.gen.ChunkGenerator;
import net.fabricmc.api.EnvType;
import net.minecraftforge.client.IRenderHandler;
import appeng.client.render.SpatialSkyRender;
public class StorageCellDimension extends Dimension {
// 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 StorageCellDimension(World world, DimensionType dimensionType) {
// FIXME: check light value
super(world, dimensionType, 1.0f);
}
@Override
public ChunkGenerator createChunkGenerator() {
return new StorageChunkGenerator(this.world);
}
@Override
public float calculateCelestialAngle(final long par1, final float par3) {
return 0;
}
@Override
public boolean isSurfaceWorld() {
return false;
}
@Override
@Environment(EnvType.CLIENT)
public float[] calcSunriseSunsetColors(final float celestialAngle, final float partialTicks) {
return null;
}
@Override
public Vec3d getFogColor(final float par1, final float par2) {
return new Vec3d(0.07, 0.07, 0.07);
}
@Override
public boolean canRespawnHere() {
return false;
}
@Override
@Environment(EnvType.CLIENT)
public boolean isSkyColored() {
return true;
}
@Override
public boolean doesXZShowFog(final int par1, final int par2) {
return false;
}
@Override
public IRenderHandler getSkyRenderer() {
return SpatialSkyRender.getInstance();
}
@Override
public boolean isDaytime() {
return false;
}
@Override
public BlockPos getSpawnCoordinate() {
return REGION_CENTER;
}
@Override
public boolean isHighHumidity(final BlockPos pos) {
return false;
}
@Override
public boolean canDoLightning(final Chunk chunk) {
return false;
}
@Override
public boolean canDoRainSnowIce(Chunk chunk) {
return false;
}
@Nullable
@Override
public BlockPos findSpawn(ChunkPos chunkPosIn, boolean checkValid) {
return getSpawnCoordinate();
}
@Nullable
@Override
public BlockPos findSpawn(int posX, int posZ, boolean checkValid) {
return getSpawnCoordinate();
}
}
@@ -1,43 +0,0 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2015, 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.function.BiFunction;
import net.minecraft.world.World;
import net.minecraft.world.dimension.DimensionType;
import net.minecraft.world.dimension.DimensionType;
import net.minecraftforge.common.ModDimension;
import appeng.core.AppEng;
public class StorageCellModDimension extends ModDimension {
public static final StorageCellModDimension INSTANCE = new StorageCellModDimension();
static {
INSTANCE.setRegistryName(AppEng.MOD_ID, "storage_cell");
}
@Override
public BiFunction<World, DimensionType, ? extends Dimension> getFactory() {
return StorageCellDimension::new;
}
}
@@ -1,96 +0,0 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, 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 net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.WorldAccess;
import net.minecraft.world.World;
import net.minecraft.world.biome.provider.BiomeProvider;
import net.minecraft.world.biome.provider.SingleBiomeProvider;
import net.minecraft.world.biome.provider.SingleBiomeProviderSettings;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.gen.ChunkGenerator;
import net.minecraft.world.gen.GenerationSettings;
import net.minecraft.world.gen.Heightmap;
import net.minecraft.world.gen.WorldGenRegion;
import appeng.api.AEApi;
public class StorageChunkGenerator extends ChunkGenerator<GenerationSettings> {
private final BlockState defaultBlockState;
public StorageChunkGenerator(final World world) {
super(world, createBiomeProvider(), createSettings());
this.defaultBlockState = AEApi.instance().definitions().blocks().matrixFrame().block().getDefaultState();
}
private static BiomeProvider createBiomeProvider() {
SingleBiomeProviderSettings biomeSettings = new SingleBiomeProviderSettings(null);
biomeSettings.setBiome(StorageCellBiome.INSTANCE);
return new SingleBiomeProvider(biomeSettings);
}
private static GenerationSettings createSettings() {
return new GenerationSettings();
}
@Override
public void generateSurface(WorldGenRegion region, Chunk chunk) {
this.fillChunk(chunk);
chunk.setModified(false);
}
private void fillChunk(Chunk chunk) {
BlockPos.Mutable mutPos = new BlockPos.Mutable();
for (int cx = 0; cx < 16; cx++) {
mutPos.setX(cx);
for (int cz = 0; cz < 16; cz++) {
// FIXME: It's likely a bad idea to fill Y in the inner-loop given the storage
// layout of chunks
mutPos.setZ(cz);
for (int cy = 0; cy < 256; cy++) {
mutPos.setY(cy);
chunk.setBlockState(mutPos, defaultBlockState, false);
}
}
}
}
@Override
public int getGroundHeight() {
return 0;
}
@Override
public void makeBase(WorldAccess worldIn, Chunk chunkIn) {
}
@Override
public int func_222529_a(int p_222529_1_, int p_222529_2_, Heightmap.Type heightmapType) {
return 0;
}
@Override
public void decorate(WorldGenRegion region) {
// Do not decorate chunks at all
}
}
@@ -1,242 +0,0 @@
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2015, 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.ArrayList;
import java.util.List;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.entity.Entity;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.World;
import net.minecraft.world.chunk.ChunkStatus;
import net.minecraft.server.world.ServerWorld;
import appeng.api.AEApi;
import appeng.api.util.WorldCoord;
import appeng.core.AppEng;
public class StorageHelper {
private static StorageHelper instance;
public static StorageHelper getInstance() {
if (instance == null) {
instance = new StorageHelper();
}
return instance;
}
/**
* Mostly from dimensional doors.. which mostly got it form X-Comp.
*
* @param entity to be teleported entity
* @param link destination
*
* @return teleported entity
*/
private Entity teleportEntity(Entity entity, final TelDestination link) {
final ServerWorld oldWorld;
final ServerWorld newWorld;
try {
oldWorld = (ServerWorld) entity.world;
newWorld = link.dim;
} catch (final Throwable e) {
return entity;
}
if (oldWorld == null) {
return entity;
}
if (newWorld == null) {
return entity;
}
if (newWorld == oldWorld) {
return entity;
}
// Are we riding something? Teleport it instead.
if (entity.hasVehicle()) {
return this.teleportEntity(entity.getVehicle(), link);
}
// Is something riding us? Handle it first.
final List<Entity> passengers = entity.getPassengerList();
final List<Entity> passengersOnOtherSide = new ArrayList<>(passengers.size());
for (Entity passenger : passengers) {
passenger.stopRiding();
passengersOnOtherSide.add(this.teleportEntity(passenger, link));
}
// We keep track of all so we can remount them on the other side.
// load the chunk!
newWorld.getChunkManager().getChunk(MathHelper.floor(link.x) >> 4, MathHelper.floor(link.z) >> 4,
ChunkStatus.FULL, true);
if (entity instanceof ServerPlayerEntity && link.dim.getDimension() instanceof StorageCellDimension) {
AppEng.instance().getAdvancementTriggers().getSpatialExplorer().trigger((ServerPlayerEntity) entity);
}
// FIXME FABRIC new METeleporter(link)
float yaw = entity.yaw;
entity = entity.changeDimension(link.dim);
entity.yaw = yaw;
entity.refreshPositionAfterTeleport(link.x, link.y, link.z);
entity.setVelocity(0, 0, 0);
if (!passengersOnOtherSide.isEmpty()) {
for (Entity passanger : passengersOnOtherSide) {
passanger.startRiding(entity, true);
}
}
return entity;
}
private void transverseEdges(final int minX, final int minY, final int minZ, final int maxX, final int maxY,
final int maxZ, final ISpatialVisitor visitor) {
for (int y = minY; y < maxY; y++) {
for (int z = minZ; z < maxZ; z++) {
visitor.visit(new BlockPos(minX, y, z));
visitor.visit(new BlockPos(maxX, y, z));
}
}
for (int x = minX; x < maxX; x++) {
for (int z = minZ; z < maxZ; z++) {
visitor.visit(new BlockPos(x, minY, z));
visitor.visit(new BlockPos(x, maxY, z));
}
}
for (int x = minX; x < maxX; x++) {
for (int y = minY; y < maxY; y++) {
visitor.visit(new BlockPos(x, y, minZ));
visitor.visit(new BlockPos(x, y, maxZ));
}
}
}
public void swapRegions(final ServerWorld srcWorld, final int srcX, final int srcY, final int srcZ, final ServerWorld dstWorld,
final int dstX, final int dstY, final int dstZ, final int scaleX, final int scaleY, final int scaleZ) {
AEApi.instance().definitions().blocks().matrixFrame().maybeBlock()
.ifPresent(matrixFrameBlock -> this.transverseEdges(dstX - 1, dstY - 1, dstZ - 1, dstX + scaleX + 1,
dstY + scaleY + 1, dstZ + scaleZ + 1,
new WrapInMatrixFrame(matrixFrameBlock.getDefaultState(), dstWorld)));
final Box srcBox = new Box(srcX, srcY, srcZ, srcX + scaleX + 1, srcY + scaleY + 1,
srcZ + scaleZ + 1);
final Box dstBox = new Box(dstX, dstY, dstZ, dstX + scaleX + 1, dstY + scaleY + 1,
dstZ + scaleZ + 1);
final CachedPlane cDst = new CachedPlane(dstWorld, dstX, dstY, dstZ, dstX + scaleX, dstY + scaleY,
dstZ + scaleZ);
final CachedPlane cSrc = new CachedPlane(srcWorld, srcX, srcY, srcZ, srcX + scaleX, srcY + scaleY,
srcZ + scaleZ);
// do nearly all the work... swaps blocks, tiles, and block ticks
cSrc.swap(cDst);
final List<Entity> srcE = srcWorld.getEntitiesIncludingUngeneratedChunks(Entity.class, srcBox);
final List<Entity> dstE = dstWorld.getEntitiesIncludingUngeneratedChunks(Entity.class, dstBox);
for (final Entity e : dstE) {
this.teleportEntity(e, new TelDestination(srcWorld, srcBox, e.getX(), e.getY(), e.getZ(),
-dstX + srcX, -dstY + srcY, -dstZ + srcZ));
}
for (final Entity e : srcE) {
this.teleportEntity(e, new TelDestination(dstWorld, dstBox, e.getX(), e.getY(), e.getZ(),
-srcX + dstX, -srcY + dstY, -srcZ + dstZ));
}
for (final WorldCoord wc : cDst.getUpdates()) {
cSrc.getWorld().updateNeighborsAlways(wc.getPos(), Blocks.AIR);
}
for (final WorldCoord wc : cSrc.getUpdates()) {
cSrc.getWorld().updateNeighborsAlways(wc.getPos(), Blocks.AIR);
}
this.transverseEdges(srcX - 1, srcY - 1, srcZ - 1, srcX + scaleX + 1, srcY + scaleY + 1, srcZ + scaleZ + 1,
new TriggerUpdates(srcWorld));
this.transverseEdges(dstX - 1, dstY - 1, dstZ - 1, dstX + scaleX + 1, dstY + scaleY + 1, dstZ + scaleZ + 1,
new TriggerUpdates(dstWorld));
this.transverseEdges(srcX, srcY, srcZ, srcX + scaleX, srcY + scaleY, srcZ + scaleZ,
new TriggerUpdates(srcWorld));
this.transverseEdges(dstX, dstY, dstZ, dstX + scaleX, dstY + scaleY, dstZ + scaleZ,
new TriggerUpdates(dstWorld));
}
private static class TriggerUpdates implements ISpatialVisitor {
private final World dst;
public TriggerUpdates(final World dst2) {
this.dst = dst2;
}
@Override
public void visit(final BlockPos pos) {
final BlockState state = this.dst.getBlockState(pos);
final Block blk = state.getBlock();
blk.neighborUpdate(state, this.dst, pos, blk, pos, false);
}
}
private static class WrapInMatrixFrame implements ISpatialVisitor {
private final World dst;
private final BlockState state;
public WrapInMatrixFrame(final BlockState state, final World dst2) {
this.dst = dst2;
this.state = state;
}
@Override
public void visit(final BlockPos pos) {
this.dst.setBlockState(pos, this.state);
}
}
private static class TelDestination {
private final ServerWorld dim;
private final double x;
private final double y;
private final double z;
TelDestination(final ServerWorld dimension, final Box srcBox, final double x, final double y,
final double z, final int tileX, final int tileY, final int tileZ) {
this.dim = dimension;
this.x = Math.min(srcBox.maxX - 0.5, Math.max(srcBox.minX + 0.5, x + tileX));
this.y = Math.min(srcBox.maxY - 0.5, Math.max(srcBox.minY + 0.5, y + tileY));
this.z = Math.min(srcBox.maxZ - 0.5, Math.max(srcBox.minZ + 0.5, z + tileZ));
}
}
}