One Spatial Dimension (#4570)
* Ported the "one spatial dimension" concept from pre-1.15 * Apply lighting updates after moving chunks. Mark world data as dirty when changing it. * Consolidated naming of spatial storage world/dimension. Use server light manager to update lighting info in chunk. * Save last transition information, and add ae2 spatial command to interact with spatial storage. * Formatting fixes. * Improved docs for the origin generation. Inverted condition on when the x/z axis are flipped. Also added a helper to map a plot to a region file * Remove TransitionResult since it's only used in lieu of a boolean. Rework transition to make the checks BEFORE allocating a new cell. * Removed unused imports. Co-authored-by: yueh <yueh@users.noreply.github.com>
This commit is contained in:
@@ -1,38 +0,0 @@
|
|||||||
/*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (c) 2013 AlgorithmX2
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
* this software and associated documentation files (the "Software"), to deal in
|
|
||||||
* the Software without restriction, including without limitation the rights to
|
|
||||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
||||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
|
||||||
* subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
||||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
||||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
||||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package appeng.api.implementations;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines the result of performing a transition from the world into a storage
|
|
||||||
* cell, if its possible, and what the energy usage is.
|
|
||||||
*/
|
|
||||||
public class TransitionResult {
|
|
||||||
public final boolean success;
|
|
||||||
public final double energyUsage;
|
|
||||||
|
|
||||||
public TransitionResult(final boolean success, final double power) {
|
|
||||||
this.success = success;
|
|
||||||
this.energyUsage = power;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -25,11 +25,8 @@ package appeng.api.implementations.items;
|
|||||||
|
|
||||||
import net.minecraft.item.Item;
|
import net.minecraft.item.Item;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.util.RegistryKey;
|
|
||||||
import net.minecraft.world.World;
|
|
||||||
import net.minecraft.world.server.ServerWorld;
|
import net.minecraft.world.server.ServerWorld;
|
||||||
|
|
||||||
import appeng.api.implementations.TransitionResult;
|
|
||||||
import appeng.api.util.WorldCoord;
|
import appeng.api.util.WorldCoord;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -52,13 +49,13 @@ public interface ISpatialStorageCell {
|
|||||||
int getMaxStoredDim(ItemStack is);
|
int getMaxStoredDim(ItemStack is);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get the currently stored Dimension id.
|
* get the currently stored spatial storage plot id.
|
||||||
*
|
*
|
||||||
* @param is spatial storage cell
|
* @param is spatial storage cell
|
||||||
*
|
*
|
||||||
* @return dimension id or -1
|
* @return plot id or -1
|
||||||
*/
|
*/
|
||||||
RegistryKey<World> getStoredDimension(ItemStack is);
|
int getAllocatedPlotId(ItemStack is);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform a spatial swap with the contents of the cell, and the world.
|
* Perform a spatial swap with the contents of the cell, and the world.
|
||||||
@@ -69,7 +66,7 @@ public interface ISpatialStorageCell {
|
|||||||
* @param max max coord
|
* @param max max coord
|
||||||
* @param playerId owner of current grid or -1
|
* @param playerId owner of current grid or -1
|
||||||
*
|
*
|
||||||
* @return result of transition
|
* @return success of transition
|
||||||
*/
|
*/
|
||||||
TransitionResult doSpatialTransition(ItemStack is, ServerWorld w, WorldCoord min, WorldCoord max, int playerId);
|
boolean doSpatialTransition(ItemStack is, ServerWorld w, WorldCoord min, WorldCoord max, int playerId);
|
||||||
}
|
}
|
||||||
@@ -1,51 +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.api.storage;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import net.minecraft.util.RegistryKey;
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
|
||||||
import net.minecraft.util.text.ITextComponent;
|
|
||||||
import net.minecraft.world.World;
|
|
||||||
import net.minecraft.world.server.ServerWorld;
|
|
||||||
|
|
||||||
public interface ISpatialDimension {
|
|
||||||
ServerWorld getWorld(RegistryKey<World> cellDim);
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
RegistryKey<World> createNewCellDimension(BlockPos capacity);
|
|
||||||
|
|
||||||
void deleteCellDimension(RegistryKey<World> cellDim);
|
|
||||||
|
|
||||||
boolean isCellDimension(RegistryKey<World> cellDim);
|
|
||||||
|
|
||||||
BlockPos getCellDimensionOrigin(RegistryKey<World> cellDim);
|
|
||||||
|
|
||||||
BlockPos getCellDimensionSize(RegistryKey<World> cellDim);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a user-facing tooltip that describes this dimension so that a player can
|
|
||||||
* keep storage cells apart.
|
|
||||||
*/
|
|
||||||
void addCellDimensionTooltip(RegistryKey<World> cellDim, List<ITextComponent> lines);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -23,10 +23,8 @@ import java.util.List;
|
|||||||
|
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.client.util.ITooltipFlag;
|
import net.minecraft.client.util.ITooltipFlag;
|
||||||
import net.minecraft.item.ItemModelsProperties;
|
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.nbt.CompoundNBT;
|
import net.minecraft.nbt.CompoundNBT;
|
||||||
import net.minecraft.util.ResourceLocation;
|
|
||||||
import net.minecraft.util.text.ITextComponent;
|
import net.minecraft.util.text.ITextComponent;
|
||||||
import net.minecraft.util.text.TranslationTextComponent;
|
import net.minecraft.util.text.TranslationTextComponent;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|||||||
@@ -30,14 +30,10 @@ import net.minecraft.block.DispenserBlock;
|
|||||||
import net.minecraft.dispenser.IDispenseItemBehavior;
|
import net.minecraft.dispenser.IDispenseItemBehavior;
|
||||||
import net.minecraft.item.Item;
|
import net.minecraft.item.Item;
|
||||||
import net.minecraft.item.ItemGroup;
|
import net.minecraft.item.ItemGroup;
|
||||||
import net.minecraft.item.ItemModelsProperties;
|
|
||||||
import net.minecraft.util.ResourceLocation;
|
|
||||||
import net.minecraftforge.api.distmarker.Dist;
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||||
|
|
||||||
import appeng.api.features.AEFeature;
|
import appeng.api.features.AEFeature;
|
||||||
import appeng.block.AEBaseBlockItemChargeable;
|
|
||||||
import appeng.bootstrap.components.IClientSetupComponent;
|
|
||||||
import appeng.bootstrap.components.IInitComponent;
|
import appeng.bootstrap.components.IInitComponent;
|
||||||
import appeng.bootstrap.components.IItemRegistrationComponent;
|
import appeng.bootstrap.components.IItemRegistrationComponent;
|
||||||
import appeng.core.AEItemGroup;
|
import appeng.core.AEItemGroup;
|
||||||
|
|||||||
@@ -220,13 +220,15 @@ import appeng.recipes.game.FacadeRecipe;
|
|||||||
import appeng.recipes.handlers.GrinderRecipeSerializer;
|
import appeng.recipes.handlers.GrinderRecipeSerializer;
|
||||||
import appeng.recipes.handlers.InscriberRecipeSerializer;
|
import appeng.recipes.handlers.InscriberRecipeSerializer;
|
||||||
import appeng.server.AECommand;
|
import appeng.server.AECommand;
|
||||||
import appeng.spatial.StorageCellBiome;
|
import appeng.spatial.SpatialStorageBiome;
|
||||||
import appeng.spatial.StorageChunkGenerator;
|
import appeng.spatial.SpatialStorageChunkGenerator;
|
||||||
|
import appeng.spatial.SpatialStorageDimensionIds;
|
||||||
import appeng.tile.AEBaseTileEntity;
|
import appeng.tile.AEBaseTileEntity;
|
||||||
import appeng.tile.crafting.MolecularAssemblerRenderer;
|
import appeng.tile.crafting.MolecularAssemblerRenderer;
|
||||||
import appeng.worldgen.ChargedQuartzOreConfig;
|
import appeng.worldgen.ChargedQuartzOreConfig;
|
||||||
import appeng.worldgen.ChargedQuartzOreFeature;
|
import appeng.worldgen.ChargedQuartzOreFeature;
|
||||||
import appeng.worldgen.meteorite.MeteoriteStructure;
|
import appeng.worldgen.meteorite.MeteoriteStructure;
|
||||||
|
import appeng.worldgen.meteorite.MeteoriteStructurePiece;
|
||||||
|
|
||||||
final class Registration {
|
final class Registration {
|
||||||
|
|
||||||
@@ -680,6 +682,8 @@ final class Registration {
|
|||||||
new ResourceLocation(dimension));
|
new ResourceLocation(dimension));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MeteoriteStructurePiece.register();
|
||||||
|
|
||||||
ForgeRegistries.BIOMES.forEach(b -> {
|
ForgeRegistries.BIOMES.forEach(b -> {
|
||||||
addMeteoriteWorldGen(b);
|
addMeteoriteWorldGen(b);
|
||||||
addQuartzWorldGen(b);
|
addQuartzWorldGen(b);
|
||||||
@@ -739,11 +743,12 @@ final class Registration {
|
|||||||
MeteoriteStructure.INSTANCE.setRegistryName(MeteoriteStructure.ID),
|
MeteoriteStructure.INSTANCE.setRegistryName(MeteoriteStructure.ID),
|
||||||
GenerationStage.Decoration.TOP_LAYER_MODIFICATION);
|
GenerationStage.Decoration.TOP_LAYER_MODIFICATION);
|
||||||
|
|
||||||
Registry.register(Registry.CHUNK_GENERATOR_CODEC, AppEng.makeId("storage"), StorageChunkGenerator.CODEC);
|
Registry.register(Registry.CHUNK_GENERATOR_CODEC, SpatialStorageDimensionIds.CHUNK_GENERATOR_ID,
|
||||||
|
SpatialStorageChunkGenerator.CODEC);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerBiomes(RegistryEvent.Register<Biome> evt) {
|
public void registerBiomes(RegistryEvent.Register<Biome> evt) {
|
||||||
evt.getRegistry().register(StorageCellBiome.INSTANCE.setRegistryName(AppEng.makeId("storage")));
|
evt.getRegistry().register(SpatialStorageBiome.INSTANCE.setRegistryName(SpatialStorageDimensionIds.BIOME_ID));
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnlyIn(Dist.CLIENT)
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
package appeng.hooks;
|
|
||||||
|
|
||||||
import net.minecraft.util.RegistryKey;
|
|
||||||
import net.minecraft.world.DimensionType;
|
|
||||||
import net.minecraft.world.World;
|
|
||||||
import net.minecraft.world.gen.ChunkGenerator;
|
|
||||||
import net.minecraft.world.server.ServerWorld;
|
|
||||||
|
|
||||||
public interface DynamicDimensions {
|
|
||||||
|
|
||||||
ServerWorld addWorld(RegistryKey<World> worldId, RegistryKey<DimensionType> dimensionTypeId,
|
|
||||||
ChunkGenerator chunkGenerator);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -29,12 +29,10 @@ import net.minecraft.block.material.Material;
|
|||||||
import net.minecraft.client.util.ITooltipFlag;
|
import net.minecraft.client.util.ITooltipFlag;
|
||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
import net.minecraft.item.ItemGroup;
|
import net.minecraft.item.ItemGroup;
|
||||||
import net.minecraft.item.ItemModelsProperties;
|
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.nbt.CompoundNBT;
|
import net.minecraft.nbt.CompoundNBT;
|
||||||
import net.minecraft.util.IItemProvider;
|
import net.minecraft.util.IItemProvider;
|
||||||
import net.minecraft.util.NonNullList;
|
import net.minecraft.util.NonNullList;
|
||||||
import net.minecraft.util.ResourceLocation;
|
|
||||||
import net.minecraft.util.math.MathHelper;
|
import net.minecraft.util.math.MathHelper;
|
||||||
import net.minecraft.util.text.ITextComponent;
|
import net.minecraft.util.text.ITextComponent;
|
||||||
import net.minecraft.util.text.StringTextComponent;
|
import net.minecraft.util.text.StringTextComponent;
|
||||||
|
|||||||
@@ -18,35 +18,40 @@
|
|||||||
|
|
||||||
package appeng.items.storage;
|
package appeng.items.storage;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
import net.minecraft.client.util.ITooltipFlag;
|
import net.minecraft.client.util.ITooltipFlag;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.nbt.CompoundNBT;
|
import net.minecraft.nbt.CompoundNBT;
|
||||||
import net.minecraft.util.RegistryKey;
|
|
||||||
import net.minecraft.util.ResourceLocation;
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.util.registry.Registry;
|
|
||||||
import net.minecraft.util.text.ITextComponent;
|
import net.minecraft.util.text.ITextComponent;
|
||||||
import net.minecraft.util.text.StringTextComponent;
|
|
||||||
import net.minecraft.util.text.TextFormatting;
|
import net.minecraft.util.text.TextFormatting;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
import net.minecraft.world.server.ServerWorld;
|
import net.minecraft.world.server.ServerWorld;
|
||||||
import net.minecraftforge.api.distmarker.Dist;
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||||
|
import net.minecraftforge.common.util.Constants;
|
||||||
|
|
||||||
import appeng.api.implementations.TransitionResult;
|
|
||||||
import appeng.api.implementations.items.ISpatialStorageCell;
|
import appeng.api.implementations.items.ISpatialStorageCell;
|
||||||
import appeng.api.storage.ISpatialDimension;
|
|
||||||
import appeng.api.util.WorldCoord;
|
import appeng.api.util.WorldCoord;
|
||||||
import appeng.core.AELog;
|
import appeng.core.AELog;
|
||||||
import appeng.core.localization.GuiText;
|
import appeng.core.localization.GuiText;
|
||||||
import appeng.items.AEBaseItem;
|
import appeng.items.AEBaseItem;
|
||||||
import appeng.spatial.SpatialDimensionManager;
|
import appeng.spatial.SpatialStorageHelper;
|
||||||
import appeng.spatial.StorageHelper;
|
import appeng.spatial.SpatialStoragePlot;
|
||||||
|
import appeng.spatial.SpatialStoragePlotManager;
|
||||||
|
import appeng.spatial.TransitionInfo;
|
||||||
|
|
||||||
public class SpatialStorageCellItem extends AEBaseItem implements ISpatialStorageCell {
|
public class SpatialStorageCellItem extends AEBaseItem implements ISpatialStorageCell {
|
||||||
private static final String TAG_DIMENSION_ID = "dimension_id";
|
private static final String TAG_PLOT_ID = "plot_id";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is only stored in the itemstack to display in the tooltip on the
|
||||||
|
* client-side.
|
||||||
|
*/
|
||||||
|
private static final String TAG_PLOT_SIZE = "plot_size";
|
||||||
|
|
||||||
private final int maxRegion;
|
private final int maxRegion;
|
||||||
|
|
||||||
@@ -59,18 +64,22 @@ public class SpatialStorageCellItem extends AEBaseItem implements ISpatialStorag
|
|||||||
@Override
|
@Override
|
||||||
public void addInformation(final ItemStack stack, final World world, final List<ITextComponent> lines,
|
public void addInformation(final ItemStack stack, final World world, final List<ITextComponent> lines,
|
||||||
final ITooltipFlag advancedTooltips) {
|
final ITooltipFlag advancedTooltips) {
|
||||||
final RegistryKey<World> worldId = this.getStoredDimension(stack);
|
int plotId = this.getAllocatedPlotId(stack);
|
||||||
if (worldId == null) {
|
if (plotId == -1) {
|
||||||
lines.add(GuiText.Unformatted.text().deepCopy().mergeStyle(TextFormatting.ITALIC));
|
lines.add(GuiText.Unformatted.text().deepCopy().mergeStyle(TextFormatting.ITALIC));
|
||||||
lines.add(GuiText.SpatialCapacity.text(maxRegion, maxRegion, maxRegion));
|
lines.add(GuiText.SpatialCapacity.text(maxRegion, maxRegion, maxRegion));
|
||||||
} else {
|
return;
|
||||||
SpatialDimensionManager.INSTANCE.addCellDimensionTooltip(worldId, lines);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (advancedTooltips.isAdvanced()) {
|
// Add a serial number to allows players to keep different cells apart
|
||||||
if (worldId != null && worldId.func_240901_a_() != null) {
|
// Try to make this a little more flavorful.
|
||||||
lines.add(new StringTextComponent("Dimension: " + worldId.func_240901_a_()));
|
String serialNumber = String.format(Locale.ROOT, "SP-%04d", plotId);
|
||||||
}
|
lines.add(GuiText.SerialNumber.text(serialNumber));
|
||||||
|
|
||||||
|
CompoundNBT tag = stack.getTag();
|
||||||
|
if (tag != null && tag.contains(TAG_PLOT_SIZE, Constants.NBT.TAG_LONG)) {
|
||||||
|
BlockPos size = BlockPos.fromLong(tag.getLong(TAG_PLOT_SIZE));
|
||||||
|
lines.add(GuiText.StoredSize.text(size.getX(), size.getY(), size.getZ()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,70 +94,80 @@ public class SpatialStorageCellItem extends AEBaseItem implements ISpatialStorag
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RegistryKey<World> getStoredDimension(final ItemStack is) {
|
public int getAllocatedPlotId(final ItemStack is) {
|
||||||
final CompoundNBT c = is.getTag();
|
final CompoundNBT c = is.getTag();
|
||||||
if (c != null && c.contains(TAG_DIMENSION_ID)) {
|
if (c != null && c.contains(TAG_PLOT_ID)) {
|
||||||
try {
|
try {
|
||||||
ResourceLocation worldId = new ResourceLocation(c.getString(TAG_DIMENSION_ID));
|
int plotId = c.getInt(TAG_PLOT_ID);
|
||||||
return RegistryKey.func_240903_a_(Registry.WORLD_KEY, worldId);
|
if (SpatialStoragePlotManager.INSTANCE.getPlot(plotId) == null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return plotId;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
AELog.warn("Failed to retrieve storage cell dimension.", e);
|
AELog.warn("Failed to retrieve spatial storage dimension: %s", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TransitionResult doSpatialTransition(final ItemStack is, final ServerWorld w, final WorldCoord min,
|
public boolean doSpatialTransition(final ItemStack is, final ServerWorld w, final WorldCoord min,
|
||||||
final WorldCoord max, int playerId) {
|
final WorldCoord max, int playerId) {
|
||||||
final int targetX = max.x - min.x - 1;
|
final int targetX = max.x - min.x - 1;
|
||||||
final int targetY = max.y - min.y - 1;
|
final int targetY = max.y - min.y - 1;
|
||||||
final int targetZ = max.z - min.z - 1;
|
final int targetZ = max.z - min.z - 1;
|
||||||
final int maxSize = this.getMaxStoredDim(is);
|
final int maxSize = this.getMaxStoredDim(is);
|
||||||
|
if (targetX > maxSize && targetY > maxSize && targetZ > maxSize) {
|
||||||
|
AELog.info(
|
||||||
|
"Failing spatial transition because the transfer area (%dx%dx%d) exceeds the cell capacity (%s).",
|
||||||
|
targetX, targetY, targetZ, maxSize);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
final BlockPos targetSize = new BlockPos(targetX, targetY, targetZ);
|
final BlockPos targetSize = new BlockPos(targetX, targetY, targetZ);
|
||||||
|
|
||||||
ISpatialDimension manager = SpatialDimensionManager.INSTANCE;
|
SpatialStoragePlotManager manager = SpatialStoragePlotManager.INSTANCE;
|
||||||
|
|
||||||
RegistryKey<World> worldId = this.getStoredDimension(is);
|
SpatialStoragePlot plot = SpatialStoragePlotManager.INSTANCE.getPlot(this.getAllocatedPlotId(is));
|
||||||
if (worldId == null || manager.getWorld(worldId) == null) {
|
if (plot != null) {
|
||||||
worldId = manager.createNewCellDimension(targetSize);
|
// Check that the existing plot has the right size
|
||||||
|
if (!plot.getSize().equals(targetSize)) {
|
||||||
|
AELog.info(
|
||||||
|
"Failing spatial transition because the transfer area (%dx%dx%d) does not match the spatial storage plot's size (%s).",
|
||||||
|
targetX, targetY, targetZ, plot.getSize());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Otherwise allocate a new one
|
||||||
|
plot = manager.allocatePlot(targetSize, playerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (worldId == null) {
|
// Store some information about this transition in the plot
|
||||||
// Failed to create the dimension
|
TransitionInfo info = new TransitionInfo(w.func_234923_W_().func_240901_a_(), min.getBlockPos(),
|
||||||
return new TransitionResult(false, 0);
|
max.getBlockPos(), Instant.now());
|
||||||
}
|
manager.setLastTransition(plot.getId(), info);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (manager.isCellDimension(worldId)) {
|
ServerWorld cellWorld = manager.getWorld();
|
||||||
ServerWorld cellWorld = manager.getWorld(worldId);
|
|
||||||
|
|
||||||
BlockPos scale = manager.getCellDimensionSize(worldId);
|
BlockPos offset = plot.getOrigin();
|
||||||
|
|
||||||
if (scale.equals(targetSize)) {
|
this.setStoredDimension(is, plot.getId(), plot.getSize());
|
||||||
if (targetX <= maxSize && targetY <= maxSize && targetZ <= maxSize) {
|
SpatialStorageHelper.getInstance().swapRegions(w, min.x + 1, min.y + 1, min.z + 1, cellWorld, offset.getX(),
|
||||||
BlockPos offset = manager.getCellDimensionOrigin(worldId);
|
offset.getY(), offset.getZ(), targetX - 1, targetY - 1, targetZ - 1);
|
||||||
|
|
||||||
this.setStoredDimension(is, worldId);
|
return true;
|
||||||
StorageHelper.getInstance().swapRegions(w, min.x + 1, min.y + 1, min.z + 1, cellWorld,
|
|
||||||
offset.getX(), offset.getY(), offset.getZ(), targetX - 1, targetY - 1, targetZ - 1);
|
|
||||||
|
|
||||||
return new TransitionResult(true, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new TransitionResult(false, 0);
|
|
||||||
} finally {
|
} finally {
|
||||||
// clean up newly created dimensions that failed transfer
|
// clean up newly created dimensions that failed transfer
|
||||||
if (manager.isCellDimension(worldId) && this.getStoredDimension(is) == null) {
|
if (this.getAllocatedPlotId(is) == -1) {
|
||||||
manager.deleteCellDimension(worldId);
|
manager.freePlot(plot.getId(), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setStoredDimension(final ItemStack is, RegistryKey<World> worldId) {
|
public void setStoredDimension(final ItemStack is, int plotId, BlockPos size) {
|
||||||
final CompoundNBT c = is.getOrCreateTag();
|
final CompoundNBT c = is.getOrCreateTag();
|
||||||
c.putString(TAG_DIMENSION_ID, worldId.func_240901_a_().toString());
|
c.putInt(TAG_PLOT_ID, plotId);
|
||||||
|
c.putLong(TAG_PLOT_SIZE, size.toLong());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ import net.minecraft.block.BlockState;
|
|||||||
import net.minecraft.client.util.ITooltipFlag;
|
import net.minecraft.client.util.ITooltipFlag;
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
import net.minecraft.item.Item;
|
import net.minecraft.item.Item;
|
||||||
import net.minecraft.item.ItemModelsProperties;
|
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.item.ItemUseContext;
|
import net.minecraft.item.ItemUseContext;
|
||||||
import net.minecraft.item.SnowballItem;
|
import net.minecraft.item.SnowballItem;
|
||||||
@@ -69,7 +68,6 @@ import appeng.block.networking.CableBusBlock;
|
|||||||
import appeng.block.paint.PaintSplotchesBlock;
|
import appeng.block.paint.PaintSplotchesBlock;
|
||||||
import appeng.core.AEConfig;
|
import appeng.core.AEConfig;
|
||||||
import appeng.core.Api;
|
import appeng.core.Api;
|
||||||
import appeng.core.AppEng;
|
|
||||||
import appeng.core.localization.GuiText;
|
import appeng.core.localization.GuiText;
|
||||||
import appeng.helpers.IMouseWheelItem;
|
import appeng.helpers.IMouseWheelItem;
|
||||||
import appeng.hooks.IBlockTool;
|
import appeng.hooks.IBlockTool;
|
||||||
|
|||||||
@@ -1,105 +0,0 @@
|
|||||||
package appeng.mixins;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
|
|
||||||
import net.minecraft.server.IDynamicRegistries;
|
|
||||||
import net.minecraft.server.MinecraftServer;
|
|
||||||
import net.minecraft.util.RegistryKey;
|
|
||||||
import net.minecraft.util.registry.Registry;
|
|
||||||
import net.minecraft.world.Dimension;
|
|
||||||
import net.minecraft.world.DimensionType;
|
|
||||||
import net.minecraft.world.World;
|
|
||||||
import net.minecraft.world.border.IBorderListener;
|
|
||||||
import net.minecraft.world.border.WorldBorder;
|
|
||||||
import net.minecraft.world.chunk.listener.IChunkStatusListener;
|
|
||||||
import net.minecraft.world.gen.ChunkGenerator;
|
|
||||||
import net.minecraft.world.gen.settings.DimensionGeneratorSettings;
|
|
||||||
import net.minecraft.world.server.ServerWorld;
|
|
||||||
import net.minecraft.world.storage.DerivedWorldInfo;
|
|
||||||
import net.minecraft.world.storage.IServerConfiguration;
|
|
||||||
import net.minecraft.world.storage.IServerWorldInfo;
|
|
||||||
import net.minecraft.world.storage.SaveFormat;
|
|
||||||
|
|
||||||
import appeng.hooks.DynamicDimensions;
|
|
||||||
|
|
||||||
@Mixin(MinecraftServer.class)
|
|
||||||
public class MinecraftServerMixin implements DynamicDimensions {
|
|
||||||
|
|
||||||
@Shadow()
|
|
||||||
private IDynamicRegistries.Impl field_240767_f_;
|
|
||||||
|
|
||||||
@Shadow
|
|
||||||
private Map<RegistryKey<World>, ServerWorld> worlds;
|
|
||||||
|
|
||||||
@Shadow
|
|
||||||
private IServerConfiguration field_240768_i_;
|
|
||||||
|
|
||||||
@Shadow
|
|
||||||
private Executor backgroundExecutor;
|
|
||||||
|
|
||||||
@Shadow
|
|
||||||
private SaveFormat.LevelSave anvilConverterForAnvilFile;
|
|
||||||
|
|
||||||
public ServerWorld addWorld(RegistryKey<World> worldId, RegistryKey<DimensionType> dimensionTypeId,
|
|
||||||
ChunkGenerator chunkGenerator) {
|
|
||||||
Preconditions.checkArgument(!worlds.containsKey(worldId), "World with id %s already exists.",
|
|
||||||
worldId.func_240901_a_());
|
|
||||||
|
|
||||||
// Each worlds must have a corresponding dimension options, otherwise they will
|
|
||||||
// not be re-created on load
|
|
||||||
DimensionGeneratorSettings generatorOptions = this.field_240768_i_.getDimensionGeneratorSettings();
|
|
||||||
Preconditions.checkArgument(!generatorOptions.func_236224_e_().containsKey(worldId.func_240901_a_()),
|
|
||||||
"Dimension config with id %s already exists.", worldId.func_240901_a_());
|
|
||||||
|
|
||||||
DimensionType dimensionType = field_240767_f_.func_230520_a_().getValueForKey(dimensionTypeId);
|
|
||||||
Preconditions.checkArgument(dimensionType != null, "dimensionTypeId %s is not registered", dimensionTypeId);
|
|
||||||
|
|
||||||
// Create dimension options with the given world-id and chunk generator
|
|
||||||
Dimension dimensionOptions = new Dimension(
|
|
||||||
() -> field_240767_f_.func_230520_a_().getValueForKey(dimensionTypeId), chunkGenerator);
|
|
||||||
|
|
||||||
IServerWorldInfo serverWorldProperties = this.field_240768_i_.func_230407_G_();
|
|
||||||
|
|
||||||
// This sucks a little, but during startup, the server will use the same
|
|
||||||
// listener for every world,
|
|
||||||
// but it is ultimately only stored in the chunk storage. So we retrieve it from
|
|
||||||
// the overworld to
|
|
||||||
// mirror the original code as much as possible
|
|
||||||
ServerWorld overworld = worlds.get(ServerWorld.field_234918_g_);
|
|
||||||
Preconditions.checkState(overworld != null, "Overworld does not exist!");
|
|
||||||
IChunkStatusListener worldGenerationProgressListener = ((ThreadedAnvilChunkStorageAccessor) overworld
|
|
||||||
.getChunkProvider().chunkManager).getWorldGenerationProgressListener();
|
|
||||||
|
|
||||||
// Inherit seed + WorldBorder from overworld
|
|
||||||
long seed = overworld.getSeed();
|
|
||||||
WorldBorder worldBorder = overworld.getWorldBorder();
|
|
||||||
|
|
||||||
MinecraftServer self = (MinecraftServer) (Object) this;
|
|
||||||
DerivedWorldInfo unmodifiableLevelProperties = new DerivedWorldInfo(this.field_240768_i_,
|
|
||||||
serverWorldProperties);
|
|
||||||
ServerWorld world = new ServerWorld(self, this.backgroundExecutor, this.anvilConverterForAnvilFile,
|
|
||||||
unmodifiableLevelProperties, worldId, dimensionTypeId, dimensionType, worldGenerationProgressListener,
|
|
||||||
chunkGenerator, false, seed, ImmutableList.of(), false);
|
|
||||||
worldBorder.addListener(new IBorderListener.Impl(world.getWorldBorder()));
|
|
||||||
this.worlds.put(worldId, world);
|
|
||||||
|
|
||||||
// Adding it here will cause the world to be re-created on server restart
|
|
||||||
RegistryKey<Dimension> dimOptKey = RegistryKey.func_240903_a_(Registry.DIMENSION_KEY, worldId.func_240901_a_());
|
|
||||||
generatorOptions.func_236224_e_().register(dimOptKey, dimensionOptions);
|
|
||||||
generatorOptions.func_236224_e_().func_239662_d_(dimOptKey); // Otherwise it wont be saved
|
|
||||||
// Ensure the save properties are saved, or the world will potentially be lost
|
|
||||||
// on restart
|
|
||||||
this.anvilConverterForAnvilFile.func_237288_a_(this.field_240767_f_, this.field_240768_i_,
|
|
||||||
self.getPlayerList().getHostPlayerData());
|
|
||||||
|
|
||||||
return world;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package appeng.mixins.spatial;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||||
|
|
||||||
|
import net.minecraft.util.RegistryKey;
|
||||||
|
import net.minecraft.world.Dimension;
|
||||||
|
|
||||||
|
import appeng.spatial.SpatialStorageDimensionIds;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This Mixin tries to hide the screen that warns users about experimental
|
||||||
|
* features being used, but only if AE2's dimension is the only added dimension.
|
||||||
|
*/
|
||||||
|
@Mixin(Dimension.class)
|
||||||
|
public class DimensionOptionMixin {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This injects right after the method makes a local copy of all present
|
||||||
|
* dimensions, and modifies the list by removing our mod-provided dimension from
|
||||||
|
* it.
|
||||||
|
* <p>
|
||||||
|
* This means Vanilla will perform it's check whether it should display an
|
||||||
|
* experimental warning without considering our dimension.
|
||||||
|
*/
|
||||||
|
@ModifyVariable(method = "func_236060_a_", at = @At(value = "INVOKE_ASSIGN", ordinal = 0, target = "Lcom/google/common/collect/Lists;newArrayList(Ljava/lang/Iterable;)Ljava/util/ArrayList;", remap = false), allow = 1)
|
||||||
|
private static List<Map.Entry<RegistryKey<Dimension>, Dimension>> overrideExperimentalCheck(
|
||||||
|
List<Map.Entry<RegistryKey<Dimension>, Dimension>> dimensions) {
|
||||||
|
// this only removes our dimension from the check
|
||||||
|
dimensions.removeIf(e -> e.getKey() == SpatialStorageDimensionIds.DIMENSION_ID);
|
||||||
|
return dimensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+8
-3
@@ -1,4 +1,4 @@
|
|||||||
package appeng.mixins;
|
package appeng.mixins.spatial;
|
||||||
|
|
||||||
import java.util.OptionalLong;
|
import java.util.OptionalLong;
|
||||||
|
|
||||||
@@ -11,15 +11,20 @@ import net.minecraft.server.IDynamicRegistries;
|
|||||||
import net.minecraft.tags.BlockTags;
|
import net.minecraft.tags.BlockTags;
|
||||||
import net.minecraft.world.DimensionType;
|
import net.minecraft.world.DimensionType;
|
||||||
|
|
||||||
import appeng.spatial.SpatialDimensionManager;
|
import appeng.spatial.SpatialStorageDimensionIds;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the storage cell world dimension type as a built-in dimension type. This
|
||||||
|
* can be registered as a JSON file as well, but doing so will trigger an
|
||||||
|
* experimental feature warning when the world is being loaded.
|
||||||
|
*/
|
||||||
@Mixin(value = DimensionType.class)
|
@Mixin(value = DimensionType.class)
|
||||||
public class DimensionTypeMixin {
|
public class DimensionTypeMixin {
|
||||||
|
|
||||||
@Inject(method = "func_236027_a_", at = @At("TAIL"))
|
@Inject(method = "func_236027_a_", at = @At("TAIL"))
|
||||||
private static void addRegistryDefaults(IDynamicRegistries.Impl registryTracker, CallbackInfoReturnable<?> cir) {
|
private static void addRegistryDefaults(IDynamicRegistries.Impl registryTracker, CallbackInfoReturnable<?> cir) {
|
||||||
|
|
||||||
registryTracker.func_239774_a_(SpatialDimensionManager.STORAGE_DIMENSION_TYPE,
|
registryTracker.func_239774_a_(SpatialStorageDimensionIds.DIMENSION_TYPE_ID,
|
||||||
new DimensionType(OptionalLong.of(12000), false, false, false, false, false, false, true, false, false,
|
new DimensionType(OptionalLong.of(12000), false, false, false, false, false, false, true, false, false,
|
||||||
256, BlockTags.INFINIBURN_OVERWORLD.getName(), 1.0f));
|
256, BlockTags.INFINIBURN_OVERWORLD.getName(), 1.0f));
|
||||||
|
|
||||||
+5
-5
@@ -1,4 +1,4 @@
|
|||||||
package appeng.mixins;
|
package appeng.mixins.spatial;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@@ -11,8 +11,8 @@ import net.minecraft.client.world.DimensionRenderInfo;
|
|||||||
import net.minecraft.util.RegistryKey;
|
import net.minecraft.util.RegistryKey;
|
||||||
import net.minecraft.world.DimensionType;
|
import net.minecraft.world.DimensionType;
|
||||||
|
|
||||||
import appeng.spatial.SpatialDimensionManager;
|
import appeng.spatial.SpatialStorageDimensionIds;
|
||||||
import appeng.spatial.StorageSkyProperties;
|
import appeng.spatial.SpatialStorageSkyProperties;
|
||||||
|
|
||||||
@Mixin(DimensionRenderInfo.class)
|
@Mixin(DimensionRenderInfo.class)
|
||||||
public class SkyPropertiesMixin {
|
public class SkyPropertiesMixin {
|
||||||
@@ -20,8 +20,8 @@ public class SkyPropertiesMixin {
|
|||||||
@Inject(method = "func_239215_a_", at = @At("HEAD"), cancellable = true)
|
@Inject(method = "func_239215_a_", at = @At("HEAD"), cancellable = true)
|
||||||
private static void byDimensionType(Optional<RegistryKey<DimensionType>> optional,
|
private static void byDimensionType(Optional<RegistryKey<DimensionType>> optional,
|
||||||
CallbackInfoReturnable<DimensionRenderInfo> ci) {
|
CallbackInfoReturnable<DimensionRenderInfo> ci) {
|
||||||
if (optional.orElse(null) == SpatialDimensionManager.STORAGE_DIMENSION_TYPE) {
|
if (optional.orElse(null) == SpatialStorageDimensionIds.DIMENSION_TYPE_ID) {
|
||||||
ci.setReturnValue(StorageSkyProperties.INSTANCE);
|
ci.setReturnValue(SpatialStorageSkyProperties.INSTANCE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
+3
-3
@@ -1,4 +1,4 @@
|
|||||||
package appeng.mixins;
|
package appeng.mixins.spatial;
|
||||||
|
|
||||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
import com.mojang.blaze3d.matrix.MatrixStack;
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ import net.minecraft.client.Minecraft;
|
|||||||
import net.minecraft.client.renderer.WorldRenderer;
|
import net.minecraft.client.renderer.WorldRenderer;
|
||||||
|
|
||||||
import appeng.client.render.SpatialSkyRender;
|
import appeng.client.render.SpatialSkyRender;
|
||||||
import appeng.spatial.SpatialDimensionManager;
|
import appeng.spatial.SpatialStorageDimensionIds;
|
||||||
|
|
||||||
@Mixin(value = WorldRenderer.class)
|
@Mixin(value = WorldRenderer.class)
|
||||||
public class SkyRenderMixin {
|
public class SkyRenderMixin {
|
||||||
@@ -23,7 +23,7 @@ public class SkyRenderMixin {
|
|||||||
@SuppressWarnings("ConstantConditions")
|
@SuppressWarnings("ConstantConditions")
|
||||||
@Inject(method = "renderSky(Lcom/mojang/blaze3d/matrix/MatrixStack;F)V", at = @At("HEAD"), cancellable = true)
|
@Inject(method = "renderSky(Lcom/mojang/blaze3d/matrix/MatrixStack;F)V", at = @At("HEAD"), cancellable = true)
|
||||||
public void renderSky(MatrixStack matrices, float tickDelta, CallbackInfo ci) {
|
public void renderSky(MatrixStack matrices, float tickDelta, CallbackInfo ci) {
|
||||||
if (mc.world.func_234922_V_() == SpatialDimensionManager.STORAGE_DIMENSION_TYPE) {
|
if (mc.world.func_234922_V_() == SpatialStorageDimensionIds.DIMENSION_TYPE_ID) {
|
||||||
SpatialSkyRender.getInstance().render(matrices);
|
SpatialSkyRender.getInstance().render(matrices);
|
||||||
ci.cancel();
|
ci.cancel();
|
||||||
}
|
}
|
||||||
@@ -28,15 +28,11 @@ import net.minecraft.block.BlockState;
|
|||||||
import net.minecraft.enchantment.EnchantmentHelper;
|
import net.minecraft.enchantment.EnchantmentHelper;
|
||||||
import net.minecraft.enchantment.Enchantments;
|
import net.minecraft.enchantment.Enchantments;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.tileentity.TileEntity;
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.world.server.ServerWorld;
|
import net.minecraft.world.server.ServerWorld;
|
||||||
import net.minecraftforge.client.model.data.IModelData;
|
import net.minecraftforge.client.model.data.IModelData;
|
||||||
|
|
||||||
import appeng.api.parts.IPart;
|
|
||||||
import appeng.api.parts.IPartHost;
|
|
||||||
import appeng.api.parts.IPartModel;
|
import appeng.api.parts.IPartModel;
|
||||||
import appeng.api.util.AEPartLocation;
|
|
||||||
import appeng.items.parts.PartModels;
|
import appeng.items.parts.PartModels;
|
||||||
|
|
||||||
public class IdentityAnnihilationPlanePart extends AnnihilationPlanePart {
|
public class IdentityAnnihilationPlanePart extends AnnihilationPlanePart {
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import net.minecraft.util.ResourceLocation;
|
|||||||
import net.minecraftforge.registries.ForgeRegistryEntry;
|
import net.minecraftforge.registries.ForgeRegistryEntry;
|
||||||
|
|
||||||
import appeng.core.AEConfig;
|
import appeng.core.AEConfig;
|
||||||
import appeng.core.AppEng;
|
|
||||||
import appeng.core.sync.BasePacket;
|
import appeng.core.sync.BasePacket;
|
||||||
|
|
||||||
public class GrinderRecipeSerializer extends ForgeRegistryEntry<IRecipeSerializer<?>>
|
public class GrinderRecipeSerializer extends ForgeRegistryEntry<IRecipeSerializer<?>>
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import net.minecraft.util.ResourceLocation;
|
|||||||
import net.minecraftforge.registries.ForgeRegistryEntry;
|
import net.minecraftforge.registries.ForgeRegistryEntry;
|
||||||
|
|
||||||
import appeng.api.features.InscriberProcessType;
|
import appeng.api.features.InscriberProcessType;
|
||||||
import appeng.core.AppEng;
|
|
||||||
import appeng.core.sync.BasePacket;
|
import appeng.core.sync.BasePacket;
|
||||||
|
|
||||||
public class InscriberRecipeSerializer extends ForgeRegistryEntry<IRecipeSerializer<?>>
|
public class InscriberRecipeSerializer extends ForgeRegistryEntry<IRecipeSerializer<?>>
|
||||||
|
|||||||
@@ -19,13 +19,15 @@
|
|||||||
package appeng.server;
|
package appeng.server;
|
||||||
|
|
||||||
import appeng.server.subcommands.ChunkLogger;
|
import appeng.server.subcommands.ChunkLogger;
|
||||||
|
import appeng.server.subcommands.SpatialStorageCommand;
|
||||||
import appeng.server.subcommands.Supporters;
|
import appeng.server.subcommands.Supporters;
|
||||||
import appeng.server.subcommands.TestMeteoritesCommand;
|
import appeng.server.subcommands.TestMeteoritesCommand;
|
||||||
import appeng.server.subcommands.TestOreGenCommand;
|
import appeng.server.subcommands.TestOreGenCommand;
|
||||||
|
|
||||||
public enum Commands {
|
public enum Commands {
|
||||||
Chunklogger(4, new ChunkLogger(), false), Supporters(0, new Supporters(), false),
|
Chunklogger(4, new ChunkLogger(), false), Supporters(0, new Supporters(), false),
|
||||||
TestOreGen(4, new TestOreGenCommand(), true), TestMeteorites(4, new TestMeteoritesCommand(), true);
|
TestOreGen(4, new TestOreGenCommand(), true), TestMeteorites(4, new TestMeteoritesCommand(), true),
|
||||||
|
Spatial(4, new SpatialStorageCommand(), false);
|
||||||
|
|
||||||
public final int level;
|
public final int level;
|
||||||
public final ISubCommand command;
|
public final ISubCommand command;
|
||||||
|
|||||||
@@ -0,0 +1,327 @@
|
|||||||
|
package appeng.server.subcommands;
|
||||||
|
|
||||||
|
import static net.minecraft.command.Commands.literal;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.function.UnaryOperator;
|
||||||
|
|
||||||
|
import com.mojang.brigadier.arguments.IntegerArgumentType;
|
||||||
|
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||||
|
import com.mojang.brigadier.context.CommandContext;
|
||||||
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
|
|
||||||
|
import net.minecraft.command.CommandException;
|
||||||
|
import net.minecraft.command.CommandSource;
|
||||||
|
import net.minecraft.command.Commands;
|
||||||
|
import net.minecraft.entity.player.ServerPlayerEntity;
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.util.text.IFormattableTextComponent;
|
||||||
|
import net.minecraft.util.text.ITextComponent;
|
||||||
|
import net.minecraft.util.text.StringTextComponent;
|
||||||
|
import net.minecraft.util.text.Style;
|
||||||
|
import net.minecraft.util.text.TextFormatting;
|
||||||
|
import net.minecraft.util.text.event.ClickEvent;
|
||||||
|
import net.minecraft.util.text.event.HoverEvent;
|
||||||
|
import net.minecraftforge.fml.server.ServerLifecycleHooks;
|
||||||
|
|
||||||
|
import appeng.core.Api;
|
||||||
|
import appeng.core.worlddata.WorldData;
|
||||||
|
import appeng.items.storage.SpatialStorageCellItem;
|
||||||
|
import appeng.server.ISubCommand;
|
||||||
|
import appeng.spatial.SpatialStorageDimensionIds;
|
||||||
|
import appeng.spatial.SpatialStoragePlot;
|
||||||
|
import appeng.spatial.SpatialStoragePlotManager;
|
||||||
|
import appeng.spatial.TransitionInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This admin command allows management of spatial storage plots.
|
||||||
|
*/
|
||||||
|
public class SpatialStorageCommand implements ISubCommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addArguments(LiteralArgumentBuilder<CommandSource> builder) {
|
||||||
|
// Shows info about a given plot or about the plot the player is currently in
|
||||||
|
builder.then(literal("info").executes(ctx -> {
|
||||||
|
showPlotInfo(ctx.getSource(), getCurrentPlot(ctx.getSource()));
|
||||||
|
return 1;
|
||||||
|
}).then(Commands.argument("plotId", IntegerArgumentType.integer(1)).executes(ctx -> {
|
||||||
|
int plotId = IntegerArgumentType.getInteger(ctx, "plotId");
|
||||||
|
showPlotInfo(ctx.getSource(), getPlot(plotId));
|
||||||
|
return 1;
|
||||||
|
})));
|
||||||
|
|
||||||
|
// Teleport into the plot
|
||||||
|
builder.then(literal("tp").then(Commands.argument("plotId", IntegerArgumentType.integer(1)).executes(ctx -> {
|
||||||
|
int plotId = IntegerArgumentType.getInteger(ctx, "plotId");
|
||||||
|
teleportToPlot(ctx.getSource(), plotId);
|
||||||
|
return 1;
|
||||||
|
})));
|
||||||
|
|
||||||
|
// Teleport from the current plot back to the source of its content, or do the
|
||||||
|
// same for a given plot id
|
||||||
|
builder.then(literal("tpback").executes(ctx -> {
|
||||||
|
teleportBack(ctx.getSource());
|
||||||
|
return 1;
|
||||||
|
}).then(Commands.argument("plotId", IntegerArgumentType.integer(1)).executes(ctx -> {
|
||||||
|
int plotId = IntegerArgumentType.getInteger(ctx, "plotId");
|
||||||
|
teleportBack(ctx.getSource(), getPlot(plotId));
|
||||||
|
return 1;
|
||||||
|
})));
|
||||||
|
|
||||||
|
// Creates a storage cell for the given plot id and gives it to the player
|
||||||
|
builder.then(
|
||||||
|
literal("givecell").then(Commands.argument("plotId", IntegerArgumentType.integer(1)).executes(ctx -> {
|
||||||
|
int plotId = IntegerArgumentType.getInteger(ctx, "plotId");
|
||||||
|
giveCell(ctx.getSource(), plotId);
|
||||||
|
return 1;
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the player is currently within a spatial storage plot, teleports them back
|
||||||
|
* to the last source of transition.
|
||||||
|
*/
|
||||||
|
private void teleportBack(CommandSource source) {
|
||||||
|
|
||||||
|
if (source.getWorld().func_234923_W_() != SpatialStorageDimensionIds.WORLD_ID) {
|
||||||
|
throw new CommandException(new StringTextComponent("Must be within the spatial storage world."));
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockPos playerPos = new BlockPos(source.getPos());
|
||||||
|
int x = playerPos.getX();
|
||||||
|
int z = playerPos.getZ();
|
||||||
|
|
||||||
|
// This is slow, but for an admin-command it's acceptable
|
||||||
|
|
||||||
|
for (SpatialStoragePlot plot : SpatialStoragePlotManager.INSTANCE.getPlots()) {
|
||||||
|
BlockPos origin = plot.getOrigin();
|
||||||
|
BlockPos size = plot.getSize();
|
||||||
|
if (x >= origin.getX() && x <= origin.getX() + size.getX() && z >= origin.getZ()
|
||||||
|
&& z <= origin.getZ() + size.getZ()) {
|
||||||
|
teleportBack(source, plot);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new CommandException(ITextComponent.func_241827_a_("Couldn't find a plot for the current position."));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Teleports back from the given plot.
|
||||||
|
*/
|
||||||
|
private void teleportBack(CommandSource source, SpatialStoragePlot plot) {
|
||||||
|
TransitionInfo lastTransition = plot.getLastTransition();
|
||||||
|
if (lastTransition == null) {
|
||||||
|
throw new CommandException(
|
||||||
|
ITextComponent.func_241827_a_("This plot doesn't have a last known transition."));
|
||||||
|
}
|
||||||
|
|
||||||
|
String command = getTeleportCommand(lastTransition.getWorldId(), lastTransition.getMin().add(0, 1, 0));
|
||||||
|
runCommandFor(source, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows detailed information about a spatial storage plot.
|
||||||
|
*/
|
||||||
|
private static void showPlotInfo(CommandSource source, SpatialStoragePlot plot) {
|
||||||
|
|
||||||
|
sendKeyValuePair(source, "Plot ID", String.valueOf(plot.getId()));
|
||||||
|
// Show the owner of the spatial storage plot
|
||||||
|
int playerId = plot.getOwner();
|
||||||
|
if (playerId != -1) {
|
||||||
|
UUID profileId = WorldData.instance().playerData().getProfileId(playerId);
|
||||||
|
|
||||||
|
if (profileId == null) {
|
||||||
|
sendKeyValuePair(source, "Owner", "Unknown AE2 player (" + playerId + ")");
|
||||||
|
} else {
|
||||||
|
MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
|
||||||
|
ServerPlayerEntity player = server.getPlayerList().getPlayerByUUID(profileId);
|
||||||
|
if (player == null) {
|
||||||
|
sendKeyValuePair(source, "Owner", "Unknown Minecraft profile (" + profileId + ")");
|
||||||
|
} else {
|
||||||
|
sendKeyValuePair(source, "Owner", player.getDisplayName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sendKeyValuePair(source, "Owner", "Unknown");
|
||||||
|
}
|
||||||
|
|
||||||
|
sendKeyValuePair(source, "Size", formatBlockPos(plot.getSize(), "x"));
|
||||||
|
|
||||||
|
// Show the plot's origin and make it clickable to teleport directly to it
|
||||||
|
String teleportToPlotCommand = getTeleportCommand(SpatialStorageDimensionIds.WORLD_ID.func_240901_a_(),
|
||||||
|
plot.getOrigin());
|
||||||
|
sendKeyValuePair(source, "Origin", new StringTextComponent(formatBlockPos(plot.getOrigin(), ","))
|
||||||
|
.modifyStyle(makeCommandLink(teleportToPlotCommand, "Teleport into plot")));
|
||||||
|
|
||||||
|
sendKeyValuePair(source, "Region file:", plot.getRegionFilename());
|
||||||
|
|
||||||
|
// Show information about what was last transfered into the plot (with a
|
||||||
|
// clickable link to the source)
|
||||||
|
TransitionInfo lastTransition = plot.getLastTransition();
|
||||||
|
if (lastTransition != null) {
|
||||||
|
source.sendFeedback(new StringTextComponent("Last Transition:").mergeStyle(TextFormatting.UNDERLINE,
|
||||||
|
TextFormatting.BOLD), true);
|
||||||
|
|
||||||
|
String sourceWorldId = lastTransition.getWorldId().toString();
|
||||||
|
IFormattableTextComponent sourceLink = new StringTextComponent(
|
||||||
|
sourceWorldId + " - " + formatBlockPos(lastTransition.getMin(), ",") + " to "
|
||||||
|
+ formatBlockPos(lastTransition.getMax(), ","));
|
||||||
|
String tpCommand = getTeleportCommand(lastTransition.getWorldId(), lastTransition.getMin().add(0, 1, 0));
|
||||||
|
sourceLink.modifyStyle(makeCommandLink(tpCommand, "Click to teleport"));
|
||||||
|
|
||||||
|
sendKeyValuePair(source, "Source", sourceLink);
|
||||||
|
sendKeyValuePair(source, "When", lastTransition.getTimestamp().toString());
|
||||||
|
} else {
|
||||||
|
source.sendFeedback(new StringTextComponent("Last Transition unknown"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void teleportToPlot(CommandSource source, int plotId) {
|
||||||
|
SpatialStoragePlot plot = getPlot(plotId);
|
||||||
|
|
||||||
|
String teleportCommand = getTeleportCommand(SpatialStorageDimensionIds.WORLD_ID.func_240901_a_(),
|
||||||
|
plot.getOrigin());
|
||||||
|
|
||||||
|
runCommandFor(source, teleportCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void giveCell(CommandSource source, int plotId) throws CommandSyntaxException {
|
||||||
|
ServerPlayerEntity player = source.asPlayer();
|
||||||
|
|
||||||
|
SpatialStoragePlot plot = getPlot(plotId);
|
||||||
|
|
||||||
|
ItemStack cell;
|
||||||
|
int longestSide = getLongestSide(plot.getSize());
|
||||||
|
if (longestSide <= 2) {
|
||||||
|
cell = Api.instance().definitions().items().spatialCell2().stack(1);
|
||||||
|
} else if (longestSide <= 16) {
|
||||||
|
cell = Api.instance().definitions().items().spatialCell16().stack(1);
|
||||||
|
} else {
|
||||||
|
cell = Api.instance().definitions().items().spatialCell128().stack(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(cell.getItem() instanceof SpatialStorageCellItem)) {
|
||||||
|
throw new CommandException(
|
||||||
|
ITextComponent.func_241827_a_("Storage cell items don't implement the storage cell interface!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
SpatialStorageCellItem spatialCellItem = (SpatialStorageCellItem) cell.getItem();
|
||||||
|
spatialCellItem.setStoredDimension(cell, plotId, plot.getSize());
|
||||||
|
|
||||||
|
player.addItemStackToInventory(cell);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getLongestSide(BlockPos size) {
|
||||||
|
return Math.max(size.getX(), Math.max(size.getY(), size.getZ()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void call(MinecraftServer srv, CommandContext<CommandSource> ctx, CommandSource sender) {
|
||||||
|
|
||||||
|
List<SpatialStoragePlot> plots = new ArrayList<>(SpatialStoragePlotManager.INSTANCE.getPlots());
|
||||||
|
|
||||||
|
// Prints the least recently used plots
|
||||||
|
plots.sort(Comparator.comparing((SpatialStoragePlot plot) -> {
|
||||||
|
TransitionInfo lastTransition = plot.getLastTransition();
|
||||||
|
if (lastTransition != null) {
|
||||||
|
return lastTransition.getTimestamp();
|
||||||
|
} else {
|
||||||
|
return Instant.MIN;
|
||||||
|
}
|
||||||
|
}).reversed());
|
||||||
|
|
||||||
|
for (int i = 0; i < Math.min(5, plots.size()); i++) {
|
||||||
|
SpatialStoragePlot plot = plots.get(i);
|
||||||
|
String size = formatBlockPos(plot.getSize(), "x");
|
||||||
|
BlockPos originPos = plot.getOrigin();
|
||||||
|
String origin = formatBlockPos(originPos, ",");
|
||||||
|
|
||||||
|
ITextComponent infoLink = new StringTextComponent("Plot #" + plot.getId())
|
||||||
|
.modifyStyle(makeCommandLink("/ae2 spatial info " + plot.getId(), "Click to show details"));
|
||||||
|
ITextComponent tpLink = new StringTextComponent("Origin: " + origin)
|
||||||
|
.modifyStyle(makeCommandLink("/ae2 spatial tp " + plot.getId(), "Click to teleport into plot"));
|
||||||
|
|
||||||
|
IFormattableTextComponent message = new StringTextComponent("").append(infoLink)
|
||||||
|
.appendString(" Size: " + size + " ").append(tpLink);
|
||||||
|
|
||||||
|
sender.sendFeedback(message, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String formatBlockPos(BlockPos size, String separator) {
|
||||||
|
return size.getX() + separator + size.getY() + separator + size.getZ();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static UnaryOperator<Style> makeCommandLink(String command, String tooltip) {
|
||||||
|
|
||||||
|
return style -> style.applyFormatting(TextFormatting.UNDERLINE)
|
||||||
|
.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, command))
|
||||||
|
.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new StringTextComponent(tooltip)));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void runCommandFor(CommandSource source, String command) {
|
||||||
|
Commands commandManager = ServerLifecycleHooks.getCurrentServer().getCommandManager();
|
||||||
|
commandManager.handleCommand(source, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getTeleportCommand(ResourceLocation worldId, BlockPos pos) {
|
||||||
|
return "/execute in " + worldId + " run tp @s " + pos.getX() + " " + (pos.getY() + 1) + " " + pos.getZ();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SpatialStoragePlot getPlot(int plotId) {
|
||||||
|
SpatialStoragePlot plot = SpatialStoragePlotManager.INSTANCE.getPlot(plotId);
|
||||||
|
if (plot == null) {
|
||||||
|
throw new CommandException(new StringTextComponent("Plot not found: " + plotId));
|
||||||
|
}
|
||||||
|
return plot;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void sendKeyValuePair(CommandSource source, String label, ITextComponent value) {
|
||||||
|
source.sendFeedback(
|
||||||
|
new StringTextComponent("")
|
||||||
|
.append(new StringTextComponent(label + ": ").mergeStyle(TextFormatting.BOLD)).append(value),
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void sendKeyValuePair(CommandSource source, String label, String value) {
|
||||||
|
sendKeyValuePair(source, label, new StringTextComponent(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the spatial storage plot that the command source is currently in.
|
||||||
|
*/
|
||||||
|
private static SpatialStoragePlot getCurrentPlot(CommandSource source) {
|
||||||
|
if (source.getWorld().func_234923_W_() != SpatialStorageDimensionIds.WORLD_ID) {
|
||||||
|
throw new CommandException(new StringTextComponent("Must be within the spatial storage world."));
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockPos playerPos = new BlockPos(source.getPos());
|
||||||
|
int x = playerPos.getX();
|
||||||
|
int z = playerPos.getZ();
|
||||||
|
|
||||||
|
// This is slow, but for an admin-command it's acceptable
|
||||||
|
for (SpatialStoragePlot plot : SpatialStoragePlotManager.INSTANCE.getPlots()) {
|
||||||
|
BlockPos origin = plot.getOrigin();
|
||||||
|
BlockPos size = plot.getSize();
|
||||||
|
if (x >= origin.getX() && x <= origin.getX() + size.getX() && z >= origin.getZ()
|
||||||
|
&& z <= origin.getZ() + size.getZ()) {
|
||||||
|
return plot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new CommandException(ITextComponent.func_241827_a_("Couldn't find a plot for the current position."));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -34,9 +34,10 @@ import net.minecraft.world.ITickList;
|
|||||||
import net.minecraft.world.NextTickListEntry;
|
import net.minecraft.world.NextTickListEntry;
|
||||||
import net.minecraft.world.chunk.Chunk;
|
import net.minecraft.world.chunk.Chunk;
|
||||||
import net.minecraft.world.chunk.ChunkSection;
|
import net.minecraft.world.chunk.ChunkSection;
|
||||||
import net.minecraft.world.server.ServerChunkProvider;
|
import net.minecraft.world.lighting.WorldLightManager;
|
||||||
import net.minecraft.world.server.ServerTickList;
|
import net.minecraft.world.server.ServerTickList;
|
||||||
import net.minecraft.world.server.ServerWorld;
|
import net.minecraft.world.server.ServerWorld;
|
||||||
|
import net.minecraft.world.server.ServerWorldLightManager;
|
||||||
|
|
||||||
import appeng.api.movable.IMovableHandler;
|
import appeng.api.movable.IMovableHandler;
|
||||||
import appeng.api.movable.IMovableRegistry;
|
import appeng.api.movable.IMovableRegistry;
|
||||||
@@ -300,12 +301,17 @@ public class CachedPlane {
|
|||||||
|
|
||||||
private void updateChunks() {
|
private void updateChunks() {
|
||||||
|
|
||||||
|
WorldLightManager lightManager = world.getLightManager();
|
||||||
|
|
||||||
// update shit..
|
// update shit..
|
||||||
for (int x = 0; x < this.cx_size; x++) {
|
if (lightManager instanceof ServerWorldLightManager) {
|
||||||
for (int z = 0; z < this.cz_size; z++) {
|
ServerWorldLightManager serverLightManager = (ServerWorldLightManager) lightManager;
|
||||||
final Chunk c = this.myChunks[x][z];
|
for (int x = 0; x < this.cx_size; x++) {
|
||||||
// FIXME: Light shit
|
for (int z = 0; z < this.cz_size; z++) {
|
||||||
c.markDirty();
|
final Chunk c = this.myChunks[x][z];
|
||||||
|
serverLightManager.lightChunk(c, false);
|
||||||
|
c.markDirty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -317,17 +323,14 @@ public class CachedPlane {
|
|||||||
|
|
||||||
WorldData.instance().compassData().service().updateArea(this.getWorld(), c);
|
WorldData.instance().compassData().service().updateArea(this.getWorld(), c);
|
||||||
|
|
||||||
// FIXME this was sending chunks to players...
|
|
||||||
SChunkDataPacket cdp = new SChunkDataPacket(c, verticalBits, false);
|
SChunkDataPacket cdp = new SChunkDataPacket(c, verticalBits, false);
|
||||||
((ServerChunkProvider) world.getChunkProvider()).chunkManager.getTrackingPlayers(c.getPos(), false)
|
world.getChunkProvider().chunkManager.getTrackingPlayers(c.getPos(), false)
|
||||||
.forEach(spe -> spe.connection.sendPacket(cdp));
|
.forEach(spe -> spe.connection.sendPacket(cdp));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME check if this makes any sense at all to send changes to players asap
|
// FIXME check if this makes any sense at all to send changes to players asap
|
||||||
ServerChunkProvider serverChunkProvider = (ServerChunkProvider) world.getChunkProvider();
|
world.getChunkProvider().tick(() -> false);
|
||||||
serverChunkProvider.tick(() -> false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<WorldCoord> getUpdates() {
|
List<WorldCoord> getUpdates() {
|
||||||
@@ -340,7 +343,6 @@ public class CachedPlane {
|
|||||||
|
|
||||||
private static class BlockStorageData {
|
private static class BlockStorageData {
|
||||||
public BlockState state;
|
public BlockState state;
|
||||||
public int light;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Column {
|
private class Column {
|
||||||
@@ -373,8 +375,6 @@ public class CachedPlane {
|
|||||||
final ChunkSection[] storage = this.c.getSections();
|
final ChunkSection[] storage = this.c.getSections();
|
||||||
final ChunkSection extendedBlockStorage = storage[y >> 4];
|
final ChunkSection extendedBlockStorage = storage[y >> 4];
|
||||||
extendedBlockStorage.setBlockState(this.x, y & 15, this.z, data.state);
|
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) {
|
private void fillData(final int y, BlockStorageData data) {
|
||||||
@@ -382,8 +382,6 @@ public class CachedPlane {
|
|||||||
final ChunkSection extendedblockstorage = storage[y >> 4];
|
final ChunkSection extendedblockstorage = storage[y >> 4];
|
||||||
|
|
||||||
data.state = extendedblockstorage.getBlockState(this.x, y & 15, this.z);
|
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) {
|
private boolean doNotSkip(final int y) {
|
||||||
|
|||||||
@@ -1,70 +0,0 @@
|
|||||||
package appeng.spatial;
|
|
||||||
|
|
||||||
import net.minecraft.nbt.CompoundNBT;
|
|
||||||
import net.minecraft.nbt.NBTUtil;
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
|
||||||
import net.minecraft.world.storage.WorldSavedData;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helps with encoding and decoding the extra data we attach to each created
|
|
||||||
* storage dimension world as persistent state.
|
|
||||||
*/
|
|
||||||
public final class SpatialDimensionExtraData extends WorldSavedData {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.ZERO;
|
|
||||||
|
|
||||||
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 read(CompoundNBT 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 = NBTUtil.readBlockPos(tag.getCompound(TAG_SIZE));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CompoundNBT write(CompoundNBT tag) {
|
|
||||||
tag.putInt(TAG_FORMAT, CURRENT_FORMAT);
|
|
||||||
tag.put(TAG_SIZE, NBTUtil.writeBlockPos(size));
|
|
||||||
return tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,201 +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.util.RegistryKey;
|
|
||||||
import net.minecraft.util.ResourceLocation;
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
|
||||||
import net.minecraft.util.registry.Registry;
|
|
||||||
import net.minecraft.util.text.ITextComponent;
|
|
||||||
import net.minecraft.world.DimensionType;
|
|
||||||
import net.minecraft.world.World;
|
|
||||||
import net.minecraft.world.server.ServerWorld;
|
|
||||||
import net.minecraftforge.fml.server.ServerLifecycleHooks;
|
|
||||||
|
|
||||||
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
|
|
||||||
.func_240903_a_(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) {
|
|
||||||
ResourceLocation dimKey = findFreeDimensionId();
|
|
||||||
AELog.info("Allocating storage cell dimension '%s'", dimKey);
|
|
||||||
|
|
||||||
DynamicDimensions dynamicDimensions = (DynamicDimensions) getServer();
|
|
||||||
|
|
||||||
RegistryKey<World> worldId = RegistryKey.func_240903_a_(Registry.WORLD_KEY, dimKey);
|
|
||||||
|
|
||||||
ServerWorld world = dynamicDimensions.addWorld(worldId, STORAGE_DIMENSION_TYPE, StorageChunkGenerator.INSTANCE);
|
|
||||||
|
|
||||||
SpatialDimensionExtraData spatialExtraData = world.getSavedData().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 ResourceLocation findFreeDimensionId() {
|
|
||||||
int maxId = 0;
|
|
||||||
for (RegistryKey<World> worldId : getServer().func_240770_D_()) {
|
|
||||||
ResourceLocation regName = worldId.func_240901_a_();
|
|
||||||
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 AppEng.makeId(DIM_ID_PREFIX + maxId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void deleteCellDimension(RegistryKey<World> worldId) {
|
|
||||||
AELog.info("Unregistering storage cell dimension %s", worldId.func_240901_a_());
|
|
||||||
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) {
|
|
||||||
ResourceLocation id = worldId.func_240901_a_();
|
|
||||||
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.func_234922_V_().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.ZERO;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addCellDimensionTooltip(RegistryKey<World> worldId, List<ITextComponent> lines) {
|
|
||||||
// Check if the cell dimension type is even registered
|
|
||||||
ResourceLocation registryName = worldId.func_240901_a_();
|
|
||||||
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.getSavedData().get(SpatialDimensionExtraData::new, SpatialDimensionExtraData.ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static MinecraftServer getServer() {
|
|
||||||
return ServerLifecycleHooks.getCurrentServer();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
+6
-3
@@ -25,11 +25,14 @@ import net.minecraft.world.gen.surfacebuilders.SurfaceBuilder;
|
|||||||
import net.minecraftforge.api.distmarker.Dist;
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||||
|
|
||||||
public class StorageCellBiome extends Biome {
|
/**
|
||||||
|
* The single biome used within the spatial storage world.
|
||||||
|
*/
|
||||||
|
public class SpatialStorageBiome extends Biome {
|
||||||
|
|
||||||
public static final StorageCellBiome INSTANCE = new StorageCellBiome();
|
public static final SpatialStorageBiome INSTANCE = new SpatialStorageBiome();
|
||||||
|
|
||||||
public StorageCellBiome() {
|
public SpatialStorageBiome() {
|
||||||
super(new Biome.Builder()
|
super(new Biome.Builder()
|
||||||
.surfaceBuilder(
|
.surfaceBuilder(
|
||||||
new ConfiguredSurfaceBuilder<>(SurfaceBuilder.NOPE, SurfaceBuilder.STONE_STONE_GRAVEL_CONFIG))
|
new ConfiguredSurfaceBuilder<>(SurfaceBuilder.NOPE, SurfaceBuilder.STONE_STONE_GRAVEL_CONFIG))
|
||||||
+8
-5
@@ -43,18 +43,21 @@ import net.minecraft.world.gen.settings.DimensionStructuresSettings;
|
|||||||
|
|
||||||
import appeng.core.Api;
|
import appeng.core.Api;
|
||||||
|
|
||||||
public class StorageChunkGenerator extends ChunkGenerator {
|
/**
|
||||||
|
* Chunk generator the spatial storage world.
|
||||||
|
*/
|
||||||
|
public class SpatialStorageChunkGenerator extends ChunkGenerator {
|
||||||
|
|
||||||
private final Blockreader columnSample;
|
private final Blockreader 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));
|
.create((instance) -> instance.stable(INSTANCE));
|
||||||
|
|
||||||
private final BlockState defaultBlockState;
|
private final BlockState defaultBlockState;
|
||||||
|
|
||||||
private StorageChunkGenerator() {
|
private SpatialStorageChunkGenerator() {
|
||||||
super(createBiomeProvider(), createSettings());
|
super(createBiomeProvider(), createSettings());
|
||||||
this.defaultBlockState = Api.instance().definitions().blocks().matrixFrame().block().getDefaultState();
|
this.defaultBlockState = Api.instance().definitions().blocks().matrixFrame().block().getDefaultState();
|
||||||
|
|
||||||
@@ -71,7 +74,7 @@ public class StorageChunkGenerator extends ChunkGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static BiomeProvider createBiomeProvider() {
|
private static BiomeProvider createBiomeProvider() {
|
||||||
return new SingleBiomeProvider(StorageCellBiome.INSTANCE);
|
return new SingleBiomeProvider(SpatialStorageBiome.INSTANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DimensionStructuresSettings createSettings() {
|
private static DimensionStructuresSettings createSettings() {
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package appeng.spatial;
|
||||||
|
|
||||||
|
import net.minecraft.util.RegistryKey;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
import net.minecraft.util.registry.Registry;
|
||||||
|
import net.minecraft.world.Dimension;
|
||||||
|
import net.minecraft.world.DimensionType;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
|
import appeng.core.AppEng;
|
||||||
|
import appeng.mixins.spatial.DimensionTypeMixin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 DimensionTypeMixin}.
|
||||||
|
*/
|
||||||
|
public static final RegistryKey<DimensionType> DIMENSION_TYPE_ID = RegistryKey
|
||||||
|
.func_240903_a_(Registry.DIMENSION_TYPE_KEY, AppEng.makeId("spatial_storage"));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID of the {@link net.minecraft.world.gen.ChunkGenerator} used for the spatial
|
||||||
|
* storage world.
|
||||||
|
*/
|
||||||
|
public static final ResourceLocation 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 ResourceLocation BIOME_ID = AppEng.makeId("spatial_storage");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID of the {@link Dimension} used for the spatial storage dimension.
|
||||||
|
* <p>
|
||||||
|
* This is defined in
|
||||||
|
* <code>data/minecraft/dimension/appliedenergistics2/spatial_storage.json</code>
|
||||||
|
*/
|
||||||
|
public static final RegistryKey<Dimension> DIMENSION_ID = RegistryKey.func_240903_a_(Registry.DIMENSION_KEY,
|
||||||
|
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.func_240903_a_(Registry.WORLD_KEY,
|
||||||
|
AppEng.makeId("spatial_storage"));
|
||||||
|
|
||||||
|
private SpatialStorageDimensionIds() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+6
-6
@@ -39,13 +39,13 @@ import appeng.api.util.WorldCoord;
|
|||||||
import appeng.core.Api;
|
import appeng.core.Api;
|
||||||
import appeng.core.AppEng;
|
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) {
|
if (instance == null) {
|
||||||
instance = new StorageHelper();
|
instance = new SpatialStorageHelper();
|
||||||
}
|
}
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
@@ -64,7 +64,7 @@ public class StorageHelper {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
oldWorld = (ServerWorld) entity.world;
|
oldWorld = (ServerWorld) entity.world;
|
||||||
newWorld = (ServerWorld) link.dim;
|
newWorld = link.dim;
|
||||||
} catch (final Throwable e) {
|
} catch (final Throwable e) {
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
@@ -98,7 +98,7 @@ public class StorageHelper {
|
|||||||
ChunkStatus.FULL, true);
|
ChunkStatus.FULL, true);
|
||||||
|
|
||||||
if (entity instanceof ServerPlayerEntity
|
if (entity instanceof ServerPlayerEntity
|
||||||
&& link.dim.func_234922_V_() == SpatialDimensionManager.STORAGE_DIMENSION_TYPE) {
|
&& link.dim.func_234922_V_() == SpatialStorageDimensionIds.DIMENSION_TYPE_ID) {
|
||||||
AppEng.instance().getAdvancementTriggers().getSpatialExplorer().trigger((ServerPlayerEntity) entity);
|
AppEng.instance().getAdvancementTriggers().getSpatialExplorer().trigger((ServerPlayerEntity) entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
package appeng.spatial;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import net.minecraft.nbt.CompoundNBT;
|
||||||
|
import net.minecraft.nbt.NBTUtil;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.util.math.ChunkPos;
|
||||||
|
import net.minecraftforge.common.util.Constants;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.getRegionCoordX(), originChunk.getRegionCoordZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompoundNBT toTag() {
|
||||||
|
CompoundNBT tag = new CompoundNBT();
|
||||||
|
tag.putInt(TAG_ID, id);
|
||||||
|
tag.put(TAG_SIZE, NBTUtil.writeBlockPos(size));
|
||||||
|
tag.putInt(TAG_OWNER, owner);
|
||||||
|
if (lastTransition != null) {
|
||||||
|
tag.put(TAG_LAST_TRANSITION, lastTransition.toTag());
|
||||||
|
}
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SpatialStoragePlot fromTag(CompoundNBT tag) {
|
||||||
|
int id = tag.getInt(TAG_ID);
|
||||||
|
BlockPos size = NBTUtil.readBlockPos(tag.getCompound(TAG_SIZE));
|
||||||
|
int ownerId = tag.getInt(TAG_OWNER);
|
||||||
|
SpatialStoragePlot plot = new SpatialStoragePlot(id, size, ownerId);
|
||||||
|
|
||||||
|
if (tag.contains(TAG_LAST_TRANSITION, Constants.NBT.TAG_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 net.minecraft.block.BlockState;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.world.server.ServerWorld;
|
||||||
|
import net.minecraftforge.fml.server.ServerLifecycleHooks;
|
||||||
|
|
||||||
|
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().getChunkProvider().getSavedData().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.getAllInBoxMutable(from, to)) {
|
||||||
|
world.setBlockState(blockPos, matrixFrame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getWorldData().removePlot(plotId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MinecraftServer getServer() {
|
||||||
|
return ServerLifecycleHooks.getCurrentServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+4
-1
@@ -7,8 +7,11 @@ import net.minecraft.util.math.vector.Vector3d;
|
|||||||
import net.minecraftforge.api.distmarker.Dist;
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines properties for how the sky in the spatial storage world is rendered.
|
||||||
|
*/
|
||||||
@OnlyIn(Dist.CLIENT)
|
@OnlyIn(Dist.CLIENT)
|
||||||
public class StorageSkyProperties {
|
public class SpatialStorageSkyProperties {
|
||||||
|
|
||||||
// See the fabric version of this to get any idea what its doing
|
// See the fabric version of this to get any idea what its doing
|
||||||
public static final DimensionRenderInfo INSTANCE = new DimensionRenderInfo(Float.NaN /* disables clouds */, false,
|
public static final DimensionRenderInfo INSTANCE = new DimensionRenderInfo(Float.NaN /* disables clouds */, false,
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
package appeng.spatial;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
|
import net.minecraft.nbt.CompoundNBT;
|
||||||
|
import net.minecraft.nbt.INBT;
|
||||||
|
import net.minecraft.nbt.ListNBT;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.world.storage.WorldSavedData;
|
||||||
|
import net.minecraftforge.common.util.Constants;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
|
|
||||||
|
import appeng.core.AELog;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extra data attached to the spatial storage world.
|
||||||
|
*/
|
||||||
|
public class SpatialStorageWorldData extends WorldSavedData {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 read(CompoundNBT 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
ListNBT plotsTag = tag.getList(TAG_PLOTS, Constants.NBT.TAG_COMPOUND);
|
||||||
|
for (INBT plotTag : plotsTag) {
|
||||||
|
SpatialStoragePlot plot = SpatialStoragePlot.fromTag((CompoundNBT) plotTag);
|
||||||
|
|
||||||
|
if (plots.containsKey(plot.getId())) {
|
||||||
|
AELog.warn("Overwriting duplicate plot id %s", plot.getId());
|
||||||
|
}
|
||||||
|
plots.put(plot.getId(), plot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompoundNBT write(CompoundNBT tag) {
|
||||||
|
tag.putInt(TAG_FORMAT, CURRENT_FORMAT);
|
||||||
|
|
||||||
|
ListNBT plotTags = new ListNBT();
|
||||||
|
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.CompoundNBT;
|
||||||
|
import net.minecraft.nbt.NBTUtil;
|
||||||
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
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 ResourceLocation worldId;
|
||||||
|
|
||||||
|
private final BlockPos min;
|
||||||
|
|
||||||
|
private final BlockPos max;
|
||||||
|
|
||||||
|
private final Instant timestamp;
|
||||||
|
|
||||||
|
public TransitionInfo(ResourceLocation worldId, BlockPos min, BlockPos max, Instant timestamp) {
|
||||||
|
this.worldId = worldId;
|
||||||
|
this.min = min.toImmutable();
|
||||||
|
this.max = max.toImmutable();
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResourceLocation getWorldId() {
|
||||||
|
return worldId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockPos getMin() {
|
||||||
|
return min;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockPos getMax() {
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Instant getTimestamp() {
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompoundNBT toTag() {
|
||||||
|
CompoundNBT tag = new CompoundNBT();
|
||||||
|
tag.putString(TAG_WORLD_ID, worldId.toString());
|
||||||
|
tag.put(TAG_MIN, NBTUtil.writeBlockPos(min));
|
||||||
|
tag.put(TAG_MAX, NBTUtil.writeBlockPos(max));
|
||||||
|
tag.putLong(TAG_TIMESTAMP, timestamp.toEpochMilli());
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TransitionInfo fromTag(CompoundNBT tag) {
|
||||||
|
ResourceLocation worldId = new ResourceLocation(tag.getString(TAG_WORLD_ID));
|
||||||
|
BlockPos min = NBTUtil.readBlockPos(tag.getCompound(TAG_MIN));
|
||||||
|
BlockPos max = NBTUtil.readBlockPos(tag.getCompound(TAG_MAX));
|
||||||
|
Instant timestamp = Instant.ofEpochMilli(tag.getLong(TAG_TIMESTAMP));
|
||||||
|
return new TransitionInfo(worldId, min, max, timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -32,7 +32,6 @@ import net.minecraftforge.items.IItemHandler;
|
|||||||
import appeng.api.config.Actionable;
|
import appeng.api.config.Actionable;
|
||||||
import appeng.api.config.PowerMultiplier;
|
import appeng.api.config.PowerMultiplier;
|
||||||
import appeng.api.config.YesNo;
|
import appeng.api.config.YesNo;
|
||||||
import appeng.api.implementations.TransitionResult;
|
|
||||||
import appeng.api.implementations.items.ISpatialStorageCell;
|
import appeng.api.implementations.items.ISpatialStorageCell;
|
||||||
import appeng.api.networking.GridFlags;
|
import appeng.api.networking.GridFlags;
|
||||||
import appeng.api.networking.IGrid;
|
import appeng.api.networking.IGrid;
|
||||||
@@ -135,14 +134,18 @@ public class SpatialIOPortTileEntity extends AENetworkInvTileEntity implements I
|
|||||||
if (Math.abs(pr - req) < req * 0.001) {
|
if (Math.abs(pr - req) < req * 0.001) {
|
||||||
final MENetworkEvent res = gi.postEvent(new MENetworkSpatialEvent(this, req));
|
final MENetworkEvent res = gi.postEvent(new MENetworkSpatialEvent(this, req));
|
||||||
if (!res.isCanceled()) {
|
if (!res.isCanceled()) {
|
||||||
int playerId = -1;
|
// Prefer player id from security system, but if unavailable, use the
|
||||||
|
// player who placed the grid node (if any)
|
||||||
|
int playerId;
|
||||||
if (this.getProxy().getSecurity().isAvailable()) {
|
if (this.getProxy().getSecurity().isAvailable()) {
|
||||||
playerId = this.getProxy().getSecurity().getOwner();
|
playerId = this.getProxy().getSecurity().getOwner();
|
||||||
|
} else {
|
||||||
|
playerId = this.getProxy().getNode().getPlayerID();
|
||||||
}
|
}
|
||||||
|
|
||||||
final TransitionResult tr = sc.doSpatialTransition(cell, serverWorld, spc.getMin(),
|
boolean success = sc.doSpatialTransition(cell, serverWorld, spc.getMin(), spc.getMax(),
|
||||||
spc.getMax(), playerId);
|
playerId);
|
||||||
if (tr.success) {
|
if (success) {
|
||||||
energy.extractAEPower(req, Actionable.MODULATE, PowerMultiplier.CONFIG);
|
energy.extractAEPower(req, Actionable.MODULATE, PowerMultiplier.CONFIG);
|
||||||
this.inv.setStackInSlot(0, ItemStack.EMPTY);
|
this.inv.setStackInSlot(0, ItemStack.EMPTY);
|
||||||
this.inv.setStackInSlot(1, cell);
|
this.inv.setStackInSlot(1, cell);
|
||||||
|
|||||||
@@ -38,6 +38,12 @@ public class MeteoriteStructurePiece extends StructurePiece {
|
|||||||
public static final IStructurePieceType TYPE = IStructurePieceType.register(MeteoriteStructurePiece::new,
|
public static final IStructurePieceType TYPE = IStructurePieceType.register(MeteoriteStructurePiece::new,
|
||||||
"AE2MTRT");
|
"AE2MTRT");
|
||||||
|
|
||||||
|
public static void register() {
|
||||||
|
// THIS MUST BE CALLED otherwise the static initializer above will not run,
|
||||||
|
// unless world generation is actually invoked, which means that chunks may
|
||||||
|
// be loaded without this being registered as a structure piece!
|
||||||
|
}
|
||||||
|
|
||||||
private PlacedMeteoriteSettings settings;
|
private PlacedMeteoriteSettings settings;
|
||||||
|
|
||||||
protected MeteoriteStructurePiece(BlockPos center, float coreRadius, CraterType craterType, FalloutMode fallout,
|
protected MeteoriteStructurePiece(BlockPos center, float coreRadius, CraterType craterType, FalloutMode fallout,
|
||||||
|
|||||||
@@ -5,15 +5,15 @@
|
|||||||
"refmap": "appliedenergistics2.mixins.refmap.json",
|
"refmap": "appliedenergistics2.mixins.refmap.json",
|
||||||
"compatibilityLevel": "JAVA_8",
|
"compatibilityLevel": "JAVA_8",
|
||||||
"mixins": [
|
"mixins": [
|
||||||
"DimensionTypeMixin",
|
"spatial.DimensionTypeMixin",
|
||||||
"MinecraftServerMixin",
|
"spatial.DimensionOptionMixin",
|
||||||
"ThreadedAnvilChunkStorageAccessor",
|
"ThreadedAnvilChunkStorageAccessor",
|
||||||
"DimensionStructuresSettingsMixin",
|
"DimensionStructuresSettingsMixin",
|
||||||
"StructureAccessor"
|
"StructureAccessor"
|
||||||
],
|
],
|
||||||
"client": [
|
"client": [
|
||||||
"SkyRenderMixin",
|
"spatial.SkyRenderMixin",
|
||||||
"SkyPropertiesMixin",
|
"spatial.SkyPropertiesMixin",
|
||||||
"unlitquad.ModelBakeryMixin",
|
"unlitquad.ModelBakeryMixin",
|
||||||
"unlitquad.BlockModelMixin",
|
"unlitquad.BlockModelMixin",
|
||||||
"unlitquad.BlockPartFaceDeserializerMixin",
|
"unlitquad.BlockPartFaceDeserializerMixin",
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"type": "appliedenergistics2:spatial_storage",
|
||||||
|
"generator": {
|
||||||
|
"type": "appliedenergistics2:spatial_storage"
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user