mirror of
https://github.com/plexusorg/Plex-FAWE.git
synced 2025-07-04 03:56:41 +00:00
Switch to Gradle. Use git log --follow for history.
This converts the project into a multi-module Gradle build. By default, Git does not show history past a rename, so use git log --follow to see further history.
This commit is contained in:
@ -0,0 +1,246 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world;
|
||||
|
||||
import com.sk89q.worldedit.BlockVector2D;
|
||||
import com.sk89q.worldedit.EditSession;
|
||||
import com.sk89q.worldedit.MaxChangedBlocksException;
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldedit.WorldEditException;
|
||||
import com.sk89q.worldedit.blocks.BaseBlock;
|
||||
import com.sk89q.worldedit.blocks.BaseItemStack;
|
||||
import com.sk89q.worldedit.blocks.BlockID;
|
||||
import com.sk89q.worldedit.blocks.BlockType;
|
||||
import com.sk89q.worldedit.extension.platform.Platform;
|
||||
import com.sk89q.worldedit.function.mask.BlockMask;
|
||||
import com.sk89q.worldedit.function.mask.Mask;
|
||||
import com.sk89q.worldedit.function.operation.Operation;
|
||||
import com.sk89q.worldedit.util.TreeGenerator.TreeType;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.PriorityQueue;
|
||||
|
||||
/**
|
||||
* An abstract implementation of {@link World}.
|
||||
*/
|
||||
public abstract class AbstractWorld implements World {
|
||||
|
||||
private final PriorityQueue<QueuedEffect> effectQueue = new PriorityQueue<QueuedEffect>();
|
||||
private int taskId = -1;
|
||||
|
||||
@Override
|
||||
public final boolean setBlockType(Vector position, int type) {
|
||||
try {
|
||||
return setBlock(position, new BaseBlock(type));
|
||||
} catch (WorldEditException ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setBlockData(Vector position, int data) {
|
||||
try {
|
||||
setBlock(position, new BaseBlock(getLazyBlock(position).getType(), data));
|
||||
} catch (WorldEditException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean setTypeIdAndData(Vector position, int type, int data) {
|
||||
try {
|
||||
return setBlock(position, new BaseBlock(type, data));
|
||||
} catch (WorldEditException ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean setBlock(Vector pt, BaseBlock block) throws WorldEditException {
|
||||
return setBlock(pt, block, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxY() {
|
||||
return getMaximumPoint().getBlockY();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidBlockType(int type) {
|
||||
return BlockType.fromID(type) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean usesBlockData(int type) {
|
||||
// We future proof here by assuming all unknown blocks use data
|
||||
return BlockType.usesData(type) || BlockType.fromID(type) == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mask createLiquidMask() {
|
||||
return new BlockMask(this,
|
||||
new BaseBlock(BlockID.STATIONARY_LAVA, -1),
|
||||
new BaseBlock(BlockID.LAVA, -1),
|
||||
new BaseBlock(BlockID.STATIONARY_WATER, -1),
|
||||
new BaseBlock(BlockID.WATER, -1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlockType(Vector pt) {
|
||||
return getLazyBlock(pt).getType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlockData(Vector pt) {
|
||||
return getLazyBlock(pt).getData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dropItem(Vector pt, BaseItemStack item, int times) {
|
||||
for (int i = 0; i < times; ++i) {
|
||||
dropItem(pt, item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void simulateBlockMine(Vector pt) {
|
||||
BaseBlock block = getLazyBlock(pt);
|
||||
BaseItemStack stack = BlockType.getBlockDrop(block.getId(), (short) block.getData());
|
||||
|
||||
if (stack != null) {
|
||||
final int amount = stack.getAmount();
|
||||
if (amount > 1) {
|
||||
dropItem(pt, new BaseItemStack(stack.getType(), 1, stack.getData()), amount);
|
||||
} else {
|
||||
dropItem(pt, stack, amount);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
setBlock(pt, new BaseBlock(BlockID.AIR));
|
||||
} catch (WorldEditException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean generateTree(EditSession editSession, Vector pt) throws MaxChangedBlocksException {
|
||||
return generateTree(TreeType.TREE, editSession, pt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean generateBigTree(EditSession editSession, Vector pt) throws MaxChangedBlocksException {
|
||||
return generateTree(TreeType.BIG_TREE, editSession, pt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean generateBirchTree(EditSession editSession, Vector pt) throws MaxChangedBlocksException {
|
||||
return generateTree(TreeType.BIRCH, editSession, pt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean generateRedwoodTree(EditSession editSession, Vector pt) throws MaxChangedBlocksException {
|
||||
return generateTree(TreeType.REDWOOD, editSession, pt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean generateTallRedwoodTree(EditSession editSession, Vector pt) throws MaxChangedBlocksException {
|
||||
return generateTree(TreeType.TALL_REDWOOD, editSession, pt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkLoadedChunk(Vector pt) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fixAfterFastMode(Iterable<BlockVector2D> chunks) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fixLighting(Iterable<BlockVector2D> chunks) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean playEffect(Vector position, int type, int data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public boolean queueBlockBreakEffect(Platform server, Vector position, int blockId, double priority) {
|
||||
if (taskId == -1) {
|
||||
taskId = server.schedule(0, 1, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
int max = Math.max(1, Math.min(30, effectQueue.size() / 3));
|
||||
for (int i = 0; i < max; ++i) {
|
||||
if (effectQueue.isEmpty()) return;
|
||||
|
||||
effectQueue.poll().play();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (taskId == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
effectQueue.offer(new QueuedEffect(position, blockId, priority));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector getMinimumPoint() {
|
||||
return new Vector(-30000000, 0, -30000000);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector getMaximumPoint() {
|
||||
return new Vector(30000000, 255, 30000000);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Operation commit() {
|
||||
return null;
|
||||
}
|
||||
|
||||
private class QueuedEffect implements Comparable<QueuedEffect> {
|
||||
private final Vector position;
|
||||
private final int blockId;
|
||||
private final double priority;
|
||||
|
||||
private QueuedEffect(Vector position, int blockId, double priority) {
|
||||
this.position = position;
|
||||
this.blockId = blockId;
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
public void play() {
|
||||
playEffect(position, 2001, blockId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@Nullable QueuedEffect other) {
|
||||
return Double.compare(priority, other != null ? other.priority : 0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world;
|
||||
|
||||
/**
|
||||
* Thrown when there is an exception related to data handling.
|
||||
*/
|
||||
public class DataException extends Exception {
|
||||
|
||||
public DataException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public DataException() {
|
||||
super();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world;
|
||||
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Indicates an object that contains extra data identified as an NBT structure.
|
||||
* This interface is used when saving and loading objects to a serialized
|
||||
* format, but may be used in other cases.
|
||||
*/
|
||||
public interface NbtValued {
|
||||
|
||||
/**
|
||||
* Returns whether the block contains NBT data. {@link #getNbtData()}
|
||||
* must not return null if this method returns true.
|
||||
*
|
||||
* @return true if there is NBT data
|
||||
*/
|
||||
public boolean hasNbtData();
|
||||
|
||||
/**
|
||||
* Get the object's NBT data (tile entity data). The returned tag, if
|
||||
* modified in any way, should be sent to {@link #setNbtData(CompoundTag)}
|
||||
* so that the instance knows of the changes. Making changes without
|
||||
* calling {@link #setNbtData(CompoundTag)} could have unintended
|
||||
* consequences.
|
||||
*
|
||||
* <p>{@link #hasNbtData()} must return true if and only if method does
|
||||
* not return null.</p>
|
||||
*
|
||||
* @return compound tag, or null
|
||||
*/
|
||||
@Nullable
|
||||
CompoundTag getNbtData();
|
||||
|
||||
/**
|
||||
* Set the object's NBT data (tile entity data).
|
||||
*
|
||||
* @param nbtData NBT data, or null if no data
|
||||
*/
|
||||
void setNbtData(@Nullable CompoundTag nbtData);
|
||||
|
||||
}
|
@ -0,0 +1,138 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world;
|
||||
|
||||
import com.sk89q.worldedit.EditSession;
|
||||
import com.sk89q.worldedit.MaxChangedBlocksException;
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldedit.Vector2D;
|
||||
import com.sk89q.worldedit.WorldEditException;
|
||||
import com.sk89q.worldedit.blocks.BaseBlock;
|
||||
import com.sk89q.worldedit.blocks.BaseItemStack;
|
||||
import com.sk89q.worldedit.blocks.BlockID;
|
||||
import com.sk89q.worldedit.entity.BaseEntity;
|
||||
import com.sk89q.worldedit.entity.Entity;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.util.Location;
|
||||
import com.sk89q.worldedit.util.TreeGenerator.TreeType;
|
||||
import com.sk89q.worldedit.world.biome.BaseBiome;
|
||||
import com.sk89q.worldedit.world.registry.LegacyWorldData;
|
||||
import com.sk89q.worldedit.world.registry.WorldData;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A null implementation of {@link World} that drops all changes and
|
||||
* returns dummy data.
|
||||
*/
|
||||
public class NullWorld extends AbstractWorld {
|
||||
|
||||
private static final NullWorld INSTANCE = new NullWorld();
|
||||
|
||||
protected NullWorld() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "null";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setBlock(Vector position, BaseBlock block, boolean notifyAndLight) throws WorldEditException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlockLightLevel(Vector position) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean clearContainerBlockContents(Vector position) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBiome getBiome(Vector2D position) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setBiome(Vector2D position, BaseBiome biome) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dropItem(Vector position, BaseItemStack item) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean regenerate(Region region, EditSession editSession) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean generateTree(TreeType type, EditSession editSession, Vector position) throws MaxChangedBlocksException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldData getWorldData() {
|
||||
return LegacyWorldData.getInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBlock getBlock(Vector position) {
|
||||
return new BaseBlock(BlockID.AIR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBlock getLazyBlock(Vector position) {
|
||||
return new BaseBlock(BlockID.AIR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Entity> getEntities(Region region) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Entity> getEntities() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Entity createEntity(Location location, BaseEntity entity) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an instance of this null world.
|
||||
*
|
||||
* @return a null world
|
||||
*/
|
||||
public static NullWorld getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,285 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world;
|
||||
|
||||
import com.sk89q.worldedit.BlockVector2D;
|
||||
import com.sk89q.worldedit.EditSession;
|
||||
import com.sk89q.worldedit.MaxChangedBlocksException;
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldedit.WorldEditException;
|
||||
import com.sk89q.worldedit.blocks.BaseBlock;
|
||||
import com.sk89q.worldedit.blocks.BaseItemStack;
|
||||
import com.sk89q.worldedit.extension.platform.Platform;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.function.mask.Mask;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.util.TreeGenerator;
|
||||
import com.sk89q.worldedit.util.TreeGenerator.TreeType;
|
||||
import com.sk89q.worldedit.world.registry.WorldData;
|
||||
|
||||
/**
|
||||
* Represents a world (dimension).
|
||||
*/
|
||||
public interface World extends Extent {
|
||||
|
||||
/**
|
||||
* Get the name of the world.
|
||||
*
|
||||
* @return a name for the world
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Get the maximum Y.
|
||||
*
|
||||
* @return the maximum Y
|
||||
*/
|
||||
int getMaxY();
|
||||
|
||||
/**
|
||||
* Checks whether the given block ID is a valid block ID.
|
||||
*
|
||||
* @param id the block ID
|
||||
* @return true if the block ID is a valid one
|
||||
*/
|
||||
boolean isValidBlockType(int id);
|
||||
|
||||
/**
|
||||
* Checks whether the given block ID uses data values for differentiating
|
||||
* types of blocks.
|
||||
*
|
||||
* @param id the block ID
|
||||
* @return true if the block uses data values
|
||||
*/
|
||||
boolean usesBlockData(int id);
|
||||
|
||||
/**
|
||||
* Create a mask that matches all liquids.
|
||||
*
|
||||
* <p>Implementations should override this so that custom liquids
|
||||
* are supported.</p>
|
||||
*
|
||||
* @return a mask
|
||||
*/
|
||||
Mask createLiquidMask();
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #getLazyBlock(Vector)}
|
||||
*/
|
||||
@Deprecated
|
||||
int getBlockType(Vector pt);
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #getLazyBlock(Vector)}
|
||||
*/
|
||||
@Deprecated
|
||||
int getBlockData(Vector pt);
|
||||
|
||||
/**
|
||||
* Similar to {@link Extent#setBlock(Vector, BaseBlock)} but a
|
||||
* {@code notifyAndLight} parameter indicates whether adjacent blocks
|
||||
* should be notified that changes have been made and lighting operations
|
||||
* should be executed.
|
||||
*
|
||||
* <p>If it's not possible to skip lighting, or if it's not possible to
|
||||
* avoid notifying adjacent blocks, then attempt to meet the
|
||||
* specification as best as possible.</p>
|
||||
*
|
||||
* <p>On implementations where the world is not simulated, the
|
||||
* {@code notifyAndLight} parameter has no effect either way.</p>
|
||||
*
|
||||
* @param position position of the block
|
||||
* @param block block to set
|
||||
* @param notifyAndLight true to to notify and light
|
||||
* @return true if the block was successfully set (return value may not be accurate)
|
||||
*/
|
||||
boolean setBlock(Vector position, BaseBlock block, boolean notifyAndLight) throws WorldEditException;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #setBlock(Vector, BaseBlock)}
|
||||
*/
|
||||
@Deprecated
|
||||
boolean setBlockType(Vector position, int type);
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #setBlock(Vector, BaseBlock)}
|
||||
*/
|
||||
@Deprecated
|
||||
void setBlockData(Vector position, int data);
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #setBlock(Vector, BaseBlock)}
|
||||
*/
|
||||
@Deprecated
|
||||
boolean setTypeIdAndData(Vector position, int type, int data);
|
||||
|
||||
/**
|
||||
* Get the light level at the given block.
|
||||
*
|
||||
* @param position the position
|
||||
* @return the light level (0-15)
|
||||
*/
|
||||
int getBlockLightLevel(Vector position);
|
||||
|
||||
/**
|
||||
* Clear a chest's contents.
|
||||
*
|
||||
* @param position the position
|
||||
* @return true if the container was cleared
|
||||
*/
|
||||
boolean clearContainerBlockContents(Vector position);
|
||||
|
||||
/**
|
||||
* Drop an item at the given position.
|
||||
*
|
||||
* @param position the position
|
||||
* @param item the item to drop
|
||||
* @param count the number of individual stacks to drop (number of item entities)
|
||||
*/
|
||||
void dropItem(Vector position, BaseItemStack item, int count);
|
||||
|
||||
/**
|
||||
* Drop one stack of the item at the given position.
|
||||
*
|
||||
* @param position the position
|
||||
* @param item the item to drop
|
||||
* @see #dropItem(Vector, BaseItemStack, int) shortcut method to specify the number of stacks
|
||||
*/
|
||||
void dropItem(Vector position, BaseItemStack item);
|
||||
|
||||
/**
|
||||
* Simulate a block being mined at the given position.
|
||||
*
|
||||
* @param position the position
|
||||
*/
|
||||
void simulateBlockMine(Vector position);
|
||||
|
||||
/**
|
||||
* Regenerate an area.
|
||||
*
|
||||
* @param region the region
|
||||
* @param editSession the {@link EditSession}
|
||||
* @return true if re-generation was successful
|
||||
*/
|
||||
boolean regenerate(Region region, EditSession editSession);
|
||||
|
||||
/**
|
||||
* Generate a tree at the given position.
|
||||
*
|
||||
* @param type the tree type
|
||||
* @param editSession the {@link EditSession}
|
||||
* @param position the position
|
||||
* @return true if generation was successful
|
||||
* @throws MaxChangedBlocksException thrown if too many blocks were changed
|
||||
*/
|
||||
boolean generateTree(TreeGenerator.TreeType type, EditSession editSession, Vector position) throws MaxChangedBlocksException;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #generateTree(TreeType, EditSession, Vector)}
|
||||
*/
|
||||
@Deprecated
|
||||
boolean generateTree(EditSession editSession, Vector position) throws MaxChangedBlocksException;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #generateTree(TreeType, EditSession, Vector)}
|
||||
*/
|
||||
@Deprecated
|
||||
boolean generateBigTree(EditSession editSession, Vector position) throws MaxChangedBlocksException;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #generateTree(TreeType, EditSession, Vector)}
|
||||
*/
|
||||
@Deprecated
|
||||
boolean generateBirchTree(EditSession editSession, Vector position) throws MaxChangedBlocksException;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #generateTree(TreeType, EditSession, Vector)}
|
||||
*/
|
||||
@Deprecated
|
||||
boolean generateRedwoodTree(EditSession editSession, Vector position) throws MaxChangedBlocksException;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #generateTree(TreeType, EditSession, Vector)}
|
||||
*/
|
||||
@Deprecated
|
||||
boolean generateTallRedwoodTree(EditSession editSession, Vector position) throws MaxChangedBlocksException;
|
||||
|
||||
/**
|
||||
* Load the chunk at the given position if it isn't loaded.
|
||||
*
|
||||
* @param position the position
|
||||
*/
|
||||
void checkLoadedChunk(Vector position);
|
||||
|
||||
/**
|
||||
* Fix the given chunks after fast mode was used.
|
||||
*
|
||||
* <p>Fast mode makes calls to {@link #setBlock(Vector, BaseBlock, boolean)}
|
||||
* with {@code false} for the {@code notifyAndLight} parameter, which
|
||||
* may causes lighting errors to accumulate. Use of this method, if
|
||||
* it is implemented by the underlying world, corrects those lighting
|
||||
* errors and may trigger block change notifications.</p>
|
||||
*
|
||||
* @param chunks a list of chunk coordinates to fix
|
||||
*/
|
||||
void fixAfterFastMode(Iterable<BlockVector2D> chunks);
|
||||
|
||||
/**
|
||||
* Relight the given chunks if possible.
|
||||
*
|
||||
* @param chunks a list of chunk coordinates to fix
|
||||
*/
|
||||
void fixLighting(Iterable<BlockVector2D> chunks);
|
||||
|
||||
/**
|
||||
* Play the given effect.
|
||||
*
|
||||
* @param position the position
|
||||
* @param type the effect type
|
||||
* @param data the effect data
|
||||
* @return true if the effect was played
|
||||
*/
|
||||
boolean playEffect(Vector position, int type, int data);
|
||||
|
||||
/**
|
||||
* Queue a block break effect.
|
||||
*
|
||||
* @param server the server
|
||||
* @param position the position
|
||||
* @param blockId the block ID
|
||||
* @param priority the priority
|
||||
* @return true if the effect was played
|
||||
*/
|
||||
boolean queueBlockBreakEffect(Platform server, Vector position, int blockId, double priority);
|
||||
|
||||
/**
|
||||
* Get the data for blocks and so on for this world.
|
||||
*
|
||||
* @return the world data
|
||||
*/
|
||||
WorldData getWorldData();
|
||||
|
||||
@Override
|
||||
boolean equals(Object other);
|
||||
|
||||
@Override
|
||||
int hashCode();
|
||||
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.biome;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Basic storage object to represent a given biome.
|
||||
*/
|
||||
public class BaseBiome {
|
||||
|
||||
private int id;
|
||||
|
||||
/**
|
||||
* Create a new biome with the given biome ID.
|
||||
*
|
||||
* @param id the biome ID
|
||||
*/
|
||||
public BaseBiome(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a clone of the given biome.
|
||||
*
|
||||
* @param biome the biome to clone
|
||||
*/
|
||||
public BaseBiome(BaseBiome biome) {
|
||||
checkNotNull(biome);
|
||||
this.id = biome.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the biome ID.
|
||||
*
|
||||
* @return the biome ID
|
||||
*/
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the biome id.
|
||||
*
|
||||
* @param id the biome ID
|
||||
*/
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
BaseBiome baseBiome = (BaseBiome) o;
|
||||
|
||||
return id == baseBiome.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id;
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.biome;
|
||||
|
||||
/**
|
||||
* Provides information about a biome.
|
||||
*/
|
||||
public interface BiomeData {
|
||||
|
||||
/**
|
||||
* Get the name of the biome, which does not have to follow any
|
||||
* particular convention.
|
||||
*
|
||||
* @return the biome's name
|
||||
*/
|
||||
String getName();
|
||||
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.biome;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.sk89q.worldedit.world.registry.BiomeRegistry;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Returns the name of a biome using a given {@code BiomeRegistry}.
|
||||
*/
|
||||
class BiomeName implements Function<BaseBiome, String> {
|
||||
|
||||
private final BiomeRegistry registry;
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param registry the biome registry
|
||||
*/
|
||||
BiomeName(BiomeRegistry registry) {
|
||||
checkNotNull(registry);
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String apply(BaseBiome input) {
|
||||
BiomeData data = registry.getData(input);
|
||||
if (data != null) {
|
||||
return data.getName();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.biome;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Functions;
|
||||
import com.google.common.base.Optional;
|
||||
import com.sk89q.worldedit.util.WeightedChoice;
|
||||
import com.sk89q.worldedit.util.WeightedChoice.Choice;
|
||||
import com.sk89q.worldedit.util.function.LevenshteinDistance;
|
||||
import com.sk89q.worldedit.world.registry.BiomeRegistry;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Utility methods related to biomes.
|
||||
*/
|
||||
public final class Biomes {
|
||||
|
||||
private Biomes() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a biome that matches the given input name.
|
||||
*
|
||||
* @param biomes a list of biomes
|
||||
* @param name the name to test
|
||||
* @param registry a biome registry
|
||||
* @return a biome or null
|
||||
*/
|
||||
@Nullable
|
||||
public static BaseBiome findBiomeByName(Collection<BaseBiome> biomes, String name, BiomeRegistry registry) {
|
||||
checkNotNull(biomes);
|
||||
checkNotNull(name);
|
||||
checkNotNull(registry);
|
||||
|
||||
Function<String, ? extends Number> compare = new LevenshteinDistance(name, false, LevenshteinDistance.STANDARD_CHARS);
|
||||
WeightedChoice<BaseBiome> chooser = new WeightedChoice<BaseBiome>(Functions.compose(compare, new BiomeName(registry)), 0);
|
||||
for (BaseBiome biome : biomes) {
|
||||
chooser.consider(biome);
|
||||
}
|
||||
Optional<Choice<BaseBiome>> choice = chooser.getChoice();
|
||||
if (choice.isPresent() && choice.get().getScore() <= 1) {
|
||||
return choice.get().getValue();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,291 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.chunk;
|
||||
|
||||
import com.sk89q.jnbt.ByteArrayTag;
|
||||
import com.sk89q.jnbt.ByteTag;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.jnbt.IntTag;
|
||||
import com.sk89q.jnbt.ListTag;
|
||||
import com.sk89q.jnbt.NBTUtils;
|
||||
import com.sk89q.jnbt.Tag;
|
||||
import com.sk89q.worldedit.BlockVector;
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldedit.blocks.BaseBlock;
|
||||
import com.sk89q.worldedit.blocks.TileEntityBlock;
|
||||
import com.sk89q.worldedit.world.DataException;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
import com.sk89q.worldedit.world.storage.InvalidFormatException;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class AnvilChunk implements Chunk {
|
||||
|
||||
private CompoundTag rootTag;
|
||||
private byte[][] blocks;
|
||||
private byte[][] blocksAdd;
|
||||
private byte[][] data;
|
||||
private int rootX;
|
||||
private int rootZ;
|
||||
|
||||
private Map<BlockVector, Map<String,Tag>> tileEntities;
|
||||
@SuppressWarnings("unused")
|
||||
private World world; // TODO: remove if stays unused.
|
||||
|
||||
/**
|
||||
* Construct the chunk with a compound tag.
|
||||
*
|
||||
* @param world the world to construct the chunk for
|
||||
* @param tag the tag to read
|
||||
* @throws DataException on a data error
|
||||
*/
|
||||
public AnvilChunk(World world, CompoundTag tag) throws DataException {
|
||||
rootTag = tag;
|
||||
this.world = world;
|
||||
|
||||
rootX = NBTUtils.getChildTag(rootTag.getValue(), "xPos", IntTag.class).getValue();
|
||||
rootZ = NBTUtils.getChildTag(rootTag.getValue(), "zPos", IntTag.class).getValue();
|
||||
|
||||
blocks = new byte[16][16 * 16 * 16];
|
||||
blocksAdd = new byte[16][16 * 16 * 8];
|
||||
data = new byte[16][16 * 16 * 8];
|
||||
|
||||
List<Tag> sections = NBTUtils.getChildTag(rootTag.getValue(), "Sections", ListTag.class).getValue();
|
||||
|
||||
for (Tag rawSectionTag : sections) {
|
||||
if (!(rawSectionTag instanceof CompoundTag)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CompoundTag sectionTag = (CompoundTag) rawSectionTag;
|
||||
if (!sectionTag.getValue().containsKey("Y")) {
|
||||
continue; // Empty section.
|
||||
}
|
||||
|
||||
int y = NBTUtils.getChildTag(sectionTag.getValue(), "Y", ByteTag.class).getValue();
|
||||
if (y < 0 || y >= 16) {
|
||||
continue;
|
||||
}
|
||||
|
||||
blocks[y] = NBTUtils.getChildTag(sectionTag.getValue(),
|
||||
"Blocks", ByteArrayTag.class).getValue();
|
||||
data[y] = NBTUtils.getChildTag(sectionTag.getValue(), "Data",
|
||||
ByteArrayTag.class).getValue();
|
||||
|
||||
// 4096 ID block support
|
||||
if (sectionTag.getValue().containsKey("Add")) {
|
||||
blocksAdd[y] = NBTUtils.getChildTag(sectionTag.getValue(),
|
||||
"Add", ByteArrayTag.class).getValue();
|
||||
}
|
||||
}
|
||||
|
||||
int sectionsize = 16 * 16 * 16;
|
||||
for (byte[] block : blocks) {
|
||||
if (block.length != sectionsize) {
|
||||
throw new InvalidFormatException(
|
||||
"Chunk blocks byte array expected " + "to be "
|
||||
+ sectionsize + " bytes; found "
|
||||
+ block.length);
|
||||
}
|
||||
}
|
||||
|
||||
for (byte[] aData : data) {
|
||||
if (aData.length != (sectionsize / 2)) {
|
||||
throw new InvalidFormatException("Chunk block data byte array "
|
||||
+ "expected to be " + sectionsize + " bytes; found "
|
||||
+ aData.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlockID(Vector position) throws DataException {
|
||||
int x = position.getBlockX() - rootX * 16;
|
||||
int y = position.getBlockY();
|
||||
int z = position.getBlockZ() - rootZ * 16;
|
||||
|
||||
int section = y >> 4;
|
||||
if (section < 0 || section >= blocks.length) {
|
||||
throw new DataException("Chunk does not contain position " + position);
|
||||
}
|
||||
|
||||
int yindex = y & 0x0F;
|
||||
if (yindex < 0 || yindex >= 16) {
|
||||
throw new DataException("Chunk does not contain position " + position);
|
||||
}
|
||||
|
||||
int index = x + (z * 16 + (yindex * 16 * 16));
|
||||
|
||||
try {
|
||||
int addId = 0;
|
||||
|
||||
// The block ID is the combination of the Blocks byte array with the
|
||||
// Add byte array. 'Blocks' stores the lowest 8 bits of a block's ID, and
|
||||
// 'Add' stores the highest 4 bits of the ID. The first block is stored
|
||||
// in the lowest nibble in the Add byte array.
|
||||
if (index % 2 == 0) {
|
||||
addId = (blocksAdd[section][index >> 1] & 0x0F) << 8;
|
||||
} else {
|
||||
addId = (blocksAdd[section][index >> 1] & 0xF0) << 4;
|
||||
}
|
||||
|
||||
return (blocks[section][index] & 0xFF) + addId;
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
throw new DataException("Chunk does not contain position " + position);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlockData(Vector position) throws DataException {
|
||||
int x = position.getBlockX() - rootX * 16;
|
||||
int y = position.getBlockY();
|
||||
int z = position.getBlockZ() - rootZ * 16;
|
||||
|
||||
int section = y >> 4;
|
||||
int yIndex = y & 0x0F;
|
||||
|
||||
if (section < 0 || section >= blocks.length) {
|
||||
throw new DataException("Chunk does not contain position " + position);
|
||||
}
|
||||
|
||||
if (yIndex < 0 || yIndex >= 16) {
|
||||
throw new DataException("Chunk does not contain position " + position);
|
||||
}
|
||||
|
||||
int index = x + (z * 16 + (yIndex * 16 * 16));
|
||||
boolean shift = index % 2 == 0;
|
||||
index /= 2;
|
||||
|
||||
try {
|
||||
if (!shift) {
|
||||
return (data[section][index] & 0xF0) >> 4;
|
||||
} else {
|
||||
return data[section][index] & 0xF;
|
||||
}
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
throw new DataException("Chunk does not contain position " + position);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to load the tile entities.
|
||||
*
|
||||
* @throws DataException
|
||||
*/
|
||||
private void populateTileEntities() throws DataException {
|
||||
List<Tag> tags = NBTUtils.getChildTag(rootTag.getValue(),
|
||||
"TileEntities", ListTag.class).getValue();
|
||||
|
||||
tileEntities = new HashMap<BlockVector, Map<String, Tag>>();
|
||||
|
||||
for (Tag tag : tags) {
|
||||
if (!(tag instanceof CompoundTag)) {
|
||||
throw new InvalidFormatException(
|
||||
"CompoundTag expected in TileEntities");
|
||||
}
|
||||
|
||||
CompoundTag t = (CompoundTag) tag;
|
||||
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int z = 0;
|
||||
|
||||
Map<String, Tag> values = new HashMap<String, Tag>();
|
||||
|
||||
for (Map.Entry<String, Tag> entry : t.getValue().entrySet()) {
|
||||
if (entry.getKey().equals("x")) {
|
||||
if (entry.getValue() instanceof IntTag) {
|
||||
x = ((IntTag) entry.getValue()).getValue();
|
||||
}
|
||||
} else if (entry.getKey().equals("y")) {
|
||||
if (entry.getValue() instanceof IntTag) {
|
||||
y = ((IntTag) entry.getValue()).getValue();
|
||||
}
|
||||
} else if (entry.getKey().equals("z")) {
|
||||
if (entry.getValue() instanceof IntTag) {
|
||||
z = ((IntTag) entry.getValue()).getValue();
|
||||
}
|
||||
}
|
||||
|
||||
values.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
BlockVector vec = new BlockVector(x, y, z);
|
||||
tileEntities.put(vec, values);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the map of tags keyed to strings for a block's tile entity data. May
|
||||
* return null if there is no tile entity data. Not public yet because
|
||||
* what this function returns isn't ideal for usage.
|
||||
*
|
||||
* @param position the position
|
||||
* @return the compound tag for that position, which may be null
|
||||
* @throws DataException thrown if there is a data error
|
||||
*/
|
||||
@Nullable
|
||||
private CompoundTag getBlockTileEntity(Vector position) throws DataException {
|
||||
if (tileEntities == null) {
|
||||
populateTileEntities();
|
||||
}
|
||||
|
||||
Map<String, Tag> values = tileEntities.get(new BlockVector(position));
|
||||
if (values == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new CompoundTag("", values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBlock getBlock(Vector position) throws DataException {
|
||||
int id = getBlockID(position);
|
||||
int data = getBlockData(position);
|
||||
BaseBlock block;
|
||||
|
||||
/*if (id == BlockID.WALL_SIGN || id == BlockID.SIGN_POST) {
|
||||
block = new SignBlock(id, data);
|
||||
} else if (id == BlockID.CHEST) {
|
||||
block = new ChestBlock(data);
|
||||
} else if (id == BlockID.FURNACE || id == BlockID.BURNING_FURNACE) {
|
||||
block = new FurnaceBlock(id, data);
|
||||
} else if (id == BlockID.DISPENSER) {
|
||||
block = new DispenserBlock(data);
|
||||
} else if (id == BlockID.MOB_SPAWNER) {
|
||||
block = new MobSpawnerBlock(data);
|
||||
} else if (id == BlockID.NOTE_BLOCK) {
|
||||
block = new NoteBlock(data);
|
||||
} else {*/
|
||||
block = new BaseBlock(id, data);
|
||||
//}
|
||||
|
||||
CompoundTag tileEntity = getBlockTileEntity(position);
|
||||
if (tileEntity != null) {
|
||||
((TileEntityBlock) block).setNbtData(tileEntity);
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.chunk;
|
||||
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldedit.blocks.BaseBlock;
|
||||
import com.sk89q.worldedit.world.DataException;
|
||||
|
||||
/**
|
||||
* A 16 by 16 block chunk.
|
||||
*/
|
||||
public interface Chunk {
|
||||
|
||||
/**
|
||||
* Get the block ID of a block.
|
||||
*
|
||||
* @param position the position of the block
|
||||
* @return the type ID of the block
|
||||
* @throws DataException thrown on data error
|
||||
*/
|
||||
public int getBlockID(Vector position) throws DataException;
|
||||
|
||||
/**
|
||||
* Get the block data of a block.
|
||||
*
|
||||
* @param position the position of the block
|
||||
* @return the data value of the block
|
||||
* @throws DataException thrown on data error
|
||||
*/
|
||||
public int getBlockData(Vector position) throws DataException;
|
||||
|
||||
|
||||
/**
|
||||
* Get a block;
|
||||
*
|
||||
* @param position the position of the block
|
||||
* @return block the block
|
||||
* @throws DataException thrown on data error
|
||||
*/
|
||||
public BaseBlock getBlock(Vector position) throws DataException;
|
||||
|
||||
}
|
@ -0,0 +1,215 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.chunk;
|
||||
|
||||
import com.sk89q.jnbt.ByteArrayTag;
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.jnbt.IntTag;
|
||||
import com.sk89q.jnbt.ListTag;
|
||||
import com.sk89q.jnbt.NBTUtils;
|
||||
import com.sk89q.jnbt.Tag;
|
||||
import com.sk89q.worldedit.BlockVector;
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldedit.blocks.BaseBlock;
|
||||
import com.sk89q.worldedit.world.DataException;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
import com.sk89q.worldedit.world.storage.InvalidFormatException;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Represents an Alpha chunk.
|
||||
*/
|
||||
public class OldChunk implements Chunk {
|
||||
|
||||
private CompoundTag rootTag;
|
||||
private byte[] blocks;
|
||||
private byte[] data;
|
||||
private int rootX;
|
||||
private int rootZ;
|
||||
|
||||
private Map<BlockVector, Map<String,Tag>> tileEntities;
|
||||
|
||||
/**
|
||||
* Construct the chunk with a compound tag.
|
||||
*
|
||||
* @param world the world
|
||||
* @param tag the tag
|
||||
* @throws DataException
|
||||
*/
|
||||
public OldChunk(World world, CompoundTag tag) throws DataException {
|
||||
rootTag = tag;
|
||||
|
||||
blocks = NBTUtils.getChildTag(rootTag.getValue(), "Blocks", ByteArrayTag.class).getValue();
|
||||
data = NBTUtils.getChildTag(rootTag.getValue(), "Data", ByteArrayTag.class).getValue();
|
||||
rootX = NBTUtils.getChildTag(rootTag.getValue(), "xPos", IntTag.class).getValue();
|
||||
rootZ = NBTUtils.getChildTag(rootTag.getValue(), "zPos", IntTag.class).getValue();
|
||||
|
||||
int size = 16 * 16 * 128;
|
||||
if (blocks.length != size) {
|
||||
throw new InvalidFormatException("Chunk blocks byte array expected "
|
||||
+ "to be " + size + " bytes; found " + blocks.length);
|
||||
}
|
||||
|
||||
if (data.length != (size/2)) {
|
||||
throw new InvalidFormatException("Chunk block data byte array "
|
||||
+ "expected to be " + size + " bytes; found " + data.length);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlockID(Vector position) throws DataException {
|
||||
if(position.getBlockY() >= 128) return 0;
|
||||
|
||||
int x = position.getBlockX() - rootX * 16;
|
||||
int y = position.getBlockY();
|
||||
int z = position.getBlockZ() - rootZ * 16;
|
||||
int index = y + (z * 128 + (x * 128 * 16));
|
||||
try {
|
||||
return blocks[index];
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
throw new DataException("Chunk does not contain position " + position);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlockData(Vector position) throws DataException {
|
||||
if(position.getBlockY() >= 128) return 0;
|
||||
|
||||
int x = position.getBlockX() - rootX * 16;
|
||||
int y = position.getBlockY();
|
||||
int z = position.getBlockZ() - rootZ * 16;
|
||||
int index = y + (z * 128 + (x * 128 * 16));
|
||||
boolean shift = index % 2 == 0;
|
||||
index /= 2;
|
||||
|
||||
try {
|
||||
if (!shift) {
|
||||
return (data[index] & 0xF0) >> 4;
|
||||
} else {
|
||||
return data[index] & 0xF;
|
||||
}
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
throw new DataException("Chunk does not contain position " + position);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to load the tile entities.
|
||||
*
|
||||
* @throws DataException
|
||||
*/
|
||||
private void populateTileEntities() throws DataException {
|
||||
List<Tag> tags = NBTUtils.getChildTag(
|
||||
rootTag.getValue(), "TileEntities", ListTag.class)
|
||||
.getValue();
|
||||
|
||||
tileEntities = new HashMap<BlockVector, Map<String, Tag>>();
|
||||
|
||||
for (Tag tag : tags) {
|
||||
if (!(tag instanceof CompoundTag)) {
|
||||
throw new InvalidFormatException("CompoundTag expected in TileEntities");
|
||||
}
|
||||
|
||||
CompoundTag t = (CompoundTag) tag;
|
||||
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int z = 0;
|
||||
|
||||
Map<String, Tag> values = new HashMap<String, Tag>();
|
||||
|
||||
for (Map.Entry<String, Tag> entry : t.getValue().entrySet()) {
|
||||
if (entry.getKey().equals("x")) {
|
||||
if (entry.getValue() instanceof IntTag) {
|
||||
x = ((IntTag) entry.getValue()).getValue();
|
||||
}
|
||||
} else if (entry.getKey().equals("y")) {
|
||||
if (entry.getValue() instanceof IntTag) {
|
||||
y = ((IntTag) entry.getValue()).getValue();
|
||||
}
|
||||
} else if (entry.getKey().equals("z")) {
|
||||
if (entry.getValue() instanceof IntTag) {
|
||||
z = ((IntTag) entry.getValue()).getValue();
|
||||
}
|
||||
}
|
||||
|
||||
values.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
BlockVector vec = new BlockVector(x, y, z);
|
||||
tileEntities.put(vec, values);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the map of tags keyed to strings for a block's tile entity data. May
|
||||
* return null if there is no tile entity data. Not public yet because
|
||||
* what this function returns isn't ideal for usage.
|
||||
*
|
||||
* @param position the position
|
||||
* @return a tag
|
||||
* @throws DataException
|
||||
*/
|
||||
private CompoundTag getBlockTileEntity(Vector position) throws DataException {
|
||||
if (tileEntities == null) {
|
||||
populateTileEntities();
|
||||
}
|
||||
|
||||
Map<String, Tag> values = tileEntities.get(new BlockVector(position));
|
||||
if (values == null) {
|
||||
return null;
|
||||
}
|
||||
return new CompoundTag("", values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseBlock getBlock(Vector position) throws DataException {
|
||||
int id = getBlockID(position);
|
||||
int data = getBlockData(position);
|
||||
BaseBlock block;
|
||||
|
||||
/*if (id == BlockID.WALL_SIGN || id == BlockID.SIGN_POST) {
|
||||
block = new SignBlock(id, data);
|
||||
} else if (id == BlockID.CHEST) {
|
||||
block = new ChestBlock(data);
|
||||
} else if (id == BlockID.FURNACE || id == BlockID.BURNING_FURNACE) {
|
||||
block = new FurnaceBlock(id, data);
|
||||
} else if (id == BlockID.DISPENSER) {
|
||||
block = new DispenserBlock(data);
|
||||
} else if (id == BlockID.MOB_SPAWNER) {
|
||||
block = new MobSpawnerBlock(data);
|
||||
} else if (id == BlockID.NOTE_BLOCK) {
|
||||
block = new NoteBlock(data);
|
||||
} else {*/
|
||||
block = new BaseBlock(id, data);
|
||||
//}
|
||||
|
||||
CompoundTag tileEntity = getBlockTileEntity(position);
|
||||
if (tileEntity != null) {
|
||||
block.setNbtData(tileEntity);
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.registry;
|
||||
|
||||
import com.sk89q.worldedit.world.biome.BaseBiome;
|
||||
import com.sk89q.worldedit.world.biome.BiomeData;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Provides information on biomes.
|
||||
*/
|
||||
public interface BiomeRegistry {
|
||||
|
||||
/**
|
||||
* Create a new biome given its biome ID.
|
||||
*
|
||||
* @param id its biome ID
|
||||
* @return a new biome or null if it can't be created
|
||||
*/
|
||||
@Nullable
|
||||
BaseBiome createFromId(int id);
|
||||
|
||||
/**
|
||||
* Get a list of available biomes.
|
||||
*
|
||||
* @return a list of biomes
|
||||
*/
|
||||
List<BaseBiome> getBiomes();
|
||||
|
||||
/**
|
||||
* Get data about a biome.
|
||||
*
|
||||
* @param biome the biome
|
||||
* @return a data object or null if information is not known
|
||||
*/
|
||||
@Nullable
|
||||
BiomeData getData(BaseBiome biome);
|
||||
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.registry;
|
||||
|
||||
import com.sk89q.worldedit.blocks.BaseBlock;
|
||||
import com.sk89q.worldedit.blocks.BlockMaterial;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Provides information on blocks and provides methods to create them.
|
||||
*/
|
||||
public interface BlockRegistry {
|
||||
|
||||
/**
|
||||
* Create a new block using its ID.
|
||||
*
|
||||
* @param id the id
|
||||
* @return the block, which may be null if no block exists
|
||||
*/
|
||||
@Nullable
|
||||
BaseBlock createFromId(String id);
|
||||
|
||||
/**
|
||||
* Create a new block using its legacy numeric ID.
|
||||
*
|
||||
* @param id the id
|
||||
* @return the block, which may be null if no block exists
|
||||
*/
|
||||
@Nullable
|
||||
BaseBlock createFromId(int id);
|
||||
|
||||
/**
|
||||
* Get the material for the given block.
|
||||
*
|
||||
* @param block the block
|
||||
* @return the material, or null if the material information is not known
|
||||
*/
|
||||
@Nullable
|
||||
BlockMaterial getMaterial(BaseBlock block);
|
||||
|
||||
/**
|
||||
* Get an unmodifiable map of states for this block.
|
||||
*
|
||||
* @param block the block
|
||||
* @return a map of states where the key is the state's ID
|
||||
*/
|
||||
@Nullable
|
||||
Map<String, ? extends State> getStates(BaseBlock block);
|
||||
|
||||
}
|
@ -0,0 +1,187 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.registry;
|
||||
|
||||
import com.google.common.io.Resources;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldedit.blocks.BlockMaterial;
|
||||
import com.sk89q.worldedit.util.gson.VectorAdapter;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Provides block data based on the built-in block database that is bundled
|
||||
* with WorldEdit.
|
||||
*
|
||||
* <p>A new instance cannot be created. Use {@link #getInstance()} to get
|
||||
* an instance.</p>
|
||||
*
|
||||
* <p>The data is read from a JSON file that is bundled with WorldEdit. If
|
||||
* reading fails (which occurs when this class is first instantiated), then
|
||||
* the methods will return {@code null}s for all blocks.</p>
|
||||
*/
|
||||
public class BundledBlockData {
|
||||
|
||||
private static final Logger log = Logger.getLogger(BundledBlockData.class.getCanonicalName());
|
||||
private static final BundledBlockData INSTANCE = new BundledBlockData();
|
||||
|
||||
private final Map<String, BlockEntry> idMap = new HashMap<String, BlockEntry>();
|
||||
private final Map<Integer, BlockEntry> legacyMap = new HashMap<Integer, BlockEntry>(); // Trove usage removed temporarily
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*/
|
||||
private BundledBlockData() {
|
||||
try {
|
||||
loadFromResource();
|
||||
} catch (IOException e) {
|
||||
log.log(Level.WARNING, "Failed to load the built-in block registry", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to load the data from file.
|
||||
*
|
||||
* @throws IOException thrown on I/O error
|
||||
*/
|
||||
private void loadFromResource() throws IOException {
|
||||
GsonBuilder gsonBuilder = new GsonBuilder();
|
||||
gsonBuilder.registerTypeAdapter(Vector.class, new VectorAdapter());
|
||||
Gson gson = gsonBuilder.create();
|
||||
URL url = BundledBlockData.class.getResource("blocks.json");
|
||||
if (url == null) {
|
||||
throw new IOException("Could not find blocks.json");
|
||||
}
|
||||
String data = Resources.toString(url, Charset.defaultCharset());
|
||||
List<BlockEntry> entries = gson.fromJson(data, new TypeToken<List<BlockEntry>>() {}.getType());
|
||||
|
||||
for (BlockEntry entry : entries) {
|
||||
entry.postDeserialization();
|
||||
idMap.put(entry.id, entry);
|
||||
legacyMap.put(entry.legacyId, entry);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the entry for the given block ID.
|
||||
*
|
||||
* @param id the ID
|
||||
* @return the entry, or null
|
||||
*/
|
||||
@Nullable
|
||||
private BlockEntry findById(String id) {
|
||||
return idMap.get(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the entry for the given block legacy numeric ID.
|
||||
*
|
||||
* @param id the ID
|
||||
* @return the entry, or null
|
||||
*/
|
||||
@Nullable
|
||||
private BlockEntry findById(int id) {
|
||||
return legacyMap.get(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given string ID to a legacy numeric ID.
|
||||
*
|
||||
* @param id the ID
|
||||
* @return the legacy ID, which may be null if the block does not have a legacy ID
|
||||
*/
|
||||
@Nullable
|
||||
public Integer toLegacyId(String id) {
|
||||
BlockEntry entry = findById(id);
|
||||
if (entry != null) {
|
||||
return entry.legacyId;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the material properties for the given block.
|
||||
*
|
||||
* @param id the legacy numeric ID
|
||||
* @return the material's properties, or null
|
||||
*/
|
||||
@Nullable
|
||||
public BlockMaterial getMaterialById(int id) {
|
||||
BlockEntry entry = findById(id);
|
||||
if (entry != null) {
|
||||
return entry.material;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the states for the given block.
|
||||
*
|
||||
* @param id the legacy numeric ID
|
||||
* @return the block's states, or null if no information is available
|
||||
*/
|
||||
@Nullable
|
||||
public Map<String, ? extends State> getStatesById(int id) {
|
||||
BlockEntry entry = findById(id);
|
||||
if (entry != null) {
|
||||
return entry.states;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a singleton instance of this object.
|
||||
*
|
||||
* @return the instance
|
||||
*/
|
||||
public static BundledBlockData getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private static class BlockEntry {
|
||||
private int legacyId;
|
||||
private String id;
|
||||
private String unlocalizedName;
|
||||
private List<String> aliases;
|
||||
private Map<String, SimpleState> states = new HashMap<String, SimpleState>();
|
||||
private SimpleBlockMaterial material = new SimpleBlockMaterial();
|
||||
|
||||
void postDeserialization() {
|
||||
for (SimpleState state : states.values()) {
|
||||
state.postDeserialization();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.registry;
|
||||
|
||||
import com.sk89q.worldedit.entity.BaseEntity;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Provides information on entities.
|
||||
*/
|
||||
public interface EntityRegistry {
|
||||
|
||||
/**
|
||||
* Create a new entity using its ID.
|
||||
*
|
||||
* @param id the id
|
||||
* @return the entity, which may be null if the entity does not exist
|
||||
*/
|
||||
@Nullable
|
||||
BaseEntity createFromId(String id);
|
||||
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.registry;
|
||||
|
||||
import com.sk89q.worldedit.blocks.BaseBlock;
|
||||
import com.sk89q.worldedit.blocks.BlockMaterial;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A block registry that uses {@link BundledBlockData} to serve information
|
||||
* about blocks.
|
||||
*/
|
||||
public class LegacyBlockRegistry implements BlockRegistry {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BaseBlock createFromId(String id) {
|
||||
Integer legacyId = BundledBlockData.getInstance().toLegacyId(id);
|
||||
if (legacyId != null) {
|
||||
return createFromId(legacyId);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BaseBlock createFromId(int id) {
|
||||
return new BaseBlock(id);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockMaterial getMaterial(BaseBlock block) {
|
||||
return BundledBlockData.getInstance().getMaterialById(block.getId());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Map<String, ? extends State> getStates(BaseBlock block) {
|
||||
return BundledBlockData.getInstance().getStatesById(block.getId());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.registry;
|
||||
|
||||
/**
|
||||
* An implementation of {@link WorldData} that uses legacy numeric IDs and
|
||||
* a built-in block database.
|
||||
*/
|
||||
public class LegacyWorldData implements WorldData {
|
||||
|
||||
private static final LegacyWorldData INSTANCE = new LegacyWorldData();
|
||||
private final LegacyBlockRegistry blockRegistry = new LegacyBlockRegistry();
|
||||
private final NullEntityRegistry entityRegistry = new NullEntityRegistry();
|
||||
private final NullBiomeRegistry biomeRegistry = new NullBiomeRegistry();
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*/
|
||||
protected LegacyWorldData() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockRegistry getBlockRegistry() {
|
||||
return blockRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityRegistry getEntityRegistry() {
|
||||
return entityRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiomeRegistry getBiomeRegistry() {
|
||||
return biomeRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a singleton instance.
|
||||
*
|
||||
* @return an instance
|
||||
*/
|
||||
public static LegacyWorldData getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.registry;
|
||||
|
||||
import com.sk89q.worldedit.world.biome.BaseBiome;
|
||||
import com.sk89q.worldedit.world.biome.BiomeData;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A biome registry that knows nothing.
|
||||
*/
|
||||
public class NullBiomeRegistry implements BiomeRegistry {
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*/
|
||||
public NullBiomeRegistry() {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BaseBiome createFromId(int id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BaseBiome> getBiomes() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BiomeData getData(BaseBiome biome) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.registry;
|
||||
|
||||
import com.sk89q.worldedit.entity.BaseEntity;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* An implementation of an entity registry that knows nothing.
|
||||
*/
|
||||
public class NullEntityRegistry implements EntityRegistry {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BaseEntity createFromId(String id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,246 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.registry;
|
||||
|
||||
import com.sk89q.worldedit.blocks.BlockMaterial;
|
||||
|
||||
class SimpleBlockMaterial implements BlockMaterial {
|
||||
|
||||
private boolean renderedAsNormalBlock;
|
||||
private boolean fullCube;
|
||||
private boolean opaque;
|
||||
private boolean powerSource;
|
||||
private boolean liquid;
|
||||
private boolean solid;
|
||||
private float hardness;
|
||||
private float resistance;
|
||||
private float slipperiness;
|
||||
private boolean grassBlocking;
|
||||
private float ambientOcclusionLightValue;
|
||||
private int lightOpacity;
|
||||
private int lightValue;
|
||||
private boolean fragileWhenPushed;
|
||||
private boolean unpushable;
|
||||
private boolean adventureModeExempt;
|
||||
private boolean ticksRandomly;
|
||||
private boolean usingNeighborLight;
|
||||
private boolean movementBlocker;
|
||||
private boolean burnable;
|
||||
private boolean toolRequired;
|
||||
private boolean replacedDuringPlacement;
|
||||
|
||||
@Override
|
||||
public boolean isRenderedAsNormalBlock() {
|
||||
return renderedAsNormalBlock;
|
||||
}
|
||||
|
||||
public void setRenderedAsNormalBlock(boolean renderedAsNormalBlock) {
|
||||
this.renderedAsNormalBlock = renderedAsNormalBlock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFullCube() {
|
||||
return fullCube;
|
||||
}
|
||||
|
||||
public void setFullCube(boolean fullCube) {
|
||||
this.fullCube = fullCube;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpaque() {
|
||||
return opaque;
|
||||
}
|
||||
|
||||
public void setOpaque(boolean opaque) {
|
||||
this.opaque = opaque;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPowerSource() {
|
||||
return powerSource;
|
||||
}
|
||||
|
||||
public void setPowerSource(boolean powerSource) {
|
||||
this.powerSource = powerSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLiquid() {
|
||||
return liquid;
|
||||
}
|
||||
|
||||
public void setLiquid(boolean liquid) {
|
||||
this.liquid = liquid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSolid() {
|
||||
return solid;
|
||||
}
|
||||
|
||||
public void setSolid(boolean solid) {
|
||||
this.solid = solid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getHardness() {
|
||||
return hardness;
|
||||
}
|
||||
|
||||
public void setHardness(float hardness) {
|
||||
this.hardness = hardness;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getResistance() {
|
||||
return resistance;
|
||||
}
|
||||
|
||||
public void setResistance(float resistance) {
|
||||
this.resistance = resistance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getSlipperiness() {
|
||||
return slipperiness;
|
||||
}
|
||||
|
||||
public void setSlipperiness(float slipperiness) {
|
||||
this.slipperiness = slipperiness;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGrassBlocking() {
|
||||
return grassBlocking;
|
||||
}
|
||||
|
||||
public void setGrassBlocking(boolean grassBlocking) {
|
||||
this.grassBlocking = grassBlocking;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getAmbientOcclusionLightValue() {
|
||||
return ambientOcclusionLightValue;
|
||||
}
|
||||
|
||||
public void setAmbientOcclusionLightValue(float ambientOcclusionLightValue) {
|
||||
this.ambientOcclusionLightValue = ambientOcclusionLightValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLightOpacity() {
|
||||
return lightOpacity;
|
||||
}
|
||||
|
||||
public void setLightOpacity(int lightOpacity) {
|
||||
this.lightOpacity = lightOpacity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLightValue() {
|
||||
return lightValue;
|
||||
}
|
||||
|
||||
public void setLightValue(int lightValue) {
|
||||
this.lightValue = lightValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFragileWhenPushed() {
|
||||
return fragileWhenPushed;
|
||||
}
|
||||
|
||||
public void setFragileWhenPushed(boolean fragileWhenPushed) {
|
||||
this.fragileWhenPushed = fragileWhenPushed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUnpushable() {
|
||||
return unpushable;
|
||||
}
|
||||
|
||||
public void setUnpushable(boolean unpushable) {
|
||||
this.unpushable = unpushable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAdventureModeExempt() {
|
||||
return adventureModeExempt;
|
||||
}
|
||||
|
||||
public void setAdventureModeExempt(boolean adventureModeExempt) {
|
||||
this.adventureModeExempt = adventureModeExempt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTicksRandomly() {
|
||||
return ticksRandomly;
|
||||
}
|
||||
|
||||
public void setTicksRandomly(boolean ticksRandomly) {
|
||||
this.ticksRandomly = ticksRandomly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUsingNeighborLight() {
|
||||
return usingNeighborLight;
|
||||
}
|
||||
|
||||
public void setUsingNeighborLight(boolean usingNeighborLight) {
|
||||
this.usingNeighborLight = usingNeighborLight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMovementBlocker() {
|
||||
return movementBlocker;
|
||||
}
|
||||
|
||||
public void setMovementBlocker(boolean movementBlocker) {
|
||||
this.movementBlocker = movementBlocker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBurnable() {
|
||||
return burnable;
|
||||
}
|
||||
|
||||
public void setBurnable(boolean burnable) {
|
||||
this.burnable = burnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isToolRequired() {
|
||||
return toolRequired;
|
||||
}
|
||||
|
||||
public void setToolRequired(boolean toolRequired) {
|
||||
this.toolRequired = toolRequired;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReplacedDuringPlacement() {
|
||||
return replacedDuringPlacement;
|
||||
}
|
||||
|
||||
public void setReplacedDuringPlacement(boolean replacedDuringPlacement) {
|
||||
this.replacedDuringPlacement = replacedDuringPlacement;
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.registry;
|
||||
|
||||
import com.sk89q.worldedit.blocks.BaseBlock;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
class SimpleState implements State {
|
||||
|
||||
private Byte dataMask;
|
||||
private Map<String, SimpleStateValue> values;
|
||||
|
||||
@Override
|
||||
public Map<String, SimpleStateValue> valueMap() {
|
||||
return Collections.unmodifiableMap(values);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public StateValue getValue(BaseBlock block) {
|
||||
for (StateValue value : values.values()) {
|
||||
if (value.isSet(block)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
byte getDataMask() {
|
||||
return dataMask != null ? dataMask : 0xF;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasDirection() {
|
||||
for (SimpleStateValue value : values.values()) {
|
||||
if (value.getDirection() != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void postDeserialization() {
|
||||
for (SimpleStateValue v : values.values()) {
|
||||
v.setState(this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.registry;
|
||||
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldedit.blocks.BaseBlock;
|
||||
|
||||
class SimpleStateValue implements StateValue {
|
||||
|
||||
private SimpleState state;
|
||||
private Byte data;
|
||||
private Vector direction;
|
||||
|
||||
void setState(SimpleState state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSet(BaseBlock block) {
|
||||
return data != null && (block.getData() & state.getDataMask()) == data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean set(BaseBlock block) {
|
||||
if (data != null) {
|
||||
block.setData((block.getData() & ~state.getDataMask()) | data);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector getDirection() {
|
||||
return direction;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.registry;
|
||||
|
||||
import com.sk89q.worldedit.blocks.BaseBlock;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Describes a state property of a block.
|
||||
*
|
||||
* <p>Example states include "variant" (indicating material or type) and
|
||||
* "facing" (indicating orientation).</p>
|
||||
*/
|
||||
public interface State {
|
||||
|
||||
/**
|
||||
* Return a map of available values for this state.
|
||||
*
|
||||
* <p>Keys are the value of state and map values describe that
|
||||
* particular state value.</p>
|
||||
*
|
||||
* @return the map of state values
|
||||
*/
|
||||
Map<String, ? extends StateValue> valueMap();
|
||||
|
||||
/**
|
||||
* Get the value that the block is set to.
|
||||
*
|
||||
* @param block the block
|
||||
* @return the state, otherwise null if the block isn't set to any of the values
|
||||
*/
|
||||
@Nullable
|
||||
StateValue getValue(BaseBlock block);
|
||||
|
||||
/**
|
||||
* Returns whether this state contains directional data.
|
||||
*
|
||||
* @return true if directional data is available
|
||||
*/
|
||||
boolean hasDirection();
|
||||
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.registry;
|
||||
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldedit.blocks.BaseBlock;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Describes a possible value for a {@code State}.
|
||||
*/
|
||||
public interface StateValue {
|
||||
|
||||
/**
|
||||
* Return whether this state is set on the given block.
|
||||
*
|
||||
* @param block the block
|
||||
* @return true if this value is set
|
||||
*/
|
||||
boolean isSet(BaseBlock block);
|
||||
|
||||
/**
|
||||
* Set the state to this value on the given block.
|
||||
*
|
||||
* @param block the block to change
|
||||
* @return true if the value was set successfully
|
||||
*/
|
||||
boolean set(BaseBlock block);
|
||||
|
||||
/**
|
||||
* Return the direction associated with this value.
|
||||
*
|
||||
* @return the direction, otherwise null
|
||||
*/
|
||||
@Nullable
|
||||
Vector getDirection();
|
||||
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.registry;
|
||||
|
||||
/**
|
||||
* Describes the necessary data for blocks, entities, and other objects
|
||||
* on a world.
|
||||
*/
|
||||
public interface WorldData {
|
||||
|
||||
/**
|
||||
* Get the block registry.
|
||||
*
|
||||
* @return the block registry
|
||||
*/
|
||||
BlockRegistry getBlockRegistry();
|
||||
|
||||
/**
|
||||
* Get the entity registry.
|
||||
*
|
||||
* @return the entity registry
|
||||
*/
|
||||
EntityRegistry getEntityRegistry();
|
||||
|
||||
/**
|
||||
* Get the biome registry.
|
||||
*
|
||||
* @return the biome registry
|
||||
*/
|
||||
BiomeRegistry getBiomeRegistry();
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.snapshot;
|
||||
|
||||
public class InvalidSnapshotException extends Exception {
|
||||
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.snapshot;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
public class ModificationTimerParser implements SnapshotDateParser {
|
||||
|
||||
@Override
|
||||
public Calendar detectDate(File file) {
|
||||
Calendar cal = new GregorianCalendar();
|
||||
cal.setTimeInMillis(file.lastModified());
|
||||
return cal;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,212 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// $Id$
|
||||
|
||||
package com.sk89q.worldedit.world.snapshot;
|
||||
|
||||
import com.sk89q.worldedit.world.DataException;
|
||||
import com.sk89q.worldedit.world.storage.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Calendar;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
/**
|
||||
* A snapshot is a backup.
|
||||
*/
|
||||
public class Snapshot implements Comparable<Snapshot> {
|
||||
|
||||
protected static Logger logger = Logger.getLogger(Snapshot.class.getCanonicalName());
|
||||
|
||||
protected File file;
|
||||
protected String name;
|
||||
protected Calendar date;
|
||||
|
||||
/**
|
||||
* Construct a snapshot restoration operation.
|
||||
*
|
||||
* @param repo a repository
|
||||
* @param snapshot a snapshot name
|
||||
*/
|
||||
public Snapshot(SnapshotRepository repo, String snapshot) {
|
||||
file = new File(repo.getDirectory(), snapshot);
|
||||
name = snapshot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a chunk store.
|
||||
*
|
||||
* @return a chunk store
|
||||
* @throws IOException
|
||||
* @throws DataException
|
||||
*/
|
||||
public ChunkStore getChunkStore() throws IOException, DataException {
|
||||
ChunkStore chunkStore = internalGetChunkStore();
|
||||
|
||||
logger.info("WorldEdit: Using " + chunkStore.getClass().getCanonicalName()
|
||||
+ " for loading snapshot '" + file.getAbsolutePath() + "'");
|
||||
|
||||
return chunkStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a chunk store.
|
||||
*
|
||||
* @return a chunk store
|
||||
* @throws IOException
|
||||
* @throws DataException
|
||||
*/
|
||||
private ChunkStore internalGetChunkStore() throws IOException, DataException {
|
||||
if (file.getName().toLowerCase().endsWith(".zip")) {
|
||||
try {
|
||||
ChunkStore chunkStore = new TrueZipMcRegionChunkStore(file);
|
||||
|
||||
if (!chunkStore.isValid()) {
|
||||
return new TrueZipLegacyChunkStore(file);
|
||||
}
|
||||
|
||||
return chunkStore;
|
||||
} catch (NoClassDefFoundError e) {
|
||||
ChunkStore chunkStore = new ZippedMcRegionChunkStore(file);
|
||||
|
||||
if (!chunkStore.isValid()) {
|
||||
return new ZippedLegacyChunkStore(file);
|
||||
}
|
||||
|
||||
return chunkStore;
|
||||
}
|
||||
} else if (file.getName().toLowerCase().endsWith(".tar.bz2")
|
||||
|| file.getName().toLowerCase().endsWith(".tar.gz")
|
||||
|| file.getName().toLowerCase().endsWith(".tar")) {
|
||||
try {
|
||||
ChunkStore chunkStore = new TrueZipMcRegionChunkStore(file);
|
||||
|
||||
if (!chunkStore.isValid()) {
|
||||
return new TrueZipLegacyChunkStore(file);
|
||||
}
|
||||
|
||||
return chunkStore;
|
||||
} catch (NoClassDefFoundError e) {
|
||||
throw new DataException("TrueZIP is required for .tar support");
|
||||
}
|
||||
} else {
|
||||
ChunkStore chunkStore = new FileMcRegionChunkStore(file);
|
||||
|
||||
if (!chunkStore.isValid()) {
|
||||
return new FileLegacyChunkStore(file);
|
||||
}
|
||||
|
||||
return chunkStore;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the zip/tar file it contains the given world.
|
||||
*
|
||||
* @return true if the zip/tar file contains the given world
|
||||
*/
|
||||
public boolean containsWorld(String worldname) {
|
||||
try {
|
||||
if (file.getName().toLowerCase().endsWith(".zip")) {
|
||||
ZipFile entry = new ZipFile(file);
|
||||
return (entry.getEntry(worldname) != null
|
||||
|| entry.getEntry(worldname + "/level.dat") != null);
|
||||
} else if (file.getName().toLowerCase().endsWith(".tar.bz2")
|
||||
|| file.getName().toLowerCase().endsWith(".tar.gz")
|
||||
|| file.getName().toLowerCase().endsWith(".tar")) {
|
||||
try {
|
||||
de.schlichtherle.util.zip.ZipFile entry = new de.schlichtherle.util.zip.ZipFile(file);
|
||||
|
||||
return entry.getEntry(worldname) != null;
|
||||
} catch (NoClassDefFoundError e) {
|
||||
throw new DataException("TrueZIP is required for .tar support");
|
||||
}
|
||||
} else {
|
||||
return (file.getName().equalsIgnoreCase(worldname));
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
// Skip the file, but print an error
|
||||
logger.info("Could not load snapshot: "
|
||||
+ file.getPath());
|
||||
} catch (DataException ex) {
|
||||
// No truezip, so tar file not supported.
|
||||
// Dont print, just skip the file.
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the snapshot's name.
|
||||
*
|
||||
* @return the name of the snapshot
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file for the snapshot.
|
||||
*
|
||||
* @return path to the snapshot
|
||||
*/
|
||||
public File getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the date associated with this snapshot.
|
||||
*
|
||||
* @return date for the snapshot
|
||||
*/
|
||||
public Calendar getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the date of the snapshot.
|
||||
*
|
||||
* @param date the date of the snapshot
|
||||
*/
|
||||
public void setDate(Calendar date) {
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Snapshot o) {
|
||||
if (o.date == null || date == null) {
|
||||
// Remove the folder from the name
|
||||
int i = name.indexOf("/"), j = o.name.indexOf("/");
|
||||
return name.substring((i > 0 ? 0 : i)).compareTo(o.name.substring((j > 0 ? 0 : j)));
|
||||
} else {
|
||||
return date.compareTo(o.date);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return o instanceof Snapshot && file.equals(((Snapshot) o).file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return file.hashCode();
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.snapshot;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.File;
|
||||
import java.util.Calendar;
|
||||
|
||||
/**
|
||||
* A name parser attempts to make sense of a filename for a snapshot.
|
||||
*/
|
||||
public interface SnapshotDateParser {
|
||||
|
||||
/**
|
||||
* Attempt to detect a date from a file.
|
||||
*
|
||||
* @param file the file
|
||||
* @return date or null
|
||||
*/
|
||||
@Nullable
|
||||
public Calendar detectDate(File file);
|
||||
|
||||
}
|
@ -0,0 +1,243 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// $Id$
|
||||
|
||||
package com.sk89q.worldedit.world.snapshot;
|
||||
|
||||
import com.sk89q.worldedit.world.storage.MissingWorldException;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A repository contains zero or more snapshots.
|
||||
*/
|
||||
public class SnapshotRepository {
|
||||
|
||||
protected File dir;
|
||||
protected List<SnapshotDateParser> dateParsers = new ArrayList<SnapshotDateParser>();
|
||||
|
||||
/**
|
||||
* Create a new instance of a repository.
|
||||
*
|
||||
* @param dir the directory
|
||||
*/
|
||||
public SnapshotRepository(File dir) {
|
||||
this.dir = dir;
|
||||
|
||||
// If folder doesn't exist, make it
|
||||
dir.mkdirs();
|
||||
|
||||
dateParsers.add(new YYMMDDHHIISSParser());
|
||||
dateParsers.add(new ModificationTimerParser());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance of a repository.
|
||||
*
|
||||
* @param dir the directory
|
||||
*/
|
||||
public SnapshotRepository(String dir) {
|
||||
this(new File(dir));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of snapshots in a directory. The newest snapshot is
|
||||
* near the top of the array.
|
||||
*
|
||||
* @param newestFirst true to get the newest first
|
||||
* @return a list of snapshots
|
||||
*/
|
||||
public List<Snapshot> getSnapshots(boolean newestFirst, String worldName) throws MissingWorldException {
|
||||
FilenameFilter filter = new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(File dir, String name) {
|
||||
File f = new File(dir, name);
|
||||
return isValidSnapshot(f);
|
||||
}
|
||||
};
|
||||
|
||||
File[] snapshotFiles = dir.listFiles();
|
||||
if (snapshotFiles == null) {
|
||||
throw new MissingWorldException(worldName);
|
||||
}
|
||||
List<Snapshot> list = new ArrayList<Snapshot>(snapshotFiles.length);
|
||||
|
||||
for (File file : snapshotFiles) {
|
||||
if (isValidSnapshot(file)) {
|
||||
Snapshot snapshot = new Snapshot(this, file.getName());
|
||||
if (snapshot.containsWorld(worldName)) {
|
||||
detectDate(snapshot);
|
||||
list.add(snapshot);
|
||||
}
|
||||
} else if (file.isDirectory() && file.getName().equalsIgnoreCase(worldName)) {
|
||||
for (String name : file.list(filter)) {
|
||||
Snapshot snapshot = new Snapshot(this, file.getName() + "/" + name);
|
||||
detectDate(snapshot);
|
||||
list.add(snapshot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (newestFirst) {
|
||||
Collections.sort(list, Collections.reverseOrder());
|
||||
} else {
|
||||
Collections.sort(list);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first snapshot after a date.
|
||||
*
|
||||
* @param date a date
|
||||
* @return a snapshot or null
|
||||
*/
|
||||
@Nullable
|
||||
public Snapshot getSnapshotAfter(Calendar date, String world) throws MissingWorldException {
|
||||
List<Snapshot> snapshots = getSnapshots(true, world);
|
||||
Snapshot last = null;
|
||||
|
||||
for (Snapshot snapshot : snapshots) {
|
||||
if (snapshot.getDate() != null && snapshot.getDate().before(date)) {
|
||||
return last;
|
||||
}
|
||||
|
||||
last = snapshot;
|
||||
}
|
||||
|
||||
return last;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first snapshot before a date.
|
||||
*
|
||||
* @param date a date
|
||||
* @return a snapshot or null
|
||||
*/
|
||||
@Nullable
|
||||
public Snapshot getSnapshotBefore(Calendar date, String world) throws MissingWorldException {
|
||||
List<Snapshot> snapshots = getSnapshots(false, world);
|
||||
Snapshot last = null;
|
||||
|
||||
for (Snapshot snapshot : snapshots) {
|
||||
if (snapshot.getDate().after(date)) {
|
||||
return last;
|
||||
}
|
||||
|
||||
last = snapshot;
|
||||
}
|
||||
|
||||
return last;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to detect a snapshot's date and assign it.
|
||||
*
|
||||
* @param snapshot the snapshot
|
||||
*/
|
||||
protected void detectDate(Snapshot snapshot) {
|
||||
for (SnapshotDateParser parser : dateParsers) {
|
||||
Calendar date = parser.detectDate(snapshot.getFile());
|
||||
if (date != null) {
|
||||
snapshot.setDate(date);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
snapshot.setDate(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default snapshot.
|
||||
*
|
||||
* @param world the world name
|
||||
* @return a snapshot or null
|
||||
*/
|
||||
@Nullable
|
||||
public Snapshot getDefaultSnapshot(String world) throws MissingWorldException {
|
||||
List<Snapshot> snapshots = getSnapshots(true, world);
|
||||
|
||||
if (snapshots.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return snapshots.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if a snapshot is valid.
|
||||
*
|
||||
* @param snapshot a snapshot name
|
||||
* @return whether it is a valid snapshot
|
||||
*/
|
||||
public boolean isValidSnapshotName(String snapshot) {
|
||||
return isValidSnapshot(new File(dir, snapshot));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if a snapshot is valid.
|
||||
*
|
||||
* @param file the file to the snapshot
|
||||
* @return whether it is a valid snapshot
|
||||
*/
|
||||
protected boolean isValidSnapshot(File file) {
|
||||
if (!file.getName().matches("^[A-Za-z0-9_\\- \\./\\\\'\\$@~!%\\^\\*\\(\\)\\[\\]\\+\\{\\},\\?]+$")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (file.isDirectory() && (new File(file, "level.dat")).exists())
|
||||
|| (file.isFile() && (file.getName().toLowerCase().endsWith(".zip")
|
||||
|| file.getName().toLowerCase().endsWith(".tar.bz2")
|
||||
|| file.getName().toLowerCase().endsWith(".tar.gz")
|
||||
|| file.getName().toLowerCase().endsWith(".tar")));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a snapshot.
|
||||
*
|
||||
* @param name the name of the snapshot
|
||||
* @return a snapshot
|
||||
* @throws InvalidSnapshotException if the snapshot is invalid
|
||||
*/
|
||||
public Snapshot getSnapshot(String name) throws InvalidSnapshotException {
|
||||
if (!isValidSnapshotName(name)) {
|
||||
throw new InvalidSnapshotException();
|
||||
}
|
||||
|
||||
return new Snapshot(this, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the snapshot directory.
|
||||
*
|
||||
* @return a path
|
||||
*/
|
||||
public File getDirectory() {
|
||||
return dir;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,211 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.snapshot;
|
||||
|
||||
import com.sk89q.worldedit.BlockVector2D;
|
||||
import com.sk89q.worldedit.EditSession;
|
||||
import com.sk89q.worldedit.MaxChangedBlocksException;
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldedit.Vector2D;
|
||||
import com.sk89q.worldedit.blocks.BaseBlock;
|
||||
import com.sk89q.worldedit.regions.CuboidRegion;
|
||||
import com.sk89q.worldedit.regions.Region;
|
||||
import com.sk89q.worldedit.world.DataException;
|
||||
import com.sk89q.worldedit.world.chunk.Chunk;
|
||||
import com.sk89q.worldedit.world.storage.ChunkStore;
|
||||
import com.sk89q.worldedit.world.storage.MissingChunkException;
|
||||
import com.sk89q.worldedit.world.storage.MissingWorldException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A snapshot restore operation.
|
||||
*/
|
||||
public class SnapshotRestore {
|
||||
|
||||
private final Map<BlockVector2D, ArrayList<Vector>> neededChunks = new LinkedHashMap<BlockVector2D, ArrayList<Vector>>();
|
||||
private final ChunkStore chunkStore;
|
||||
private final EditSession editSession;
|
||||
private ArrayList<Vector2D> missingChunks;
|
||||
private ArrayList<Vector2D> errorChunks;
|
||||
private String lastErrorMessage;
|
||||
|
||||
/**
|
||||
* Construct the snapshot restore operation.
|
||||
*
|
||||
* @param chunkStore The {@link ChunkStore} to restore from
|
||||
* @param editSession The {@link EditSession} to restore to
|
||||
* @param region The {@link Region} to restore to
|
||||
*/
|
||||
public SnapshotRestore(ChunkStore chunkStore, EditSession editSession, Region region) {
|
||||
this.chunkStore = chunkStore;
|
||||
this.editSession = editSession;
|
||||
|
||||
if (region instanceof CuboidRegion) {
|
||||
findNeededCuboidChunks(region);
|
||||
} else {
|
||||
findNeededChunks(region);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find needed chunks in the axis-aligned bounding box of the region.
|
||||
*
|
||||
* @param region The {@link Region} to iterate
|
||||
*/
|
||||
private void findNeededCuboidChunks(Region region) {
|
||||
Vector min = region.getMinimumPoint();
|
||||
Vector max = region.getMaximumPoint();
|
||||
|
||||
// First, we need to group points by chunk so that we only need
|
||||
// to keep one chunk in memory at any given moment
|
||||
for (int x = min.getBlockX(); x <= max.getBlockX(); ++x) {
|
||||
for (int y = min.getBlockY(); y <= max.getBlockY(); ++y) {
|
||||
for (int z = min.getBlockZ(); z <= max.getBlockZ(); ++z) {
|
||||
Vector pos = new Vector(x, y, z);
|
||||
checkAndAddBlock(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find needed chunks in the region.
|
||||
*
|
||||
* @param region The {@link Region} to iterate
|
||||
*/
|
||||
private void findNeededChunks(Region region) {
|
||||
// First, we need to group points by chunk so that we only need
|
||||
// to keep one chunk in memory at any given moment
|
||||
for (Vector pos : region) {
|
||||
checkAndAddBlock(pos);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkAndAddBlock(Vector pos) {
|
||||
if (editSession.getMask() != null && !editSession.getMask().test(pos))
|
||||
return;
|
||||
|
||||
BlockVector2D chunkPos = ChunkStore.toChunk(pos);
|
||||
|
||||
// Unidentified chunk
|
||||
if (!neededChunks.containsKey(chunkPos)) {
|
||||
neededChunks.put(chunkPos, new ArrayList<Vector>());
|
||||
}
|
||||
|
||||
neededChunks.get(chunkPos).add(pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of chunks that are needed.
|
||||
*
|
||||
* @return a number of chunks
|
||||
*/
|
||||
public int getChunksAffected() {
|
||||
return neededChunks.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores to world.
|
||||
*
|
||||
* @throws MaxChangedBlocksException
|
||||
*/
|
||||
public void restore() throws MaxChangedBlocksException {
|
||||
|
||||
missingChunks = new ArrayList<Vector2D>();
|
||||
errorChunks = new ArrayList<Vector2D>();
|
||||
|
||||
// Now let's start restoring!
|
||||
for (Map.Entry<BlockVector2D, ArrayList<Vector>> entry : neededChunks.entrySet()) {
|
||||
BlockVector2D chunkPos = entry.getKey();
|
||||
Chunk chunk;
|
||||
|
||||
try {
|
||||
chunk = chunkStore.getChunk(chunkPos, editSession.getWorld());
|
||||
// Good, the chunk could be at least loaded
|
||||
|
||||
// Now just copy blocks!
|
||||
for (Vector pos : entry.getValue()) {
|
||||
try {
|
||||
BaseBlock block = chunk.getBlock(pos);
|
||||
editSession.setBlock(pos, block);
|
||||
} catch (DataException e) {
|
||||
// this is a workaround: just ignore for now
|
||||
}
|
||||
}
|
||||
} catch (MissingChunkException me) {
|
||||
missingChunks.add(chunkPos);
|
||||
} catch (MissingWorldException me) {
|
||||
errorChunks.add(chunkPos);
|
||||
lastErrorMessage = me.getMessage();
|
||||
} catch (DataException de) {
|
||||
errorChunks.add(chunkPos);
|
||||
lastErrorMessage = de.getMessage();
|
||||
} catch (IOException ioe) {
|
||||
errorChunks.add(chunkPos);
|
||||
lastErrorMessage = ioe.getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of the missing chunks. restore() must have been called
|
||||
* already.
|
||||
*
|
||||
* @return a list of coordinates
|
||||
*/
|
||||
public List<Vector2D> getMissingChunks() {
|
||||
return missingChunks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of the chunks that could not have been loaded for other
|
||||
* reasons. restore() must have been called already.
|
||||
*
|
||||
* @return a list of coordinates
|
||||
*/
|
||||
public List<Vector2D> getErrorChunks() {
|
||||
return errorChunks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see where the backup succeeded in any capacity. False will
|
||||
* be returned if no chunk could be successfully loaded.
|
||||
*
|
||||
* @return true if there was total failure
|
||||
*/
|
||||
public boolean hadTotalFailure() {
|
||||
return missingChunks.size() + errorChunks.size() == getChunksAffected();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last error message.
|
||||
*
|
||||
* @return a message
|
||||
*/
|
||||
public String getLastErrorMessage() {
|
||||
return lastErrorMessage;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.snapshot;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class YYMMDDHHIISSParser implements SnapshotDateParser {
|
||||
|
||||
protected Pattern patt =
|
||||
Pattern.compile("([0-9]+)[^0-9]?([0-9]+)[^0-9]?([0-9]+)[^0-9]?"
|
||||
+ "([0-9]+)[^0-9]?([0-9]+)[^0-9]?([0-9]+)");
|
||||
|
||||
@Override
|
||||
public Calendar detectDate(File file) {
|
||||
Matcher matcher = patt.matcher(file.getName());
|
||||
if (matcher.matches()) {
|
||||
int year = Integer.parseInt(matcher.group(1));
|
||||
int month = Integer.parseInt(matcher.group(2));
|
||||
int day = Integer.parseInt(matcher.group(3));
|
||||
int hrs = Integer.parseInt(matcher.group(4));
|
||||
int min = Integer.parseInt(matcher.group(5));
|
||||
int sec = Integer.parseInt(matcher.group(6));
|
||||
Calendar calender = new GregorianCalendar();
|
||||
calender.set(year, month, day, hrs, min, sec);
|
||||
return calender;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.storage;
|
||||
|
||||
import com.sk89q.worldedit.CuboidClipboard.FlipDirection;
|
||||
|
||||
/**
|
||||
* Block data related classes.
|
||||
*
|
||||
* @deprecated use {@link com.sk89q.worldedit.blocks.BlockData}
|
||||
*/
|
||||
@Deprecated
|
||||
public final class BlockData {
|
||||
|
||||
private BlockData() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotate a block's data value 90 degrees (north->east->south->west->north);
|
||||
*
|
||||
* @param type type ID of the block
|
||||
* @param data data value of the block
|
||||
* @return a new data value
|
||||
* @deprecated use {@link com.sk89q.worldedit.blocks.BlockData#rotate90(int, int)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static int rotate90(int type, int data) {
|
||||
return com.sk89q.worldedit.blocks.BlockData.rotate90(type, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotate a block's data value -90 degrees (north<-east<-south<-west<-north);
|
||||
*
|
||||
* @param type type ID of the block
|
||||
* @param data data value of the block
|
||||
* @return a new data value
|
||||
* @deprecated use {@link com.sk89q.worldedit.blocks.BlockData#rotate90Reverse(int, int)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static int rotate90Reverse(int type, int data) {
|
||||
return com.sk89q.worldedit.blocks.BlockData.rotate90Reverse(type, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flip a block's data value.
|
||||
*
|
||||
* @param type type ID of the block
|
||||
* @param data data value of the block
|
||||
* @return a new data value
|
||||
* @deprecated use return {@link com.sk89q.worldedit.blocks.BlockData#flip(int, int)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static int flip(int type, int data) {
|
||||
return rotate90(type, rotate90(type, data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Flip a block's data value.
|
||||
*
|
||||
* @param type type ID of the block
|
||||
* @param data data value of the block
|
||||
* @param direction the direction to flip
|
||||
* @return a new data value
|
||||
* @deprecated use {@link com.sk89q.worldedit.blocks.BlockData#flip(int, int, FlipDirection)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static int flip(int type, int data, FlipDirection direction) {
|
||||
return com.sk89q.worldedit.blocks.BlockData.flip(type, data, direction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cycle a block's data value. This usually goes through some rotational pattern
|
||||
* depending on the block. If it returns -1, it means the id and data specified
|
||||
* do not have anything to cycle to.
|
||||
*
|
||||
* @param type block id to be cycled
|
||||
* @param data block data value that it starts at
|
||||
* @param increment whether to go forward (1) or backward (-1) in the cycle
|
||||
* @return the new data value for the block
|
||||
* @deprecated use {@link com.sk89q.worldedit.blocks.BlockData#cycle(int, int, int)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static int cycle(int type, int data, int increment) {
|
||||
return com.sk89q.worldedit.blocks.BlockData.cycle(type, data, increment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data value for the next color of cloth in the rainbow. This
|
||||
* should not be used if you want to just increment the data value.
|
||||
*
|
||||
* @param data the data value
|
||||
* @return a new data value
|
||||
* @deprecated use {@link com.sk89q.worldedit.blocks.BlockData#nextClothColor(int)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static int nextClothColor(int data) {
|
||||
return com.sk89q.worldedit.blocks.BlockData.nextClothColor(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data value for the previous ext color of cloth in the rainbow.
|
||||
* This should not be used if you want to just increment the data value.
|
||||
*
|
||||
* @param data the data value
|
||||
* @return a new data value
|
||||
* @deprecated use {@link com.sk89q.worldedit.blocks.BlockData#prevClothColor(int)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static int prevClothColor(int data) {
|
||||
return com.sk89q.worldedit.blocks.BlockData.prevClothColor(data);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.storage;
|
||||
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.jnbt.Tag;
|
||||
import com.sk89q.worldedit.BlockVector2D;
|
||||
import com.sk89q.worldedit.Vector;
|
||||
import com.sk89q.worldedit.Vector2D;
|
||||
import com.sk89q.worldedit.world.DataException;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
import com.sk89q.worldedit.world.chunk.AnvilChunk;
|
||||
import com.sk89q.worldedit.world.chunk.Chunk;
|
||||
import com.sk89q.worldedit.world.chunk.OldChunk;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Represents chunk storage mechanisms.
|
||||
*/
|
||||
public abstract class ChunkStore {
|
||||
|
||||
/**
|
||||
* >> to chunk
|
||||
* << from chunk
|
||||
*/
|
||||
public static final int CHUNK_SHIFTS = 4;
|
||||
|
||||
/**
|
||||
* Convert a position to a chunk.
|
||||
*
|
||||
* @param position the position
|
||||
* @return chunk coordinates
|
||||
*/
|
||||
public static BlockVector2D toChunk(Vector position) {
|
||||
int chunkX = (int) Math.floor(position.getBlockX() / 16.0);
|
||||
int chunkZ = (int) Math.floor(position.getBlockZ() / 16.0);
|
||||
|
||||
return new BlockVector2D(chunkX, chunkZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tag for a chunk.
|
||||
*
|
||||
* @param position the position of the chunk
|
||||
* @return tag
|
||||
* @throws DataException thrown on data error
|
||||
* @throws IOException thrown on I/O error
|
||||
*/
|
||||
public abstract CompoundTag getChunkTag(Vector2D position, World world) throws DataException, IOException;
|
||||
|
||||
/**
|
||||
* Get a chunk at a location.
|
||||
*
|
||||
* @param position the position of the chunk
|
||||
* @return a chunk
|
||||
* @throws ChunkStoreException thrown if there is an error from the chunk store
|
||||
* @throws DataException thrown on data error
|
||||
* @throws IOException thrown on I/O error
|
||||
*/
|
||||
public Chunk getChunk(Vector2D position, World world) throws DataException, IOException {
|
||||
CompoundTag tag = getChunkTag(position, world);
|
||||
Map<String, Tag> tags = tag.getValue();
|
||||
if (tags.containsKey("Sections")) {
|
||||
return new AnvilChunk(world, tag);
|
||||
}
|
||||
|
||||
return new OldChunk(world, tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close resources.
|
||||
*
|
||||
* @throws IOException on I/O error
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the chunk store is of this type.
|
||||
*
|
||||
* @return true if valid
|
||||
*/
|
||||
public abstract boolean isValid();
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.storage;
|
||||
|
||||
import com.sk89q.worldedit.world.DataException;
|
||||
|
||||
public class ChunkStoreException extends DataException {
|
||||
|
||||
public ChunkStoreException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public ChunkStoreException() {
|
||||
super();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.storage;
|
||||
|
||||
import com.sk89q.worldedit.world.DataException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Represents the chunk store used by Minecraft Alpha.
|
||||
*/
|
||||
public class FileLegacyChunkStore extends LegacyChunkStore {
|
||||
|
||||
private File path;
|
||||
|
||||
/**
|
||||
* Create an instance. The passed path is the folder to read the
|
||||
* chunk files from.
|
||||
*
|
||||
* @param path path to a folder
|
||||
*/
|
||||
public FileLegacyChunkStore(File path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the input stream for a chunk file.
|
||||
*
|
||||
* @param f1 the first part of the pathname
|
||||
* @param f2 the second part of the pathname
|
||||
* @param name the name of the file
|
||||
* @return an input stream
|
||||
* @throws DataException
|
||||
* @throws IOException
|
||||
*/
|
||||
@Override
|
||||
protected InputStream getInputStream(String f1, String f2, String name) throws DataException, IOException {
|
||||
String file = f1 + File.separator + f2 + File.separator + name;
|
||||
try {
|
||||
return new FileInputStream(new File(path, file));
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new MissingChunkException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return true; // Yeah, oh well
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.storage;
|
||||
|
||||
import com.sk89q.worldedit.world.DataException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class FileMcRegionChunkStore extends McRegionChunkStore {
|
||||
|
||||
private File path;
|
||||
|
||||
/**
|
||||
* Create an instance. The passed path is the folder to read the
|
||||
* chunk files from.
|
||||
*
|
||||
* @param path a path
|
||||
*/
|
||||
public FileMcRegionChunkStore(File path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InputStream getInputStream(String name, String world) throws IOException, DataException {
|
||||
Pattern ext = Pattern.compile(".*\\.mc[ra]$"); // allow either file extension, both work the same
|
||||
File file = null;
|
||||
File[] files = new File(path, "region").listFiles();
|
||||
|
||||
if (files == null) {
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
|
||||
for (File f : files) {
|
||||
String tempName = f.getName().replaceFirst("mcr$", "mca"); // matcher only does one at a time
|
||||
if (ext.matcher(f.getName()).matches() && name.equalsIgnoreCase(tempName)) {
|
||||
// get full original path now
|
||||
file = new File(path + File.separator + "region" + File.separator + f.getName());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (file == null) throw new FileNotFoundException();
|
||||
return new FileInputStream(file);
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new MissingChunkException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return new File(path, "region").isDirectory() ||
|
||||
new File(path, "DIM-1" + File.separator + "region").isDirectory();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.storage;
|
||||
|
||||
import com.sk89q.worldedit.world.DataException;
|
||||
|
||||
public class InvalidFormatException extends DataException {
|
||||
|
||||
public InvalidFormatException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.storage;
|
||||
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.jnbt.NBTInputStream;
|
||||
import com.sk89q.jnbt.Tag;
|
||||
import com.sk89q.worldedit.*;
|
||||
import com.sk89q.worldedit.world.DataException;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Map;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
/**
|
||||
* Represents chunk stores that use Alpha's file format for storing chunks.
|
||||
* The code to resolve the filename is already implemented in this class
|
||||
* and an inheriting class merely needs to implement getInputStream().
|
||||
*/
|
||||
public abstract class LegacyChunkStore extends ChunkStore {
|
||||
|
||||
/**
|
||||
* Get the filename of a chunk.
|
||||
*
|
||||
* @param position chunk position
|
||||
* @param separator folder separator character
|
||||
* @return pathname
|
||||
*/
|
||||
public static String getFilename(Vector2D position, String separator) {
|
||||
int x = position.getBlockX();
|
||||
int z = position.getBlockZ();
|
||||
|
||||
String folder1 = Integer.toString(divisorMod(x, 64), 36);
|
||||
String folder2 = Integer.toString(divisorMod(z, 64), 36);
|
||||
String filename = "c." + Integer.toString(x, 36)
|
||||
+ "." + Integer.toString(z, 36) + ".dat";
|
||||
|
||||
return folder1 + separator + folder2 + separator + filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the filename of a chunk, using the system's default path
|
||||
* separator.
|
||||
*
|
||||
* @param position chunk position
|
||||
* @return pathname
|
||||
*/
|
||||
public static String getFilename(Vector2D position) {
|
||||
return getFilename(position, File.separator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getChunkTag(Vector2D position, World world) throws DataException, IOException {
|
||||
int x = position.getBlockX();
|
||||
int z = position.getBlockZ();
|
||||
|
||||
String folder1 = Integer.toString(divisorMod(x, 64), 36);
|
||||
String folder2 = Integer.toString(divisorMod(z, 64), 36);
|
||||
String filename = "c." + Integer.toString(x, 36)
|
||||
+ "." + Integer.toString(z, 36) + ".dat";
|
||||
|
||||
InputStream stream = getInputStream(folder1, folder2, filename);
|
||||
NBTInputStream nbt = new NBTInputStream(
|
||||
new GZIPInputStream(stream));
|
||||
Tag tag;
|
||||
|
||||
try {
|
||||
tag = nbt.readTag();
|
||||
if (!(tag instanceof CompoundTag)) {
|
||||
throw new ChunkStoreException("CompoundTag expected for chunk; got "
|
||||
+ tag.getClass().getName());
|
||||
}
|
||||
|
||||
Map<String, Tag> children = ((CompoundTag) tag).getValue();
|
||||
CompoundTag rootTag = null;
|
||||
|
||||
// Find Level tag
|
||||
for (Map.Entry<String, Tag> entry : children.entrySet()) {
|
||||
if (entry.getKey().equals("Level")) {
|
||||
if (entry.getValue() instanceof CompoundTag) {
|
||||
rootTag = (CompoundTag) entry.getValue();
|
||||
break;
|
||||
} else {
|
||||
throw new ChunkStoreException("CompoundTag expected for 'Level'; got "
|
||||
+ entry.getValue().getClass().getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rootTag == null) {
|
||||
throw new ChunkStoreException("Missing root 'Level' tag");
|
||||
}
|
||||
|
||||
return rootTag;
|
||||
} finally {
|
||||
nbt.close();
|
||||
}
|
||||
}
|
||||
|
||||
private static int divisorMod(int a, int n) {
|
||||
return (int) (a - n * Math.floor(Math.floor(a) / (double) n));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the input stream for a chunk file.
|
||||
*
|
||||
* @param f1 the first part of the path
|
||||
* @param f2 the second part of the path
|
||||
* @param name the name
|
||||
* @return an input stream
|
||||
* @throws IOException
|
||||
*/
|
||||
protected abstract InputStream getInputStream(String f1, String f2, String name) throws IOException, DataException;
|
||||
|
||||
}
|
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.storage;
|
||||
|
||||
import com.sk89q.jnbt.CompoundTag;
|
||||
import com.sk89q.jnbt.NBTInputStream;
|
||||
import com.sk89q.jnbt.Tag;
|
||||
import com.sk89q.worldedit.Vector2D;
|
||||
import com.sk89q.worldedit.world.DataException;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class McRegionChunkStore extends ChunkStore {
|
||||
|
||||
protected String curFilename = null;
|
||||
protected McRegionReader cachedReader = null;
|
||||
|
||||
/**
|
||||
* Get the filename of a region file.
|
||||
*
|
||||
* @param position chunk position
|
||||
* @return the filename
|
||||
*/
|
||||
public static String getFilename(Vector2D position) {
|
||||
int x = position.getBlockX();
|
||||
int z = position.getBlockZ();
|
||||
|
||||
return "r." + (x >> 5) + "." + (z >> 5) + ".mca";
|
||||
}
|
||||
|
||||
protected McRegionReader getReader(Vector2D pos, String worldname) throws DataException, IOException {
|
||||
String filename = getFilename(pos);
|
||||
if (curFilename != null) {
|
||||
if (curFilename.equals(filename)) {
|
||||
return cachedReader;
|
||||
} else {
|
||||
try {
|
||||
cachedReader.close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
InputStream stream = getInputStream(filename, worldname);
|
||||
cachedReader = new McRegionReader(stream);
|
||||
//curFilename = filename;
|
||||
return cachedReader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getChunkTag(Vector2D position, World world) throws DataException, IOException {
|
||||
McRegionReader reader = getReader(position, world.getName());
|
||||
|
||||
InputStream stream = reader.getChunkInputStream(position);
|
||||
NBTInputStream nbt = new NBTInputStream(stream);
|
||||
Tag tag;
|
||||
|
||||
try {
|
||||
tag = nbt.readTag();
|
||||
if (!(tag instanceof CompoundTag)) {
|
||||
throw new ChunkStoreException("CompoundTag expected for chunk; got " + tag.getClass().getName());
|
||||
}
|
||||
|
||||
Map<String, Tag> children = ((CompoundTag) tag).getValue();
|
||||
CompoundTag rootTag = null;
|
||||
|
||||
// Find Level tag
|
||||
for (Map.Entry<String, Tag> entry : children.entrySet()) {
|
||||
if (entry.getKey().equals("Level")) {
|
||||
if (entry.getValue() instanceof CompoundTag) {
|
||||
rootTag = (CompoundTag) entry.getValue();
|
||||
break;
|
||||
} else {
|
||||
throw new ChunkStoreException("CompoundTag expected for 'Level'; got " + entry.getValue().getClass().getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rootTag == null) {
|
||||
throw new ChunkStoreException("Missing root 'Level' tag");
|
||||
}
|
||||
|
||||
return rootTag;
|
||||
} finally {
|
||||
nbt.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the input stream for a chunk file.
|
||||
*
|
||||
* @param name the name of the chunk file
|
||||
* @param worldName the world name
|
||||
* @return an input stream
|
||||
* @throws IOException
|
||||
*/
|
||||
protected abstract InputStream getInputStream(String name, String worldName) throws IOException, DataException;
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (cachedReader != null) {
|
||||
cachedReader.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,199 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// $Id$
|
||||
/*
|
||||
|
||||
Region File Format
|
||||
|
||||
Concept: The minimum unit of storage on hard drives is 4KB. 90% of Minecraft
|
||||
chunks are smaller than 4KB. 99% are smaller than 8KB. Write a simple
|
||||
container to store chunks in single files in runs of 4KB sectors.
|
||||
|
||||
Each region file represents a 32x32 group of chunks. The conversion from
|
||||
chunk number to region number is floor(coord / 32): a chunk at (30, -3)
|
||||
would be in region (0, -1), and one at (70, -30) would be at (3, -1).
|
||||
Region files are named "r.x.z.data", where x and z are the region coordinates.
|
||||
|
||||
A region file begins with a 4KB header that describes where chunks are stored
|
||||
in the file. A 4-byte big-endian integer represents sector offsets and sector
|
||||
counts. The chunk offset for a chunk (x, z) begins at byte 4*(x+z*32) in the
|
||||
file. The bottom byte of the chunk offset indicates the number of sectors the
|
||||
chunk takes up, and the top 3 bytes represent the sector number of the chunk.
|
||||
Given a chunk offset o, the chunk data begins at byte 4096*(o/256) and takes up
|
||||
at most 4096*(o%256) bytes. A chunk cannot exceed 1MB in size. If a chunk
|
||||
offset is 0, the corresponding chunk is not stored in the region file.
|
||||
|
||||
Chunk data begins with a 4-byte big-endian integer representing the chunk data
|
||||
length in bytes, not counting the length field. The length must be smaller than
|
||||
4096 times the number of sectors. The next byte is a version field, to allow
|
||||
backwards-compatible updates to how chunks are encoded.
|
||||
|
||||
A version of 1 represents a gzipped NBT file. The gzipped data is the chunk
|
||||
length - 1.
|
||||
|
||||
A version of 2 represents a deflated (zlib compressed) NBT file. The deflated
|
||||
data is the chunk length - 1.
|
||||
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.storage;
|
||||
|
||||
import com.sk89q.worldedit.Vector2D;
|
||||
import com.sk89q.worldedit.util.io.ForwardSeekableInputStream;
|
||||
import com.sk89q.worldedit.world.DataException;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
|
||||
/**
|
||||
* Reader for a MCRegion file. This reader works on input streams, meaning
|
||||
* that it can be used to read files from non-file based sources.
|
||||
*/
|
||||
public class McRegionReader {
|
||||
|
||||
protected static final int VERSION_GZIP = 1;
|
||||
protected static final int VERSION_DEFLATE = 2;
|
||||
protected static final int SECTOR_BYTES = 4096;
|
||||
protected static final int SECTOR_INTS = SECTOR_BYTES / 4;
|
||||
public static final int CHUNK_HEADER_SIZE = 5;
|
||||
|
||||
protected ForwardSeekableInputStream stream;
|
||||
protected DataInputStream dataStream;
|
||||
|
||||
protected int[] offsets;
|
||||
|
||||
/**
|
||||
* Construct the reader.
|
||||
*
|
||||
* @param stream the stream
|
||||
* @throws DataException
|
||||
* @throws IOException
|
||||
*/
|
||||
public McRegionReader(InputStream stream) throws DataException, IOException {
|
||||
this.stream = new ForwardSeekableInputStream(stream);
|
||||
this.dataStream = new DataInputStream(this.stream);
|
||||
|
||||
readHeader();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the header.
|
||||
*
|
||||
* @throws DataException
|
||||
* @throws IOException
|
||||
*/
|
||||
private void readHeader() throws DataException, IOException {
|
||||
offsets = new int[SECTOR_INTS];
|
||||
|
||||
for (int i = 0; i < SECTOR_INTS; ++i) {
|
||||
int offset = dataStream.readInt();
|
||||
offsets[i] = offset;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the uncompressed data input stream for a chunk.
|
||||
*
|
||||
* @param position chunk position
|
||||
* @return an input stream
|
||||
* @throws IOException
|
||||
* @throws DataException
|
||||
*/
|
||||
public synchronized InputStream getChunkInputStream(Vector2D position) throws IOException, DataException {
|
||||
int x = position.getBlockX() & 31;
|
||||
int z = position.getBlockZ() & 31;
|
||||
|
||||
if (x < 0 || x >= 32 || z < 0 || z >= 32) {
|
||||
throw new DataException("MCRegion file does not contain " + x + "," + z);
|
||||
}
|
||||
|
||||
int offset = getOffset(x, z);
|
||||
|
||||
// The chunk hasn't been generated
|
||||
if (offset == 0) {
|
||||
throw new DataException("The chunk at " + x + "," + z + " is not generated");
|
||||
}
|
||||
|
||||
int sectorNumber = offset >> 8;
|
||||
int numSectors = offset & 0xFF;
|
||||
|
||||
stream.seek(sectorNumber * SECTOR_BYTES);
|
||||
int length = dataStream.readInt();
|
||||
|
||||
if (length > SECTOR_BYTES * numSectors) {
|
||||
throw new DataException("MCRegion chunk at "
|
||||
+ x + "," + z + " has an invalid length of " + length);
|
||||
}
|
||||
|
||||
byte version = dataStream.readByte();
|
||||
|
||||
if (version == VERSION_GZIP) {
|
||||
byte[] data = new byte[length - 1];
|
||||
if (dataStream.read(data) < length - 1) {
|
||||
throw new DataException("MCRegion file does not contain "
|
||||
+ x + "," + z + " in full");
|
||||
}
|
||||
return new GZIPInputStream(new ByteArrayInputStream(data));
|
||||
} else if (version == VERSION_DEFLATE) {
|
||||
byte[] data = new byte[length - 1];
|
||||
if (dataStream.read(data) < length - 1) {
|
||||
throw new DataException("MCRegion file does not contain "
|
||||
+ x + "," + z + " in full");
|
||||
}
|
||||
return new InflaterInputStream(new ByteArrayInputStream(data));
|
||||
} else {
|
||||
throw new DataException("MCRegion chunk at "
|
||||
+ x + "," + z + " has an unsupported version of " + version);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the offset for a chunk. May return 0 if it doesn't exist.
|
||||
*
|
||||
* @param x the X coordinate
|
||||
* @param z the Z coordinate
|
||||
* @return the offset
|
||||
*/
|
||||
private int getOffset(int x, int z) {
|
||||
return offsets[x + z * 32];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the file contains a chunk.
|
||||
*
|
||||
* @param x the X coordinate
|
||||
* @param z the Z coordinate
|
||||
* @return the offset
|
||||
*/
|
||||
public boolean hasChunk(int x, int z) {
|
||||
return getOffset(x, z) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the stream.
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
stream.close();
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.storage;
|
||||
|
||||
import com.sk89q.worldedit.Vector2D;
|
||||
|
||||
/**
|
||||
* Thrown if a chunk is missing.
|
||||
*/
|
||||
public class MissingChunkException extends ChunkStoreException {
|
||||
|
||||
private Vector2D position;
|
||||
|
||||
public MissingChunkException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public MissingChunkException(Vector2D position) {
|
||||
super();
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get chunk position in question. May be null if unknown.
|
||||
*
|
||||
* @return a chunk position
|
||||
*/
|
||||
public Vector2D getChunkPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.storage;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Thrown if the world is missing.
|
||||
*/
|
||||
public class MissingWorldException extends ChunkStoreException {
|
||||
|
||||
private String worldName;
|
||||
|
||||
public MissingWorldException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public MissingWorldException(String worldName) {
|
||||
super();
|
||||
this.worldName = worldName;
|
||||
}
|
||||
|
||||
public MissingWorldException(String msg, String worldName) {
|
||||
super(msg);
|
||||
this.worldName = worldName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get name of the world in question. May be null if unknown.
|
||||
*
|
||||
* @return the world name
|
||||
*/
|
||||
@Nullable
|
||||
public String getWorldName() {
|
||||
return worldName;
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.storage;
|
||||
|
||||
import com.sk89q.jnbt.ListTag;
|
||||
import com.sk89q.worldedit.extent.Extent;
|
||||
import com.sk89q.worldedit.util.Location;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
/**
|
||||
* Utility methods for working with NBT data used in Minecraft.
|
||||
*/
|
||||
public final class NBTConversions {
|
||||
|
||||
private NBTConversions() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a {@code Location} from two list tags, the first of which contains
|
||||
* three numbers for the X, Y, and Z components, and the second of
|
||||
* which contains two numbers, the yaw and pitch in degrees.
|
||||
*
|
||||
* <p>For values that are unavailable, their values will be 0.</p>
|
||||
*
|
||||
* @param extent the extent
|
||||
* @param positionTag the position tag
|
||||
* @param directionTag the direction tag
|
||||
* @return a location
|
||||
*/
|
||||
public static Location toLocation(Extent extent, ListTag positionTag, ListTag directionTag) {
|
||||
checkNotNull(extent);
|
||||
checkNotNull(positionTag);
|
||||
checkNotNull(directionTag);
|
||||
return new Location(
|
||||
extent,
|
||||
positionTag.asDouble(0), positionTag.asDouble(1), positionTag.asDouble(2),
|
||||
(float) directionTag.asDouble(0), (float) directionTag.asDouble(1));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,158 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.storage;
|
||||
|
||||
import com.sk89q.worldedit.world.DataException;
|
||||
import de.schlichtherle.util.zip.ZipEntry;
|
||||
import de.schlichtherle.util.zip.ZipFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Enumeration;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.zip.ZipException;
|
||||
|
||||
/**
|
||||
* Represents the chunk store used by Minecraft alpha but zipped. Uses
|
||||
* the replacement classes for java.util.zip.* from TrueZip.
|
||||
*/
|
||||
public class TrueZipLegacyChunkStore extends LegacyChunkStore {
|
||||
|
||||
private File zipFile;
|
||||
private ZipFile zip;
|
||||
private String folder;
|
||||
|
||||
/**
|
||||
* Create an instance. The folder argument lets you choose a folder or
|
||||
* path to look into in the ZIP for the files. Use a blank string for
|
||||
* the folder to not look into a subdirectory.
|
||||
*
|
||||
* @param zipFile the ZIP file to open
|
||||
* @param folder the folder to look into in the ZIP
|
||||
* @throws IOException
|
||||
* @throws ZipException
|
||||
*/
|
||||
public TrueZipLegacyChunkStore(File zipFile, String folder) throws IOException, ZipException {
|
||||
this.zipFile = zipFile;
|
||||
this.folder = folder;
|
||||
|
||||
zip = new ZipFile(zipFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance. The subf-older containing the chunk data will
|
||||
* be detected.
|
||||
*
|
||||
* @param zipFile the ZIP file to open
|
||||
* @throws IOException
|
||||
* @throws ZipException
|
||||
*/
|
||||
public TrueZipLegacyChunkStore(File zipFile) throws IOException, ZipException {
|
||||
this.zipFile = zipFile;
|
||||
|
||||
zip = new ZipFile(zipFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the input stream for a chunk file.
|
||||
*
|
||||
* @param f1 the first part of the filename
|
||||
* @param f2 the second part of the filename
|
||||
* @param name the name of the file
|
||||
* @return an input stream
|
||||
* @throws IOException
|
||||
* @throws DataException
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected InputStream getInputStream(String f1, String f2, String name) throws IOException, DataException {
|
||||
String file = f1 + "/" + f2 + "/" + name;
|
||||
|
||||
// Detect subfolder for the world's files
|
||||
if (folder != null) {
|
||||
if (!folder.equals("")) {
|
||||
file = folder + "/" + file;
|
||||
}
|
||||
} else {
|
||||
ZipEntry testEntry = zip.getEntry("level.dat");
|
||||
|
||||
// So, the data is not in the root directory
|
||||
if (testEntry == null) {
|
||||
// Let's try a world/ sub-directory
|
||||
testEntry = getEntry("world/level.dat");
|
||||
|
||||
Pattern pattern = Pattern.compile(".*[\\\\/]level\\.dat$");
|
||||
|
||||
// So not there either...
|
||||
if (testEntry == null) {
|
||||
for (Enumeration<? extends ZipEntry> e = zip.entries(); e.hasMoreElements(); ) {
|
||||
testEntry = e.nextElement();
|
||||
|
||||
// Whoo, found level.dat!
|
||||
if (pattern.matcher(testEntry.getName()).matches()) {
|
||||
folder = testEntry.getName().replaceAll("level\\.dat$", "");
|
||||
folder = folder.substring(0, folder.length() - 1);
|
||||
file = folder + file;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
file = "world/" + file;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ZipEntry entry = getEntry(file);
|
||||
if (entry == null) {
|
||||
throw new MissingChunkException();
|
||||
}
|
||||
try {
|
||||
return zip.getInputStream(entry);
|
||||
} catch (ZipException e) {
|
||||
throw new IOException("Failed to read " + file + " in ZIP");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an entry from the ZIP, trying both types of slashes.
|
||||
*
|
||||
* @param file the file
|
||||
* @return an entry
|
||||
*/
|
||||
private ZipEntry getEntry(String file) {
|
||||
ZipEntry entry = zip.getEntry(file);
|
||||
if (entry != null) {
|
||||
return entry;
|
||||
}
|
||||
return zip.getEntry(file.replace("/", "\\"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
zip.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return true; // Yeah, oh well
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,161 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// $Id$
|
||||
|
||||
package com.sk89q.worldedit.world.storage;
|
||||
|
||||
import com.sk89q.worldedit.world.DataException;
|
||||
import de.schlichtherle.util.zip.ZipEntry;
|
||||
import de.schlichtherle.util.zip.ZipFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Enumeration;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.zip.ZipException;
|
||||
|
||||
/**
|
||||
* Represents the chunk store used by Minecraft but zipped. Uses
|
||||
* the replacement classes for java.util.zip.* from TrueZip.
|
||||
*/
|
||||
public class TrueZipMcRegionChunkStore extends McRegionChunkStore {
|
||||
|
||||
protected File zipFile;
|
||||
protected ZipFile zip;
|
||||
protected String folder;
|
||||
|
||||
/**
|
||||
* Create an instance. The folder argument lets you choose a folder or
|
||||
* path to look into in the ZIP for the files. Use a blank string for
|
||||
* the folder to not look into a subdirectory.
|
||||
*
|
||||
* @param zipFile the ZIP file
|
||||
* @param folder the folder to look into
|
||||
* @throws IOException
|
||||
* @throws ZipException
|
||||
*/
|
||||
public TrueZipMcRegionChunkStore(File zipFile, String folder) throws IOException, ZipException {
|
||||
this.zipFile = zipFile;
|
||||
this.folder = folder;
|
||||
|
||||
zip = new ZipFile(zipFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance. The subfolder containing the chunk data will
|
||||
* be detected.
|
||||
*
|
||||
* @param zipFile the ZIP file
|
||||
* @throws IOException
|
||||
* @throws ZipException
|
||||
*/
|
||||
public TrueZipMcRegionChunkStore(File zipFile) throws IOException, ZipException {
|
||||
this.zipFile = zipFile;
|
||||
|
||||
zip = new ZipFile(zipFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the input stream for a chunk file.
|
||||
*
|
||||
* @param name the name
|
||||
* @param worldName the world name
|
||||
* @return an input stream
|
||||
* @throws IOException
|
||||
* @throws DataException
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected InputStream getInputStream(String name, String worldName) throws IOException, DataException {
|
||||
// Detect subfolder for the world's files
|
||||
if (folder != null) {
|
||||
if (!folder.equals("")) {
|
||||
name = folder + "/" + name;
|
||||
}
|
||||
} else {
|
||||
Pattern pattern = Pattern.compile(".*\\.mc[ra]$");
|
||||
// World pattern
|
||||
Pattern worldPattern = Pattern.compile(worldName + "\\$");
|
||||
for (Enumeration<? extends ZipEntry> e = zip.entries(); e.hasMoreElements(); ) {
|
||||
ZipEntry testEntry = e.nextElement();
|
||||
// Check for world
|
||||
if (worldPattern.matcher(worldName).matches()) {
|
||||
// Check for file
|
||||
if (pattern.matcher(testEntry.getName()).matches()) {
|
||||
folder = testEntry.getName().substring(0, testEntry.getName().lastIndexOf("/"));
|
||||
name = folder + "/" + name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if world is found
|
||||
if (folder == null) {
|
||||
throw new MissingWorldException("Target world is not present in ZIP.", worldName);
|
||||
}
|
||||
}
|
||||
|
||||
ZipEntry entry = getEntry(name);
|
||||
if (entry == null) {
|
||||
throw new MissingChunkException();
|
||||
}
|
||||
try {
|
||||
return zip.getInputStream(entry);
|
||||
} catch (ZipException e) {
|
||||
throw new IOException("Failed to read " + name + " in ZIP");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an entry from the ZIP, trying both types of slashes.
|
||||
*
|
||||
* @param file the file
|
||||
* @return an entry
|
||||
*/
|
||||
private ZipEntry getEntry(String file) {
|
||||
ZipEntry entry = zip.getEntry(file);
|
||||
if (entry != null) {
|
||||
return entry;
|
||||
}
|
||||
return zip.getEntry(file.replace("/", "\\"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
zip.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean isValid() {
|
||||
for (Enumeration<? extends ZipEntry> e = zip.entries(); e.hasMoreElements(); ) {
|
||||
|
||||
ZipEntry testEntry = e.nextElement();
|
||||
|
||||
if (testEntry.getName().matches(".*\\.mcr$") || testEntry.getName().matches(".*\\.mca$")) { // TODO: does this need a separate class?
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,153 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sk89q.worldedit.world.storage;
|
||||
|
||||
import com.sk89q.worldedit.world.DataException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipException;
|
||||
import java.util.zip.ZipFile;
|
||||
import java.util.Enumeration;
|
||||
|
||||
/**
|
||||
* Represents the chunk store used by Minecraft alpha but zipped.
|
||||
*/
|
||||
public class ZippedLegacyChunkStore extends LegacyChunkStore {
|
||||
|
||||
private ZipFile zip;
|
||||
private String folder;
|
||||
|
||||
/**
|
||||
* Create an instance. The folder argument lets you choose a folder or
|
||||
* path to look into in the ZIP for the files. Use a blank string for
|
||||
* the folder to not look into a subdirectory.
|
||||
*
|
||||
* @param zipFile the zip file
|
||||
* @param folder the folder
|
||||
* @throws IOException
|
||||
* @throws ZipException
|
||||
*/
|
||||
public ZippedLegacyChunkStore(File zipFile, String folder) throws IOException, ZipException {
|
||||
this.folder = folder;
|
||||
|
||||
zip = new ZipFile(zipFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance. The subfolder containing the chunk data will
|
||||
* be detected.
|
||||
*
|
||||
* @param zipFile the zip file
|
||||
* @throws IOException
|
||||
* @throws ZipException
|
||||
*/
|
||||
public ZippedLegacyChunkStore(File zipFile) throws IOException, ZipException {
|
||||
zip = new ZipFile(zipFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the input stream for a chunk file.
|
||||
*
|
||||
* @param f1 the first part of the path
|
||||
* @param f2 the second part of the path
|
||||
* @param name the name of the file
|
||||
* @return an input stream
|
||||
* @throws IOException
|
||||
* @throws DataException
|
||||
*/
|
||||
@Override
|
||||
protected InputStream getInputStream(String f1, String f2, String name) throws IOException, DataException {
|
||||
String file = f1 + "/" + f2 + "/" + name;
|
||||
|
||||
// Detect subfolder for the world's files
|
||||
if (folder != null) {
|
||||
if (!folder.equals("")) {
|
||||
file = folder + "/" + file;
|
||||
}
|
||||
} else {
|
||||
ZipEntry testEntry = zip.getEntry("level.dat");
|
||||
|
||||
// So, the data is not in the root directory
|
||||
if (testEntry == null) {
|
||||
// Let's try a world/ sub-directory
|
||||
testEntry = getEntry("world/level.dat");
|
||||
|
||||
Pattern pattern = Pattern.compile(".*[\\\\/]level\\.dat$");
|
||||
|
||||
// So not there either...
|
||||
if (testEntry == null) {
|
||||
for (Enumeration<? extends ZipEntry> e = zip.entries(); e.hasMoreElements(); ) {
|
||||
|
||||
testEntry = e.nextElement();
|
||||
|
||||
// Whoo, found level.dat!
|
||||
if (pattern.matcher(testEntry.getName()).matches()) {
|
||||
folder = testEntry.getName().replaceAll("level\\.dat$", "");
|
||||
folder = folder.substring(0, folder.length() - 1);
|
||||
file = folder + file;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
file = "world/" + file;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ZipEntry entry = getEntry(file);
|
||||
if (entry == null) {
|
||||
throw new MissingChunkException();
|
||||
}
|
||||
try {
|
||||
return zip.getInputStream(entry);
|
||||
} catch (ZipException e) {
|
||||
throw new IOException("Failed to read " + file + " in ZIP");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an entry from the ZIP, trying both types of slashes.
|
||||
*
|
||||
* @param file the file
|
||||
* @return an entry
|
||||
*/
|
||||
private ZipEntry getEntry(String file) {
|
||||
ZipEntry entry = zip.getEntry(file);
|
||||
if (entry != null) {
|
||||
return entry;
|
||||
}
|
||||
return zip.getEntry(file.replace("/", "\\"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
zip.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return true; // Yeah, oh well
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* WorldEdit, a Minecraft world manipulation toolkit
|
||||
* Copyright (C) sk89q <http://www.sk89q.com>
|
||||
* Copyright (C) WorldEdit team and contributors
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// $Id$
|
||||
|
||||
package com.sk89q.worldedit.world.storage;
|
||||
|
||||
import com.sk89q.worldedit.world.DataException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipException;
|
||||
import java.util.zip.ZipFile;
|
||||
import java.util.Enumeration;
|
||||
|
||||
/**
|
||||
* Represents the chunk store used by Minecraft alpha but zipped.
|
||||
*/
|
||||
public class ZippedMcRegionChunkStore extends McRegionChunkStore {
|
||||
|
||||
protected File zipFile;
|
||||
protected ZipFile zip;
|
||||
protected String folder;
|
||||
|
||||
/**
|
||||
* Create an instance. The folder argument lets you choose a folder or
|
||||
* path to look into in the ZIP for the files. Use a blank string for
|
||||
* the folder to not look into a subdirectory.
|
||||
*
|
||||
* @param zipFile the ZIP file
|
||||
* @param folder the folder
|
||||
* @throws IOException
|
||||
* @throws ZipException
|
||||
*/
|
||||
public ZippedMcRegionChunkStore(File zipFile, String folder) throws IOException, ZipException {
|
||||
this.zipFile = zipFile;
|
||||
this.folder = folder;
|
||||
|
||||
zip = new ZipFile(zipFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance. The sub-folder containing the chunk data will
|
||||
* be detected.
|
||||
*
|
||||
* @param zipFile the ZIP file
|
||||
* @throws IOException
|
||||
* @throws ZipException
|
||||
*/
|
||||
public ZippedMcRegionChunkStore(File zipFile) throws IOException, ZipException {
|
||||
this.zipFile = zipFile;
|
||||
|
||||
zip = new ZipFile(zipFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InputStream getInputStream(String name, String worldName) throws IOException, DataException {
|
||||
// Detect subfolder for the world's files
|
||||
if (folder != null) {
|
||||
if (!folder.equals("")) {
|
||||
name = folder + "/" + name;
|
||||
}
|
||||
} else {
|
||||
Pattern pattern = Pattern.compile(".*\\.mc[ra]$");
|
||||
for (Enumeration<? extends ZipEntry> e = zip.entries(); e.hasMoreElements(); ) {
|
||||
ZipEntry testEntry = e.nextElement();
|
||||
// Check for world
|
||||
if (testEntry.getName().startsWith(worldName + "/")) {
|
||||
if (pattern.matcher(testEntry.getName()).matches()) { // does entry end in .mca
|
||||
folder = testEntry.getName().substring(0, testEntry.getName().lastIndexOf("/"));
|
||||
name = folder + "/" + name;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Check if world is found
|
||||
if (folder == null) {
|
||||
throw new MissingWorldException("Target world is not present in ZIP.", worldName);
|
||||
}
|
||||
}
|
||||
|
||||
ZipEntry entry = getEntry(name);
|
||||
if (entry == null) {
|
||||
throw new MissingChunkException();
|
||||
}
|
||||
try {
|
||||
return zip.getInputStream(entry);
|
||||
} catch (ZipException e) {
|
||||
throw new IOException("Failed to read " + name + " in ZIP");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an entry from the ZIP, trying both types of slashes.
|
||||
*
|
||||
* @param file the file
|
||||
* @return a ZIP entry
|
||||
*/
|
||||
private ZipEntry getEntry(String file) {
|
||||
ZipEntry entry = zip.getEntry(file);
|
||||
if (entry != null) {
|
||||
return entry;
|
||||
}
|
||||
return zip.getEntry(file.replace("/", "\\"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
zip.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
for (Enumeration<? extends ZipEntry> e = zip.entries(); e.hasMoreElements(); ) {
|
||||
|
||||
ZipEntry testEntry = e.nextElement();
|
||||
|
||||
if (testEntry.getName().matches(".*\\.mcr$") || testEntry.getName().matches(".*\\.mca$")) { // TODO: does this need a separate class?
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user