Working on Spatial Stuff.
This commit is contained in:
@@ -0,0 +1,408 @@
|
||||
/*
|
||||
* 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 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;
|
||||
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.packet.s2c.play.ChunkDataS2CPacket;
|
||||
import net.minecraft.server.world.ServerChunkManager;
|
||||
import net.minecraft.server.world.ServerTickScheduler;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.util.Tickable;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.ScheduledTick;
|
||||
import net.minecraft.world.TickScheduler;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
import net.minecraft.world.chunk.ChunkSection;
|
||||
import net.minecraft.world.chunk.WorldChunk;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
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 WorldChunk[][] myChunks;
|
||||
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 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 WorldChunk[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 WorldChunk c = w.getChunk(minCX + cx, minCZ + cz);
|
||||
this.myChunks[cx][cz] = c;
|
||||
|
||||
Set<BlockPos> blockEntityPositions = c.getBlockEntityPositions();
|
||||
for (BlockPos tePOS : blockEntityPositions) {
|
||||
final BlockEntity te = c.getBlockEntity(tePOS);
|
||||
if (te == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
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(tePOS);
|
||||
} 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()) {
|
||||
w.removeBlock(tePOS, false);
|
||||
} else {
|
||||
this.myColumns[tePOS.getX() - minX][tePOS.getZ() - minZ].setSkip(tePOS.getY());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (final BlockPos cp : deadTiles) {
|
||||
c.removeBlockEntity(cp);
|
||||
}
|
||||
|
||||
final long gameTime = this.getWorld().getTime();
|
||||
final TickScheduler<Block> pendingBlockTicks = this.getWorld().getBlockTickScheduler();
|
||||
if (pendingBlockTicks instanceof ServerTickScheduler) {
|
||||
List<ScheduledTick<Block>> pending = ((ServerTickScheduler<Block>) pendingBlockTicks)
|
||||
.getScheduledTicksInChunk(c.getPos(), false, true);
|
||||
for (final ScheduledTick<Block> entry : pending) {
|
||||
final BlockPos tePOS = entry.pos;
|
||||
if (tePOS.getX() >= minX && tePOS.getX() <= maxX && tePOS.getY() >= minY && tePOS.getY() <= maxY
|
||||
&& tePOS.getZ() >= minZ && tePOS.getZ() <= maxZ) {
|
||||
this.ticks.add(new ScheduledTick<>(tePOS, entry.getObject(),
|
||||
entry.time - gameTime, entry.priority));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (final BlockEntity te : this.tiles) {
|
||||
try {
|
||||
this.getWorld().blockEntities.remove(te);
|
||||
if (te instanceof Tickable) {
|
||||
this.getWorld().tickingBlockEntities.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 ScheduledTick<Block> entry : this.ticks) {
|
||||
final BlockPos tePOS = entry.pos;
|
||||
dst.addTick(tePOS.getX() - this.x_offset, tePOS.getY() - this.y_offset, tePOS.getZ() - this.z_offset,
|
||||
entry);
|
||||
}
|
||||
|
||||
for (final ScheduledTick<Block> entry : dst.ticks) {
|
||||
final BlockPos tePOS = entry.pos;
|
||||
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 ScheduledTick<Block> entry) {
|
||||
BlockPos where = new BlockPos(x + this.x_offset, y + this.y_offset, z + this.z_offset);
|
||||
this.world.getBlockTickScheduler().schedule(where, entry.getObject(), (int) entry.time,
|
||||
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.setBlockEntity(pos, 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 WorldChunk 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 WorldChunk c = this.myChunks[x][z];
|
||||
|
||||
WorldData.instance().compassData().service().updateArea((ServerWorld) this.getWorld(), c);
|
||||
|
||||
// FIXME this was sending chunks to players...
|
||||
ChunkDataS2CPacket cdp = new ChunkDataS2CPacket(c, verticalBits, false);
|
||||
((ServerChunkManager) world.getChunkManager()).threadedAnvilChunkStorage.getPlayersWatchingChunk(c.getPos(), false)
|
||||
.forEach(spe -> spe.networkHandler.sendPacket(cdp));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME check if this makes any sense at all to send changes to players asap
|
||||
ServerChunkManager serverChunkProvider = (ServerChunkManager) 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.getSectionArray();
|
||||
|
||||
// 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.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) {
|
||||
final ChunkSection[] storage = this.c.getSectionArray();
|
||||
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.getSectionArray();
|
||||
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,26 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* 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 appeng.api.storage.ISpatialDimension;
|
||||
import appeng.core.AELog;
|
||||
import appeng.core.AppEng;
|
||||
import appeng.core.localization.GuiText;
|
||||
import appeng.hooks.DynamicDimensions;
|
||||
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 javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.world.biome.BiomeEffects;
|
||||
import net.minecraft.world.gen.surfacebuilder.ConfiguredSurfaceBuilder;
|
||||
import net.minecraft.world.gen.surfacebuilder.SurfaceBuilder;
|
||||
|
||||
public class StorageCellBiome extends Biome {
|
||||
|
||||
public static final StorageCellBiome INSTANCE = new StorageCellBiome();
|
||||
|
||||
public StorageCellBiome() {
|
||||
super(new Biome.Settings().surfaceBuilder(new ConfiguredSurfaceBuilder<>(SurfaceBuilder.NOPE, SurfaceBuilder.STONE_CONFIG))
|
||||
.precipitation(Precipitation.NONE).category(Category.NONE).depth(0).scale(1)
|
||||
// Copied from the vanilla void biome
|
||||
.temperature(0.5F).downfall(0.5F)
|
||||
.effects(new BiomeEffects.Builder()
|
||||
.waterColor(4159204)
|
||||
.waterFogColor(329011)
|
||||
.fogColor(0)
|
||||
.build())
|
||||
.parent(null));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Environment(EnvType.CLIENT)
|
||||
public int getSkyColor() {
|
||||
return 0x111111;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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 appeng.api.AEApi;
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.BlockView;
|
||||
import net.minecraft.world.ChunkRegion;
|
||||
import net.minecraft.world.Heightmap;
|
||||
import net.minecraft.world.WorldAccess;
|
||||
import net.minecraft.world.biome.source.BiomeAccess;
|
||||
import net.minecraft.world.biome.source.BiomeSource;
|
||||
import net.minecraft.world.biome.source.FixedBiomeSource;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
import net.minecraft.world.gen.GenerationStep;
|
||||
import net.minecraft.world.gen.StructureAccessor;
|
||||
import net.minecraft.world.gen.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.gen.chunk.StructuresConfig;
|
||||
import net.minecraft.world.gen.chunk.VerticalBlockSample;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
|
||||
public class StorageChunkGenerator extends ChunkGenerator {
|
||||
|
||||
private final VerticalBlockSample columnSample;
|
||||
|
||||
public static final StorageChunkGenerator INSTANCE = new StorageChunkGenerator();
|
||||
|
||||
public static final Codec<StorageChunkGenerator> CODEC = RecordCodecBuilder.create((instance) ->
|
||||
instance.stable(INSTANCE));
|
||||
|
||||
private final BlockState defaultBlockState;
|
||||
|
||||
private StorageChunkGenerator() {
|
||||
super(createBiomeProvider(), createSettings());
|
||||
this.defaultBlockState = AEApi.instance().definitions().blocks().matrixFrame().block().getDefaultState();
|
||||
|
||||
// Vertical sample is mostly used for Feature generation, for those purposes we're all filled with matrix blocks
|
||||
BlockState[] columnSample = new BlockState[256];
|
||||
Arrays.fill(columnSample, this.defaultBlockState);
|
||||
this.columnSample = new VerticalBlockSample(columnSample);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Codec<? extends ChunkGenerator> method_28506() {
|
||||
return CODEC;
|
||||
}
|
||||
|
||||
private static BiomeSource createBiomeProvider() {
|
||||
return new FixedBiomeSource(StorageCellBiome.INSTANCE);
|
||||
}
|
||||
|
||||
private static StructuresConfig createSettings() {
|
||||
return new StructuresConfig(Optional.empty(), Collections.emptyMap());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildSurface(ChunkRegion region, Chunk chunk) {
|
||||
this.fillChunk(chunk);
|
||||
chunk.setShouldSave(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 getSeaLevel() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkGenerator withSeed(long seed) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void populateNoise(WorldAccess world, StructureAccessor accessor, Chunk chunk) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockView getColumnSample(int x, int z) {
|
||||
return columnSample;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight(int p_222529_1_, int p_222529_2_, Heightmap.Type heightmapType) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateFeatures(ChunkRegion region, StructureAccessor accessor) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void carve(long seed, BiomeAccess access, Chunk chunk, GenerationStep.Carver carver) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* 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 appeng.api.AEApi;
|
||||
import appeng.api.util.WorldCoord;
|
||||
import appeng.core.AppEng;
|
||||
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.server.world.ServerWorld;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Box;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
import net.minecraft.util.registry.RegistryKey;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.chunk.ChunkStatus;
|
||||
import net.minecraft.world.dimension.DimensionType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
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.getDimensionRegistryKey().equals(SpatialDimensionManager.STORAGE_DIMENSION_TYPE)) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user