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:
sk89q
2014-11-14 11:27:39 -08:00
parent 44559cde68
commit 7192780251
714 changed files with 333 additions and 834 deletions

View File

@ -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);
}
}
}

View File

@ -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();
}
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -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();
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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();
}
}
}
}

View File

@ -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);
}

View File

@ -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());
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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 {
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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);
}

View 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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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();
}

View File

@ -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();
}
}

View File

@ -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
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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));
}
}

View File

@ -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
}
}

View File

@ -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;
}
}

View File

@ -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
}
}

View File

@ -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;
}
}