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,136 @@
/*
* 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.extent;
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.entity.BaseEntity;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.function.operation.OperationQueue;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.world.biome.BaseBiome;
import javax.annotation.Nullable;
import java.util.List;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* A base class for {@link Extent}s that merely passes extents onto another.
*/
public abstract class AbstractDelegateExtent implements Extent {
private final Extent extent;
/**
* Create a new instance.
*
* @param extent the extent
*/
protected AbstractDelegateExtent(Extent extent) {
checkNotNull(extent);
this.extent = extent;
}
/**
* Get the extent.
*
* @return the extent
*/
public Extent getExtent() {
return extent;
}
@Override
public BaseBlock getBlock(Vector position) {
return extent.getBlock(position);
}
@Override
public BaseBlock getLazyBlock(Vector position) {
return extent.getLazyBlock(position);
}
@Override
public boolean setBlock(Vector location, BaseBlock block) throws WorldEditException {
return extent.setBlock(location, block);
}
@Override
@Nullable
public Entity createEntity(Location location, BaseEntity entity) {
return extent.createEntity(location, entity);
}
@Override
public List<? extends Entity> getEntities() {
return extent.getEntities();
}
@Override
public List<? extends Entity> getEntities(Region region) {
return extent.getEntities(region);
}
@Override
public BaseBiome getBiome(Vector2D position) {
return extent.getBiome(position);
}
@Override
public boolean setBiome(Vector2D position, BaseBiome biome) {
return extent.setBiome(position, biome);
}
@Override
public Vector getMinimumPoint() {
return extent.getMinimumPoint();
}
@Override
public Vector getMaximumPoint() {
return extent.getMaximumPoint();
}
protected Operation commitBefore() {
return null;
}
@Override
public final @Nullable Operation commit() {
Operation ours = commitBefore();
Operation other = extent.commit();
if (ours != null && other != null) {
return new OperationQueue(ours, other);
} else if (ours != null) {
return ours;
} else if (other != null) {
return other;
} else {
return null;
}
}
}

View File

@ -0,0 +1,134 @@
/*
* 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.extent;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.history.change.BlockChange;
import com.sk89q.worldedit.history.change.EntityCreate;
import com.sk89q.worldedit.history.change.EntityRemove;
import com.sk89q.worldedit.history.changeset.ChangeSet;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Location;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Stores changes to a {@link ChangeSet}.
*/
public class ChangeSetExtent extends AbstractDelegateExtent {
private final ChangeSet changeSet;
/**
* Create a new instance.
*
* @param extent the extent
* @param changeSet the change set
*/
public ChangeSetExtent(Extent extent, ChangeSet changeSet) {
super(extent);
checkNotNull(changeSet);
this.changeSet = changeSet;
}
@Override
public boolean setBlock(Vector location, BaseBlock block) throws WorldEditException {
BaseBlock previous = getBlock(location);
changeSet.add(new BlockChange(location.toBlockVector(), previous, block));
return super.setBlock(location, block);
}
@Nullable
@Override
public Entity createEntity(Location location, BaseEntity state) {
Entity entity = super.createEntity(location, state);
if (state != null) {
changeSet.add(new EntityCreate(location, state, entity));
}
return entity;
}
@Override
public List<? extends Entity> getEntities() {
return wrapEntities(super.getEntities());
}
@Override
public List<? extends Entity> getEntities(Region region) {
return wrapEntities(super.getEntities(region));
}
private List<? extends Entity> wrapEntities(List<? extends Entity> entities) {
List<Entity> newList = new ArrayList<Entity>(entities.size());
for (Entity entity : entities) {
newList.add(new TrackedEntity(entity));
}
return newList;
}
private class TrackedEntity implements Entity {
private final Entity entity;
private TrackedEntity(Entity entity) {
this.entity = entity;
}
@Override
public BaseEntity getState() {
return entity.getState();
}
@Override
public Location getLocation() {
return entity.getLocation();
}
@Override
public Extent getExtent() {
return entity.getExtent();
}
@Override
public boolean remove() {
Location location = entity.getLocation();
BaseEntity state = entity.getState();
boolean success = entity.remove();
if (state != null && success) {
changeSet.add(new EntityRemove(location, state));
}
return success;
}
@Nullable
@Override
public <T> T getFacet(Class<? extends T> cls) {
return entity.getFacet(cls);
}
}
}

View File

@ -0,0 +1,92 @@
/*
* 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.extent;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.regions.Region;
import javax.annotation.Nullable;
import java.util.List;
/**
* A world, portion of a world, clipboard, or other object that can have blocks
* set or entities placed.
*
* @see InputExtent the get____() portion
* @see OutputExtent the set____() portion
*/
public interface Extent extends InputExtent, OutputExtent {
/**
* Get the minimum point in the extent.
*
* <p>If the extent is unbounded, then a large (negative) value may
* be returned.</p>
*
* @return the minimum point
*/
Vector getMinimumPoint();
/**
* Get the maximum point in the extent.
*
* <p>If the extent is unbounded, then a large (positive) value may
* be returned.</p>
*
* @return the maximum point
*/
Vector getMaximumPoint();
/**
* Get a list of all entities within the given region.
*
* <p>If the extent is not wholly loaded (i.e. a world being simulated in the
* game will not have every chunk loaded), then this list may not be
* incomplete.</p>
*
* @param region the region in which entities must be contained
* @return a list of entities
*/
List<? extends Entity> getEntities(Region region);
/**
* Get a list of all entities.
*
* <p>If the extent is not wholly loaded (i.e. a world being simulated in the
* game will not have every chunk loaded), then this list may not be
* incomplete.</p>
*
* @return a list of entities
*/
List<? extends Entity> getEntities();
/**
* Create an entity at the given location.
*
* @param entity the entity
* @param location the location
* @return a reference to the created entity, or null if the entity could not be created
*/
@Nullable Entity createEntity(Location location, BaseEntity entity);
}

View File

@ -0,0 +1,89 @@
/*
* 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.extent;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.Vector2D;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.world.biome.BaseBiome;
/**
* Provides the current state of blocks, entities, and so on.
*/
public interface InputExtent {
/**
* Get a snapshot of the block at the given location.
*
* <p>If the given position is out of the bounds of the extent, then the behavior
* is undefined (an air block could be returned). However, {@code null}
* should <strong>not</strong> be returned.</p>
*
* <p>The returned block is mutable and is a snapshot of the block at the time
* of call. It has no position attached to it, so it could be reused in
* {@link Pattern}s and so on.</p>
*
* <p>Calls to this method can actually be quite expensive, so cache results
* whenever it is possible, while being aware of the mutability aspect.
* The cost, however, depends on the implementation and particular extent.
* If only basic information about the block is required, then use of
* {@link #getLazyBlock(Vector)} is recommended.</p>
*
* @param position position of the block
* @return the block
*/
BaseBlock getBlock(Vector position);
/**
* Get a lazy, immutable snapshot of the block at the given location that only
* immediately contains information about the block's type (and metadata).
*
* <p>Further information (such as NBT data) will be available <strong>by the
* time of access</strong>. Therefore, it is not recommended that
* this method is used if the world is being simulated at the time of
* call. If the block needs to be stored for future use, then this method should
* definitely not be used. Moreover, the block that is returned is immutable (or
* should be), and therefore modifications should not be attempted on it. If a
* modifiable copy is required, then the block should be cloned.</p>
*
* <p>This method exists because it is sometimes important to inspect the block
* at a given location, but {@link #getBlock(Vector)} may be too expensive in
* the underlying implementation. It is also not possible to implement
* caching if the returned object is mutable, so this methods allows caching
* implementations to be used.</p>
*
* @param position position of the block
* @return the block
*/
BaseBlock getLazyBlock(Vector position);
/**
* Get the biome at the given location.
*
* <p>If there is no biome available, then the ocean biome should be
* returned.</p>
*
* @param position the (x, z) location to check the biome at
* @return the biome at the location
*/
BaseBiome getBiome(Vector2D position);
}

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.extent;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.function.mask.Mask;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Requires that all mutating methods pass a given {@link Mask}.
*/
public class MaskingExtent extends AbstractDelegateExtent {
private Mask mask;
/**
* Create a new instance.
*
* @param extent the extent
* @param mask the mask
*/
public MaskingExtent(Extent extent, Mask mask) {
super(extent);
checkNotNull(mask);
this.mask = mask;
}
/**
* Get the mask.
*
* @return the mask
*/
public Mask getMask() {
return mask;
}
/**
* Set a mask.
*
* @param mask a mask
*/
public void setMask(Mask mask) {
checkNotNull(mask);
this.mask = mask;
}
@Override
public boolean setBlock(Vector location, BaseBlock block) throws WorldEditException {
return mask.test(location) && super.setBlock(location, block);
}
}

View File

@ -0,0 +1,103 @@
/*
* 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.extent;
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.entity.BaseEntity;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.world.biome.BaseBiome;
import javax.annotation.Nullable;
import java.util.Collections;
import java.util.List;
/**
* An extent that returns air blocks for all blocks and does not
* pass on any changes.
*/
public class NullExtent implements Extent {
private final Vector nullPoint = new Vector(0, 0, 0);
@Override
public Vector getMinimumPoint() {
return nullPoint;
}
@Override
public Vector getMaximumPoint() {
return nullPoint;
}
@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;
}
@Override
public BaseBlock getBlock(Vector position) {
return new BaseBlock(0);
}
@Override
public BaseBlock getLazyBlock(Vector position) {
return new BaseBlock(0);
}
@Nullable
@Override
public BaseBiome getBiome(Vector2D position) {
return null;
}
@Override
public boolean setBlock(Vector position, BaseBlock block) throws WorldEditException {
return false;
}
@Override
public boolean setBiome(Vector2D position, BaseBiome biome) {
return false;
}
@Nullable
@Override
public Operation commit() {
return null;
}
}

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.extent;
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.function.operation.Operation;
import com.sk89q.worldedit.world.biome.BaseBiome;
import javax.annotation.Nullable;
/**
* Accepts block and entity changes.
*/
public interface OutputExtent {
/**
* Change the block at the given location to the given block. The operation may
* not tie the given {@link BaseBlock} to the world, so future changes to the
* {@link BaseBlock} do not affect the world until this method is called again.
*
* <p>The return value of this method indicates whether the change was probably
* successful. It may not be successful if, for example, the location is out
* of the bounds of the extent. It may be unsuccessful if the block passed
* is the same as the one in the world. However, the return value is only an
* estimation and it may be incorrect, but it could be used to count, for
* example, the approximate number of changes.</p>
*
* @param position position of the block
* @param block block to set
* @return true if the block was successfully set (return value may not be accurate)
* @throws WorldEditException thrown on an error
*/
boolean setBlock(Vector position, BaseBlock block) throws WorldEditException;
/**
* Set the biome.
*
* @param position the (x, z) location to set the biome at
* @param biome the biome to set to
* @return true if the biome was successfully set (return value may not be accurate)
*/
boolean setBiome(Vector2D position, BaseBiome biome);
/**
* Return an {@link Operation} that should be called to tie up loose ends
* (such as to commit changes in a buffer).
*
* @return an operation or null if there is none to execute
*/
@Nullable Operation commit();
}

View File

@ -0,0 +1,154 @@
/*
* 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.extent.buffer;
import com.sk89q.worldedit.BlockVector;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.blocks.BlockID;
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.mask.Masks;
import com.sk89q.worldedit.function.pattern.Pattern;
import com.sk89q.worldedit.regions.AbstractRegion;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.regions.RegionOperationException;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Buffers changes to an {@link Extent} and allows later retrieval for
* actual application of the changes.
*
* <p>This buffer will not attempt to return results from the buffer when
* accessor methods (such as {@link #getBlock(Vector)}) are called.</p>
*/
public class ForgetfulExtentBuffer extends AbstractDelegateExtent implements Pattern {
private static final BaseBlock AIR = new BaseBlock(BlockID.AIR);
private final Map<BlockVector, BaseBlock> buffer = new LinkedHashMap<BlockVector, BaseBlock>();
private final Mask mask;
private Vector min = null;
private Vector max = null;
/**
* Create a new extent buffer that will buffer every change.
*
* @param delegate the delegate extent for {@link Extent#getBlock(Vector)}, etc. calls
*/
public ForgetfulExtentBuffer(Extent delegate) {
this(delegate, Masks.alwaysTrue());
}
/**
* Create a new extent buffer that will buffer changes that meet the criteria
* of the given mask.
*
* @param delegate the delegate extent for {@link Extent#getBlock(Vector)}, etc. calls
* @param mask the mask
*/
public ForgetfulExtentBuffer(Extent delegate, Mask mask) {
super(delegate);
checkNotNull(delegate);
checkNotNull(mask);
this.mask = mask;
}
@Override
public boolean setBlock(Vector location, BaseBlock block) throws WorldEditException {
// Update minimum
if (min == null) {
min = location;
} else {
min = Vector.getMinimum(min, location);
}
// Update maximum
if (max == null) {
max = location;
} else {
max = Vector.getMaximum(max, location);
}
BlockVector blockVector = location.toBlockVector();
if (mask.test(blockVector)) {
buffer.put(blockVector, block);
return true;
} else {
return getExtent().setBlock(location, block);
}
}
@Override
public BaseBlock apply(Vector pos) {
BaseBlock block = buffer.get(pos.toBlockVector());
if (block != null) {
return block;
} else {
return AIR;
}
}
/**
* Return a region representation of this buffer.
*
* @return a region
*/
public Region asRegion() {
return new AbstractRegion(null) {
@Override
public Vector getMinimumPoint() {
return min != null ? min : new Vector();
}
@Override
public Vector getMaximumPoint() {
return max != null ? max : new Vector();
}
@Override
public void expand(Vector... changes) throws RegionOperationException {
throw new UnsupportedOperationException("Cannot change the size of this region");
}
@Override
public void contract(Vector... changes) throws RegionOperationException {
throw new UnsupportedOperationException("Cannot change the size of this region");
}
@Override
public boolean contains(Vector position) {
return buffer.containsKey(position.toBlockVector());
}
@Override
public Iterator<BlockVector> iterator() {
return buffer.keySet().iterator();
}
};
}
}

View File

@ -0,0 +1,68 @@
/*
* 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.extent.cache;
import com.sk89q.worldedit.BlockVector;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
import com.sk89q.worldedit.extent.Extent;
/**
* Returns the same cached {@link BaseBlock} for repeated calls to
* {@link #getLazyBlock(Vector)} with the same position.
*/
public class LastAccessExtentCache extends AbstractDelegateExtent {
private CachedBlock lastBlock;
/**
* Create a new instance.
*
* @param extent the extent
*/
public LastAccessExtentCache(Extent extent) {
super(extent);
}
@Override
public BaseBlock getLazyBlock(Vector position) {
BlockVector blockVector = position.toBlockVector();
CachedBlock lastBlock = this.lastBlock;
if (lastBlock != null && lastBlock.position.equals(blockVector)) {
return lastBlock.block;
} else {
BaseBlock block = super.getLazyBlock(position);
this.lastBlock = new CachedBlock(blockVector, block);
return block;
}
}
private static class CachedBlock {
private final BlockVector position;
private final BaseBlock block;
private CachedBlock(BlockVector position, BaseBlock block) {
this.position = position;
this.block = 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.extent.clipboard;
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.BlockID;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.world.biome.BaseBiome;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Stores block data as a multi-dimensional array of {@link BaseBlock}s and
* other data as lists or maps.
*/
public class BlockArrayClipboard implements Clipboard {
private final Region region;
private Vector origin = new Vector();
private final BaseBlock[][][] blocks;
private final List<ClipboardEntity> entities = new ArrayList<ClipboardEntity>();
/**
* Create a new instance.
*
* <p>The origin will be placed at the region's lowest minimum point.</p>
*
* @param region the bounding region
*/
public BlockArrayClipboard(Region region) {
checkNotNull(region);
this.region = region.clone();
this.origin = region.getMinimumPoint();
Vector dimensions = getDimensions();
blocks = new BaseBlock[dimensions.getBlockX()][dimensions.getBlockY()][dimensions.getBlockZ()];
}
@Override
public Region getRegion() {
return region.clone();
}
@Override
public Vector getOrigin() {
return origin;
}
@Override
public void setOrigin(Vector origin) {
this.origin = origin;
}
@Override
public Vector getDimensions() {
return region.getMaximumPoint().subtract(region.getMinimumPoint()).add(1, 1, 1);
}
@Override
public Vector getMinimumPoint() {
return region.getMinimumPoint();
}
@Override
public Vector getMaximumPoint() {
return region.getMaximumPoint();
}
@Override
public List<? extends Entity> getEntities(Region region) {
List<Entity> filtered = new ArrayList<Entity>();
for (Entity entity : entities) {
if (region.contains(entity.getLocation().toVector())) {
filtered.add(entity);
}
}
return Collections.unmodifiableList(filtered);
}
@Override
public List<? extends Entity> getEntities() {
return Collections.unmodifiableList(entities);
}
@Nullable
@Override
public Entity createEntity(Location location, BaseEntity entity) {
ClipboardEntity ret = new ClipboardEntity(location, entity);
entities.add(ret);
return ret;
}
@Override
public BaseBlock getBlock(Vector position) {
if (region.contains(position)) {
Vector v = position.subtract(region.getMinimumPoint());
BaseBlock block = blocks[v.getBlockX()][v.getBlockY()][v.getBlockZ()];
if (block != null) {
return new BaseBlock(block);
}
}
return new BaseBlock(BlockID.AIR);
}
@Override
public BaseBlock getLazyBlock(Vector position) {
return getBlock(position);
}
@Override
public boolean setBlock(Vector position, BaseBlock block) throws WorldEditException {
if (region.contains(position)) {
Vector v = position.subtract(region.getMinimumPoint());
blocks[v.getBlockX()][v.getBlockY()][v.getBlockZ()] = new BaseBlock(block);
return true;
} else {
return false;
}
}
@Override
public BaseBiome getBiome(Vector2D position) {
return new BaseBiome(0);
}
@Override
public boolean setBiome(Vector2D position, BaseBiome biome) {
return false;
}
@Nullable
@Override
public Operation commit() {
return null;
}
/**
* Stores entity data.
*/
private class ClipboardEntity extends StoredEntity {
ClipboardEntity(Location location, BaseEntity entity) {
super(location, entity);
}
@Override
public boolean remove() {
return entities.remove(this);
}
@Nullable
@Override
public <T> T getFacet(Class<? extends T> cls) {
return null;
}
}
}

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.extent.clipboard;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.regions.Region;
/**
* Specifies an object that implements something suitable as a "clipboard."
*/
public interface Clipboard extends Extent {
/**
* Get the bounding region of this extent.
*
* <p>Implementations should return a copy of the region.</p>
*
* @return the bounding region
*/
Region getRegion();
/**
* Get the dimensions of the copy, which is at minimum (1, 1, 1).
*
* @return the dimensions
*/
Vector getDimensions();
/**
* Get the origin point from which the copy was made from.
*
* @return the origin
*/
Vector getOrigin();
/**
* Set the origin point from which the copy was made from.
*
* @param origin the origin
*/
void setOrigin(Vector origin);
}

View File

@ -0,0 +1,76 @@
/*
* 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.extent.clipboard;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.util.Location;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* An implementation of {@link Entity} that stores a {@link BaseEntity} with it.
*
* <p>Calls to {@link #getState()} return a clone.</p>
*/
abstract class StoredEntity implements Entity {
private final Location location;
private final BaseEntity entity;
/**
* Create a new instance.
*
* @param location the location
* @param entity the entity (which will be copied)
*/
StoredEntity(Location location, BaseEntity entity) {
checkNotNull(location);
checkNotNull(entity);
this.location = location;
this.entity = new BaseEntity(entity);
}
/**
* Get the entity state. This is not a copy.
*
* @return the entity
*/
BaseEntity getEntity() {
return entity;
}
@Override
public BaseEntity getState() {
return new BaseEntity(entity);
}
@Override
public Location getLocation() {
return location;
}
@Override
public Extent getExtent() {
return location.getExtent();
}
}

View File

@ -0,0 +1,178 @@
/*
* 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.extent.clipboard.io;
import com.sk89q.jnbt.NBTConstants;
import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.jnbt.NBTOutputStream;
import javax.annotation.Nullable;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* A collection of supported clipboard formats.
*/
public enum ClipboardFormat {
/**
* The Schematic format used by many software.
*/
SCHEMATIC("mcedit", "mce", "schematic") {
@Override
public ClipboardReader getReader(InputStream inputStream) throws IOException {
NBTInputStream nbtStream = new NBTInputStream(new GZIPInputStream(inputStream));
return new SchematicReader(nbtStream);
}
@Override
public ClipboardWriter getWriter(OutputStream outputStream) throws IOException {
NBTOutputStream nbtStream = new NBTOutputStream(new GZIPOutputStream(outputStream));
return new SchematicWriter(nbtStream);
}
@Override
public boolean isFormat(File file) {
DataInputStream str = null;
try {
str = new DataInputStream(new GZIPInputStream(new FileInputStream(file)));
if ((str.readByte() & 0xFF) != NBTConstants.TYPE_COMPOUND) {
return false;
}
byte[] nameBytes = new byte[str.readShort() & 0xFFFF];
str.readFully(nameBytes);
String name = new String(nameBytes, NBTConstants.CHARSET);
return name.equals("Schematic");
} catch (IOException e) {
return false;
} finally {
if (str != null) {
try {
str.close();
} catch (IOException ignored) {
}
}
}
}
};
private static final Map<String, ClipboardFormat> aliasMap = new HashMap<String, ClipboardFormat>();
private final String[] aliases;
/**
* Create a new instance.
*
* @param aliases an array of aliases by which this format may be referred to
*/
private ClipboardFormat(String ... aliases) {
this.aliases = aliases;
}
/**
* Get a set of aliases.
*
* @return a set of aliases
*/
public Set<String> getAliases() {
return Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(aliases)));
}
/**
* Create a reader.
*
* @param inputStream the input stream
* @return a reader
* @throws IOException thrown on I/O error
*/
public abstract ClipboardReader getReader(InputStream inputStream) throws IOException;
/**
* Create a writer.
*
* @param outputStream the output stream
* @return a writer
* @throws IOException thrown on I/O error
*/
public abstract ClipboardWriter getWriter(OutputStream outputStream) throws IOException;
/**
* Return whether the given file is of this format.
*
* @param file the file
* @return true if the given file is of this format
*/
public abstract boolean isFormat(File file);
static {
for (ClipboardFormat format : EnumSet.allOf(ClipboardFormat.class)) {
for (String key : format.aliases) {
aliasMap.put(key, format);
}
}
}
/**
* Find the clipboard format named by the given alias.
*
* @param alias the alias
* @return the format, otherwise null if none is matched
*/
@Nullable
public static ClipboardFormat findByAlias(String alias) {
checkNotNull(alias);
return aliasMap.get(alias.toLowerCase().trim());
}
/**
* Detect the format given a file.
*
* @param file the file
* @return the format, otherwise null if one cannot be detected
*/
@Nullable
public static ClipboardFormat findByFile(File file) {
checkNotNull(file);
for (ClipboardFormat format : EnumSet.allOf(ClipboardFormat.class)) {
if (format.isFormat(file)) {
return format;
}
}
return null;
}
}

View File

@ -0,0 +1,43 @@
/*
* 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.extent.clipboard.io;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.world.registry.WorldData;
import java.io.IOException;
/**
* Reads {@code Clipboard}s.
*
* @see Clipboard
*/
public interface ClipboardReader {
/**
* Read a {@code Clipboard}.
*
* @param data the world data space to convert the blocks to
* @return the read clipboard
* @throws IOException thrown on I/O error
*/
Clipboard read(WorldData data) throws IOException;
}

View File

@ -0,0 +1,44 @@
/*
* 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.extent.clipboard.io;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.world.registry.WorldData;
import java.io.Closeable;
import java.io.IOException;
/**
* Writes {@code Clipboard}s.
*
* @see Clipboard
*/
public interface ClipboardWriter extends Closeable {
/**
* Writes a clipboard.
*
* @param clipboard the clipboard
* @param data the world data instance
* @throws IOException thrown on I/O error
*/
void write(Clipboard clipboard, WorldData data) throws IOException;
}

View File

@ -0,0 +1,276 @@
/*
* 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.extent.clipboard.io;
import com.sk89q.jnbt.ByteArrayTag;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.IntTag;
import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.jnbt.ShortTag;
import com.sk89q.jnbt.StringTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.BlockVector;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.world.registry.WorldData;
import com.sk89q.worldedit.world.storage.NBTConversions;
import javax.annotation.Nullable;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Reads schematic files based that are compatible with MCEdit and other editors.
*/
public class SchematicReader implements ClipboardReader {
private static final Logger log = Logger.getLogger(SchematicReader.class.getCanonicalName());
private final NBTInputStream inputStream;
/**
* Create a new instance.
*
* @param inputStream the input stream to read from
*/
public SchematicReader(NBTInputStream inputStream) {
checkNotNull(inputStream);
this.inputStream = inputStream;
}
@Override
public Clipboard read(WorldData data) throws IOException {
// Schematic tag
CompoundTag schematicTag = (CompoundTag) inputStream.readTag();
if (!schematicTag.getName().equals("Schematic")) {
throw new IOException("Tag 'Schematic' does not exist or is not first");
}
// Check
Map<String, Tag> schematic = schematicTag.getValue();
if (!schematic.containsKey("Blocks")) {
throw new IOException("Schematic file is missing a 'Blocks' tag");
}
// Check type of Schematic
String materials = requireTag(schematic, "Materials", StringTag.class).getValue();
if (!materials.equals("Alpha")) {
throw new IOException("Schematic file is not an Alpha schematic");
}
// ====================================================================
// Metadata
// ====================================================================
Vector origin;
Region region;
// Get information
short width = requireTag(schematic, "Width", ShortTag.class).getValue();
short height = requireTag(schematic, "Height", ShortTag.class).getValue();
short length = requireTag(schematic, "Length", ShortTag.class).getValue();
try {
int originX = requireTag(schematic, "WEOriginX", IntTag.class).getValue();
int originY = requireTag(schematic, "WEOriginY", IntTag.class).getValue();
int originZ = requireTag(schematic, "WEOriginZ", IntTag.class).getValue();
Vector min = new Vector(originX, originY, originZ);
int offsetX = requireTag(schematic, "WEOffsetX", IntTag.class).getValue();
int offsetY = requireTag(schematic, "WEOffsetY", IntTag.class).getValue();
int offsetZ = requireTag(schematic, "WEOffsetZ", IntTag.class).getValue();
Vector offset = new Vector(offsetX, offsetY, offsetZ);
origin = min.subtract(offset);
region = new CuboidRegion(min, min.add(width, height, length).subtract(Vector.ONE));
} catch (IOException ignored) {
origin = new Vector(0, 0, 0);
region = new CuboidRegion(origin, origin.add(width, height, length).subtract(Vector.ONE));
}
// ====================================================================
// Blocks
// ====================================================================
// Get blocks
byte[] blockId = requireTag(schematic, "Blocks", ByteArrayTag.class).getValue();
byte[] blockData = requireTag(schematic, "Data", ByteArrayTag.class).getValue();
byte[] addId = new byte[0];
short[] blocks = new short[blockId.length]; // Have to later combine IDs
// We support 4096 block IDs using the same method as vanilla Minecraft, where
// the highest 4 bits are stored in a separate byte array.
if (schematic.containsKey("AddBlocks")) {
addId = requireTag(schematic, "AddBlocks", ByteArrayTag.class).getValue();
}
// Combine the AddBlocks data with the first 8-bit block ID
for (int index = 0; index < blockId.length; index++) {
if ((index >> 1) >= addId.length) { // No corresponding AddBlocks index
blocks[index] = (short) (blockId[index] & 0xFF);
} else {
if ((index & 1) == 0) {
blocks[index] = (short) (((addId[index >> 1] & 0x0F) << 8) + (blockId[index] & 0xFF));
} else {
blocks[index] = (short) (((addId[index >> 1] & 0xF0) << 4) + (blockId[index] & 0xFF));
}
}
}
// Need to pull out tile entities
List<Tag> tileEntities = requireTag(schematic, "TileEntities", ListTag.class).getValue();
Map<BlockVector, Map<String, Tag>> tileEntitiesMap = new HashMap<BlockVector, Map<String, Tag>>();
for (Tag tag : tileEntities) {
if (!(tag instanceof CompoundTag)) continue;
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);
tileEntitiesMap.put(vec, values);
}
BlockArrayClipboard clipboard = new BlockArrayClipboard(region);
clipboard.setOrigin(origin);
// Don't log a torrent of errors
int failedBlockSets = 0;
for (int x = 0; x < width; ++x) {
for (int y = 0; y < height; ++y) {
for (int z = 0; z < length; ++z) {
int index = y * width * length + z * width + x;
BlockVector pt = new BlockVector(x, y, z);
BaseBlock block = new BaseBlock(blocks[index], blockData[index]);
if (tileEntitiesMap.containsKey(pt)) {
block.setNbtData(new CompoundTag("", tileEntitiesMap.get(pt)));
}
try {
clipboard.setBlock(region.getMinimumPoint().add(pt), block);
} catch (WorldEditException e) {
switch (failedBlockSets) {
case 0:
log.log(Level.WARNING, "Failed to set block on a Clipboard", e);
break;
case 1:
log.log(Level.WARNING, "Failed to set block on a Clipboard (again) -- no more messages will be logged", e);
break;
default:
}
failedBlockSets++;
}
}
}
}
// ====================================================================
// Entities
// ====================================================================
try {
List<Tag> entityTags = requireTag(schematic, "Entities", ListTag.class).getValue();
for (Tag tag : entityTags) {
if (tag instanceof CompoundTag) {
CompoundTag compound = (CompoundTag) tag;
String id = compound.getString("id");
Location location = NBTConversions.toLocation(clipboard, compound.getListTag("Pos"), compound.getListTag("Rotation"));
if (!id.isEmpty()) {
BaseEntity state = new BaseEntity(id, compound);
clipboard.createEntity(location, state);
}
}
}
} catch (IOException ignored) { // No entities? No problem
}
return clipboard;
}
private static <T extends Tag> T requireTag(Map<String, Tag> items, String key, Class<T> expected) throws IOException {
if (!items.containsKey(key)) {
throw new IOException("Schematic file is missing a \"" + key + "\" tag");
}
Tag tag = items.get(key);
if (!expected.isInstance(tag)) {
throw new IOException(key + " tag is not of tag type " + expected.getName());
}
return expected.cast(tag);
}
@Nullable
private static <T extends Tag> T getTag(CompoundTag tag, Class<T> expected, String key) {
Map<String, Tag> items = tag.getValue();
if (!items.containsKey(key)) {
return null;
}
Tag test = items.get(key);
if (!expected.isInstance(test)) {
return null;
}
return expected.cast(test);
}
}

View File

@ -0,0 +1,218 @@
/*
* 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.extent.clipboard.io;
import com.sk89q.jnbt.ByteArrayTag;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.DoubleTag;
import com.sk89q.jnbt.FloatTag;
import com.sk89q.jnbt.IntTag;
import com.sk89q.jnbt.ListTag;
import com.sk89q.jnbt.NBTOutputStream;
import com.sk89q.jnbt.ShortTag;
import com.sk89q.jnbt.StringTag;
import com.sk89q.jnbt.Tag;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.world.registry.WorldData;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Writes schematic files based that are compatible with MCEdit and other editors.
*/
public class SchematicWriter implements ClipboardWriter {
private static final int MAX_SIZE = Short.MAX_VALUE - Short.MIN_VALUE;
private final NBTOutputStream outputStream;
/**
* Create a new schematic writer.
*
* @param outputStream the output stream to write to
*/
public SchematicWriter(NBTOutputStream outputStream) {
checkNotNull(outputStream);
this.outputStream = outputStream;
}
@Override
public void write(Clipboard clipboard, WorldData data) throws IOException {
Region region = clipboard.getRegion();
Vector origin = clipboard.getOrigin();
Vector min = region.getMinimumPoint();
Vector offset = min.subtract(origin);
int width = region.getWidth();
int height = region.getHeight();
int length = region.getLength();
if (width > MAX_SIZE) {
throw new IllegalArgumentException("Width of region too large for a .schematic");
}
if (height > MAX_SIZE) {
throw new IllegalArgumentException("Height of region too large for a .schematic");
}
if (length > MAX_SIZE) {
throw new IllegalArgumentException("Length of region too large for a .schematic");
}
// ====================================================================
// Metadata
// ====================================================================
HashMap<String, Tag> schematic = new HashMap<String, Tag>();
schematic.put("Width", new ShortTag("Width", (short) width));
schematic.put("Length", new ShortTag("Length", (short) length));
schematic.put("Height", new ShortTag("Height", (short) height));
schematic.put("Materials", new StringTag("Materials", "Alpha"));
schematic.put("WEOriginX", new IntTag("WEOriginX", min.getBlockX()));
schematic.put("WEOriginY", new IntTag("WEOriginY", min.getBlockY()));
schematic.put("WEOriginZ", new IntTag("WEOriginZ", min.getBlockZ()));
schematic.put("WEOffsetX", new IntTag("WEOffsetX", offset.getBlockX()));
schematic.put("WEOffsetY", new IntTag("WEOffsetY", offset.getBlockY()));
schematic.put("WEOffsetZ", new IntTag("WEOffsetZ", offset.getBlockZ()));
// ====================================================================
// Block handling
// ====================================================================
byte[] blocks = new byte[width * height * length];
byte[] addBlocks = null;
byte[] blockData = new byte[width * height * length];
List<Tag> tileEntities = new ArrayList<Tag>();
for (Vector point : region) {
Vector relative = point.subtract(min);
int x = relative.getBlockX();
int y = relative.getBlockY();
int z = relative.getBlockZ();
int index = y * width * length + z * width + x;
BaseBlock block = clipboard.getBlock(point);
// Save 4096 IDs in an AddBlocks section
if (block.getType() > 255) {
if (addBlocks == null) { // Lazily create section
addBlocks = new byte[(blocks.length >> 1) + 1];
}
addBlocks[index >> 1] = (byte) (((index & 1) == 0) ?
addBlocks[index >> 1] & 0xF0 | (block.getType() >> 8) & 0xF
: addBlocks[index >> 1] & 0xF | ((block.getType() >> 8) & 0xF) << 4);
}
blocks[index] = (byte) block.getType();
blockData[index] = (byte) block.getData();
// Store TileEntity data
CompoundTag rawTag = block.getNbtData();
if (rawTag != null) {
Map<String, Tag> values = new HashMap<String, Tag>();
for (Entry<String, Tag> entry : rawTag.getValue().entrySet()) {
values.put(entry.getKey(), entry.getValue());
}
values.put("id", new StringTag("id", block.getNbtId()));
values.put("x", new IntTag("x", x));
values.put("y", new IntTag("y", y));
values.put("z", new IntTag("z", z));
CompoundTag tileEntityTag = new CompoundTag("TileEntity", values);
tileEntities.add(tileEntityTag);
}
}
schematic.put("Blocks", new ByteArrayTag("Blocks", blocks));
schematic.put("Data", new ByteArrayTag("Data", blockData));
schematic.put("TileEntities", new ListTag("TileEntities", CompoundTag.class, tileEntities));
if (addBlocks != null) {
schematic.put("AddBlocks", new ByteArrayTag("AddBlocks", addBlocks));
}
// ====================================================================
// Entities
// ====================================================================
List<Tag> entities = new ArrayList<Tag>();
for (Entity entity : clipboard.getEntities()) {
BaseEntity state = entity.getState();
if (state != null) {
Map<String, Tag> values = new HashMap<String, Tag>();
// Put NBT provided data
CompoundTag rawTag = state.getNbtData();
if (rawTag != null) {
values.putAll(rawTag.getValue());
}
// Store our location data, overwriting any
values.put("id", new StringTag("id", state.getTypeId()));
values.put("Pos", writeVector(entity.getLocation().toVector(), "Pos"));
values.put("Rotation", writeRotation(entity.getLocation(), "Rotation"));
CompoundTag entityTag = new CompoundTag("Entity", values);
entities.add(entityTag);
}
}
schematic.put("Entities", new ListTag("Entities", CompoundTag.class, entities));
// ====================================================================
// Output
// ====================================================================
CompoundTag schematicTag = new CompoundTag("Schematic", schematic);
outputStream.writeTag(schematicTag);
}
private Tag writeVector(Vector vector, String name) {
List<DoubleTag> list = new ArrayList<DoubleTag>();
list.add(new DoubleTag("", vector.getX()));
list.add(new DoubleTag("", vector.getY()));
list.add(new DoubleTag("", vector.getZ()));
return new ListTag(name, DoubleTag.class, list);
}
private Tag writeRotation(Location location, String name) {
List<FloatTag> list = new ArrayList<FloatTag>();
list.add(new FloatTag("", location.getYaw()));
list.add(new FloatTag("", location.getPitch()));
return new ListTag(name, FloatTag.class, list);
}
@Override
public void close() throws IOException {
outputStream.close();
}
}

View File

@ -0,0 +1,198 @@
/*
* 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.extent.inventory;
import com.sk89q.worldedit.WorldVector;
import com.sk89q.worldedit.blocks.*;
/**
* Represents a source to get blocks from and store removed ones.
*/
public abstract class BlockBag {
/**
* Stores a block as if it was mined.
*
* @param id the type ID
* @throws BlockBagException on error
* @deprecated Use {@link BlockBag#storeDroppedBlock(int, int)} instead
*/
@Deprecated
public void storeDroppedBlock(int id) throws BlockBagException {
storeDroppedBlock(id, 0);
}
/**
* Stores a block as if it was mined.
*
* @param id the type ID
* @param data the data value
* @throws BlockBagException on error
*/
public void storeDroppedBlock(int id, int data) throws BlockBagException {
BaseItem dropped = BlockType.getBlockBagItem(id, data);
if (dropped == null) return;
if (dropped.getType() == BlockID.AIR) return;
storeItem(dropped);
}
/**
* Sets a block as if it was placed by hand.
*
* @param id the type ID
* @throws BlockBagException on error
* @deprecated Use {@link #fetchPlacedBlock(int,int)} instead
*/
@Deprecated
public void fetchPlacedBlock(int id) throws BlockBagException {
fetchPlacedBlock(id, 0);
}
/**
* Sets a block as if it was placed by hand.
*
* @param id the type ID
* @param data the data value
* @throws BlockBagException on error
*/
public void fetchPlacedBlock(int id, int data) throws BlockBagException {
try {
// Blocks that can't be fetched...
switch (id) {
case BlockID.BEDROCK:
case BlockID.GOLD_ORE:
case BlockID.IRON_ORE:
case BlockID.COAL_ORE:
case BlockID.DIAMOND_ORE:
case BlockID.TNT:
case BlockID.MOB_SPAWNER:
case BlockID.CROPS:
case BlockID.REDSTONE_ORE:
case BlockID.GLOWING_REDSTONE_ORE:
case BlockID.SNOW:
case BlockID.LIGHTSTONE:
case BlockID.PORTAL:
throw new UnplaceableBlockException();
case BlockID.WATER:
case BlockID.STATIONARY_WATER:
case BlockID.LAVA:
case BlockID.STATIONARY_LAVA:
// Override liquids
return;
default:
fetchBlock(id);
break;
}
} catch (OutOfBlocksException e) {
BaseItem placed = BlockType.getBlockBagItem(id, data);
if (placed == null) throw e; // TODO: check
if (placed.getType() == BlockID.AIR) throw e; // TODO: check
fetchItem(placed);
}
}
/**
* Get a block.
*
* <p>Either this method or fetchItem needs to be overridden.</p>
*
* @param id the type ID
* @throws BlockBagException on error
*/
public void fetchBlock(int id) throws BlockBagException {
fetchItem(new BaseItem(id));
}
/**
* Get a block.
*
* <p>Either this method or fetchItem needs to be overridden.</p>
*
* @param item the item
* @throws BlockBagException on error
*/
public void fetchItem(BaseItem item) throws BlockBagException {
fetchBlock(item.getType());
}
/**
* Store a block.
*
* <p>Either this method or fetchItem needs to be overridden.</p>
*
* @param id the type ID
* @throws BlockBagException on error
*/
public void storeBlock(int id) throws BlockBagException {
storeItem(new BaseItem(id));
}
/**
* Store a block.
*
* <p>Either this method or fetchItem needs to be overridden.</p>
*
* @param item the item
* @throws BlockBagException on error
*/
public void storeItem(BaseItem item) throws BlockBagException {
storeBlock(item.getType());
}
/**
* Checks to see if a block exists without removing it.
*
* @param id the type ID
* @return whether the block exists
*/
public boolean peekBlock(int id) {
try {
fetchBlock(id);
storeBlock(id);
return true;
} catch (BlockBagException e) {
return false;
}
}
/**
* Flush any changes. This is called at the end.
*/
public abstract void flushChanges();
/**
* Adds a position to be used a source.
*
* @param pos the position
*/
public abstract void addSourcePosition(WorldVector pos);
/**
* Adds a position to be used a source.
*
* @param pos the position
*/
public abstract void addSingleSourcePosition(WorldVector pos);
}

View File

@ -0,0 +1,26 @@
/*
* 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.extent.inventory;
/**
* Thrown when a block bag detects a problem.
*/
public class BlockBagException extends Exception {
}

View File

@ -0,0 +1,113 @@
/*
* 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.extent.inventory;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
import com.sk89q.worldedit.extent.Extent;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
/**
* Applies a {@link BlockBag} to operations.
*/
public class BlockBagExtent extends AbstractDelegateExtent {
private Map<Integer, Integer> missingBlocks = new HashMap<Integer, Integer>();
private BlockBag blockBag;
/**
* Create a new instance.
*
* @param extent the extent
* @param blockBag the block bag
*/
public BlockBagExtent(Extent extent, @Nullable BlockBag blockBag) {
super(extent);
this.blockBag = blockBag;
}
/**
* Get the block bag.
*
* @return a block bag, which may be null if none is used
*/
public @Nullable BlockBag getBlockBag() {
return blockBag;
}
/**
* Set the block bag.
*
* @param blockBag a block bag, which may be null if none is used
*/
public void setBlockBag(@Nullable BlockBag blockBag) {
this.blockBag = blockBag;
}
/**
* Gets the list of missing blocks and clears the list for the next
* operation.
*
* @return a map of missing blocks
*/
public Map<Integer, Integer> popMissing() {
Map<Integer, Integer> missingBlocks = this.missingBlocks;
this.missingBlocks = new HashMap<Integer, Integer>();
return missingBlocks;
}
@Override
public boolean setBlock(Vector position, BaseBlock block) throws WorldEditException {
if (blockBag != null) {
BaseBlock lazyBlock = getExtent().getLazyBlock(position);
int existing = lazyBlock.getType();
final int type = block.getType();
if (type > 0) {
try {
blockBag.fetchPlacedBlock(type, 0);
} catch (UnplaceableBlockException e) {
return false;
} catch (BlockBagException e) {
if (!missingBlocks.containsKey(type)) {
missingBlocks.put(type, 1);
} else {
missingBlocks.put(type, missingBlocks.get(type) + 1);
}
return false;
}
}
if (existing > 0) {
try {
blockBag.storeDroppedBlock(existing, lazyBlock.getData());
} catch (BlockBagException ignored) {
}
}
}
return super.setBlock(position, block);
}
}

View File

@ -0,0 +1,26 @@
/*
* 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.extent.inventory;
/**
* Thrown when there are no more blocks left.
*/
public class OutOfBlocksException extends BlockBagException {
}

View File

@ -0,0 +1,46 @@
/*
* 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.extent.inventory;
/**
* Thrown when the target inventory of a block bag is full.
*/
public class OutOfSpaceException extends BlockBagException {
private int id;
/**
* Construct the object.
*
* @param id the ID of the block
*/
public OutOfSpaceException(int id) {
this.id = id;
}
/**
* Get the ID of the block
*
* @return the id
*/
public int getID() {
return id;
}
}

View File

@ -0,0 +1,26 @@
/*
* 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.extent.inventory;
/**
* Thrown when a block that can't be placed is used.
*/
public class UnplaceableBlockException extends BlockBagException {
}

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.extent.logging;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
import com.sk89q.worldedit.extent.Extent;
/**
* An abstract class to implement block loggers and so on with.
*/
public abstract class AbstractLoggingExtent extends AbstractDelegateExtent {
/**
* Create a new instance.
*
* @param extent the extent
*/
protected AbstractLoggingExtent(Extent extent) {
super(extent);
}
/**
* Called when a block is being changed.
*
* @param position the position
* @param newBlock the new block to replace the old one
*/
protected void onBlockChange(Vector position, BaseBlock newBlock) {
}
@Override
public final boolean setBlock(Vector position, BaseBlock block) throws WorldEditException {
onBlockChange(position, block);
return super.setBlock(position, block);
}
}

View File

@ -0,0 +1,218 @@
/*
* 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.extent.reorder;
import com.google.common.collect.Iterators;
import com.sk89q.worldedit.BlockVector;
import com.sk89q.worldedit.PlayerDirection;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.blocks.BlockID;
import com.sk89q.worldedit.blocks.BlockType;
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.function.operation.BlockMapEntryPlacer;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.function.operation.OperationQueue;
import com.sk89q.worldedit.function.operation.RunContext;
import com.sk89q.worldedit.util.collection.TupleArrayList;
import java.util.*;
/**
* Re-orders blocks into several stages.
*/
public class MultiStageReorder extends AbstractDelegateExtent implements ReorderingExtent {
private TupleArrayList<BlockVector, BaseBlock> stage1 = new TupleArrayList<BlockVector, BaseBlock>();
private TupleArrayList<BlockVector, BaseBlock> stage2 = new TupleArrayList<BlockVector, BaseBlock>();
private TupleArrayList<BlockVector, BaseBlock> stage3 = new TupleArrayList<BlockVector, BaseBlock>();
private boolean enabled;
/**
* Create a new instance.
*
* @param extent the extent
* @param enabled true to enable
*/
public MultiStageReorder(Extent extent, boolean enabled) {
super(extent);
this.enabled = enabled;
}
/**
* Create a new instance when the re-ordering is enabled.
*
* @param extent the extent
*/
public MultiStageReorder(Extent extent) {
this(extent, true);
}
/**
* Return whether re-ordering is enabled.
*
* @return true if re-ordering is enabled
*/
public boolean isEnabled() {
return enabled;
}
/**
* Set whether re-ordering is enabled.
*
* @param enabled true if re-ordering is enabled
*/
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
@Override
public boolean setBlock(Vector location, BaseBlock block) throws WorldEditException {
BaseBlock lazyBlock = getLazyBlock(location);
if (!enabled) {
return super.setBlock(location, block);
}
if (BlockType.shouldPlaceLast(block.getType())) {
// Place torches, etc. last
stage2.put(location.toBlockVector(), block);
return !(lazyBlock.getType() == block.getType() && lazyBlock.getData() == block.getData());
} else if (BlockType.shouldPlaceFinal(block.getType())) {
// Place signs, reed, etc even later
stage3.put(location.toBlockVector(), block);
return !(lazyBlock.getType() == block.getType() && lazyBlock.getData() == block.getData());
} else if (BlockType.shouldPlaceLast(lazyBlock.getType())) {
// Destroy torches, etc. first
super.setBlock(location, new BaseBlock(BlockID.AIR));
return super.setBlock(location, block);
} else {
stage1.put(location.toBlockVector(), block);
return !(lazyBlock.getType() == block.getType() && lazyBlock.getData() == block.getData());
}
}
@Override
public Operation commitBefore() {
return new OperationQueue(
new BlockMapEntryPlacer(
getExtent(),
Iterators.concat(stage1.iterator(), stage2.iterator())),
new Stage3Committer());
}
private class Stage3Committer implements Operation {
@Override
public Operation resume(RunContext run) throws WorldEditException {
Extent extent = getExtent();
final Set<BlockVector> blocks = new HashSet<BlockVector>();
final Map<BlockVector, BaseBlock> blockTypes = new HashMap<BlockVector, BaseBlock>();
for (Map.Entry<BlockVector, BaseBlock> entry : stage3) {
final BlockVector pt = entry.getKey();
blocks.add(pt);
blockTypes.put(pt, entry.getValue());
}
while (!blocks.isEmpty()) {
BlockVector current = blocks.iterator().next();
if (!blocks.contains(current)) {
continue;
}
final Deque<BlockVector> walked = new LinkedList<BlockVector>();
while (true) {
walked.addFirst(current);
assert (blockTypes.containsKey(current));
final BaseBlock baseBlock = blockTypes.get(current);
final int type = baseBlock.getType();
final int data = baseBlock.getData();
switch (type) {
case BlockID.WOODEN_DOOR:
case BlockID.IRON_DOOR:
if ((data & 0x8) == 0) {
// Deal with lower door halves being attached to the floor AND the upper half
BlockVector upperBlock = current.add(0, 1, 0).toBlockVector();
if (blocks.contains(upperBlock) && !walked.contains(upperBlock)) {
walked.addFirst(upperBlock);
}
}
break;
case BlockID.MINECART_TRACKS:
case BlockID.POWERED_RAIL:
case BlockID.DETECTOR_RAIL:
case BlockID.ACTIVATOR_RAIL:
// Here, rails are hardcoded to be attached to the block below them.
// They're also attached to the block they're ascending towards via BlockType.getAttachment.
BlockVector lowerBlock = current.add(0, -1, 0).toBlockVector();
if (blocks.contains(lowerBlock) && !walked.contains(lowerBlock)) {
walked.addFirst(lowerBlock);
}
break;
}
final PlayerDirection attachment = BlockType.getAttachment(type, data);
if (attachment == null) {
// Block is not attached to anything => we can place it
break;
}
current = current.add(attachment.vector()).toBlockVector();
if (!blocks.contains(current)) {
// We ran outside the remaining set => assume we can place blocks on this
break;
}
if (walked.contains(current)) {
// Cycle detected => This will most likely go wrong, but there's nothing we can do about it.
break;
}
}
for (BlockVector pt : walked) {
extent.setBlock(pt, blockTypes.get(pt));
blocks.remove(pt);
}
}
stage1.clear();
stage2.clear();
stage3.clear();
return null;
}
@Override
public void cancel() {
}
}
}

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.extent.reorder;
import com.sk89q.worldedit.extent.Extent;
/**
* An interface for {@link Extent}s that are meant to reorder changes so
* that they are more successful.
*
* <p>For example, torches in Minecraft need to be placed on a block. A smart
* reordering implementation might place the torch after the block has
* been placed.</p>
*/
public interface ReorderingExtent extends Extent {
}

View File

@ -0,0 +1,181 @@
/*
* 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.extent.transform;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.math.transform.Transform;
import com.sk89q.worldedit.world.registry.BlockRegistry;
import com.sk89q.worldedit.world.registry.State;
import com.sk89q.worldedit.world.registry.StateValue;
import javax.annotation.Nullable;
import java.util.Map;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Transforms blocks themselves (but not their position) according to a
* given transform.
*/
public class BlockTransformExtent extends AbstractDelegateExtent {
private static final double RIGHT_ANGLE = Math.toRadians(90);
private final Transform transform;
private final BlockRegistry blockRegistry;
/**
* Create a new instance.
*
* @param extent the extent
* @param blockRegistry the block registry used for block direction data
*/
public BlockTransformExtent(Extent extent, Transform transform, BlockRegistry blockRegistry) {
super(extent);
checkNotNull(transform);
checkNotNull(blockRegistry);
this.transform = transform;
this.blockRegistry = blockRegistry;
}
/**
* Get the transform.
*
* @return the transform
*/
public Transform getTransform() {
return transform;
}
/**
* Transform a block without making a copy.
*
* @param block the block
* @param reverse true to transform in the opposite direction
* @return the same block
*/
private BaseBlock transformBlock(BaseBlock block, boolean reverse) {
transform(block, reverse ? transform.inverse() : transform, blockRegistry);
return block;
}
@Override
public BaseBlock getBlock(Vector position) {
return transformBlock(super.getBlock(position), false);
}
@Override
public BaseBlock getLazyBlock(Vector position) {
return transformBlock(super.getLazyBlock(position), false);
}
@Override
public boolean setBlock(Vector location, BaseBlock block) throws WorldEditException {
return super.setBlock(location, transformBlock(new BaseBlock(block), true));
}
/**
* Transform the given block using the given transform.
*
* <p>The provided block is modified.</p>
*
* @param block the block
* @param transform the transform
* @param registry the registry
* @return the same block
*/
public static BaseBlock transform(BaseBlock block, Transform transform, BlockRegistry registry) {
return transform(block, transform, registry, block);
}
/**
* Transform the given block using the given transform.
*
* @param block the block
* @param transform the transform
* @param registry the registry
* @param changedBlock the block to change
* @return the changed block
*/
private static BaseBlock transform(BaseBlock block, Transform transform, BlockRegistry registry, BaseBlock changedBlock) {
checkNotNull(block);
checkNotNull(transform);
checkNotNull(registry);
Map<String, ? extends State> states = registry.getStates(block);
if (states == null) {
return changedBlock;
}
for (State state : states.values()) {
if (state.hasDirection()) {
StateValue value = state.getValue(block);
if (value != null && value.getDirection() != null) {
StateValue newValue = getNewStateValue(state, transform, value.getDirection());
if (newValue != null) {
newValue.set(changedBlock);
}
}
}
}
return changedBlock;
}
/**
* Get the new value with the transformed direction.
*
* @param state the state
* @param transform the transform
* @param oldDirection the old direction to transform
* @return a new state or null if none could be found
*/
@Nullable
private static StateValue getNewStateValue(State state, Transform transform, Vector oldDirection) {
Vector newDirection = transform.apply(oldDirection).subtract(transform.apply(Vector.ZERO)).normalize();
StateValue newValue = null;
double closest = -2;
boolean found = false;
for (StateValue v : state.valueMap().values()) {
if (v.getDirection() != null) {
double dot = v.getDirection().normalize().dot(newDirection);
if (dot >= closest) {
closest = dot;
newValue = v;
found = true;
}
}
}
if (found) {
return newValue;
} else {
return null;
}
}
}

View File

@ -0,0 +1,89 @@
/*
* 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.extent.validation;
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.extent.AbstractDelegateExtent;
import com.sk89q.worldedit.extent.Extent;
import static com.google.common.base.Preconditions.checkArgument;
/**
* Limits the number of blocks that can be changed before a
* {@link MaxChangedBlocksException} is thrown.
*/
public class BlockChangeLimiter extends AbstractDelegateExtent {
private int limit;
private int count = 0;
/**
* Create a new instance.
*
* @param extent the extent
* @param limit the limit (>= 0) or -1 for no limit
*/
public BlockChangeLimiter(Extent extent, int limit) {
super(extent);
setLimit(limit);
}
/**
* Get the limit.
*
* @return the limit (>= 0) or -1 for no limit
*/
public int getLimit() {
return limit;
}
/**
* Set the limit.
*
* @param limit the limit (>= 0) or -1 for no limit
*/
public void setLimit(int limit) {
checkArgument(limit >= -1, "limit >= -1 required");
this.limit = limit;
}
/**
* Get the number of blocks that have been counted so far.
*
* @return the number of blocks
*/
public int getCount() {
return count;
}
@Override
public boolean setBlock(Vector location, BaseBlock block) throws WorldEditException {
if (limit >= 0) {
if (count >= limit) {
throw new MaxChangedBlocksException(limit);
}
count++;
}
return super.setBlock(location, block);
}
}

View File

@ -0,0 +1,75 @@
/*
* 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.extent.validation;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.world.World;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Validates set data to prevent creating invalid blocks and such.
*/
public class DataValidatorExtent extends AbstractDelegateExtent {
private final World world;
/**
* Create a new instance.
*
* @param extent the extent
* @param world the world
*/
public DataValidatorExtent(Extent extent, World world) {
super(extent);
checkNotNull(world);
this.world = world;
}
@Override
public boolean setBlock(Vector location, BaseBlock block) throws WorldEditException {
final int y = location.getBlockY();
final int type = block.getType();
if (y < 0 || y > world.getMaxY()) {
return false;
}
// No invalid blocks
if (!world.isValidBlockType(type)) {
return false;
}
if (block.getData() < 0) {
throw new SevereValidationException("Cannot set a data value that is less than 0");
}
return super.setBlock(location, block);
}
private static class SevereValidationException extends WorldEditException {
private SevereValidationException(String message) {
super(message);
}
}
}

View File

@ -0,0 +1,67 @@
/*
* 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.extent.world;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.blocks.BlockID;
import com.sk89q.worldedit.blocks.BlockType;
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.world.World;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Handles various quirks when setting blocks, such as ice turning
* into water or containers dropping their contents.
*/
public class BlockQuirkExtent extends AbstractDelegateExtent {
private final World world;
/**
* Create a new instance.
*
* @param extent the extent
* @param world the world
*/
public BlockQuirkExtent(Extent extent, World world) {
super(extent);
checkNotNull(world);
this.world = world;
}
@Override
public boolean setBlock(Vector position, BaseBlock block) throws WorldEditException {
BaseBlock lazyBlock = getExtent().getLazyBlock(position);
int existing = lazyBlock.getType();
if (BlockType.isContainerBlock(existing)) {
world.clearContainerBlockContents(position); // Clear the container block so that it doesn't drop items
} else if (existing == BlockID.ICE) {
world.setBlock(position, new BaseBlock(BlockID.AIR)); // Ice turns until water so this has to be done first
}
return super.setBlock(position, block);
}
}

View File

@ -0,0 +1,68 @@
/*
* 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.extent.world;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.world.World;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Automatically loads chunks when blocks are accessed.
*/
public class ChunkLoadingExtent extends AbstractDelegateExtent {
private final World world;
private boolean enabled;
/**
* Create a new instance.
*
* @param extent the extent
* @param world the world
* @param enabled true to enable
*/
public ChunkLoadingExtent(Extent extent, World world, boolean enabled) {
super(extent);
checkNotNull(world);
this.enabled = enabled;
this.world = world;
}
/**
* Create a new instance with chunk loading enabled.
*
* @param extent the extent
* @param world the world
*/
public ChunkLoadingExtent(Extent extent, World world) {
this(extent, world, true);
}
@Override
public boolean setBlock(Vector location, BaseBlock block) throws WorldEditException {
world.checkLoadedChunk(location);
return super.setBlock(location, block);
}
}

View File

@ -0,0 +1,112 @@
/*
* 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.extent.world;
import com.sk89q.worldedit.BlockVector2D;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.function.operation.RunContext;
import com.sk89q.worldedit.world.World;
import java.util.HashSet;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Implements "fast mode" which may skip physics, lighting, etc.
*/
public class FastModeExtent extends AbstractDelegateExtent {
private final World world;
private final Set<BlockVector2D> dirtyChunks = new HashSet<BlockVector2D>();
private boolean enabled = true;
/**
* Create a new instance with fast mode enabled.
*
* @param world the world
*/
public FastModeExtent(World world) {
this(world, true);
}
/**
* Create a new instance.
*
* @param world the world
* @param enabled true to enable fast mode
*/
public FastModeExtent(World world, boolean enabled) {
super(world);
checkNotNull(world);
this.world = world;
this.enabled = enabled;
}
/**
* Return whether fast mode is enabled.
*
* @return true if fast mode is enabled
*/
public boolean isEnabled() {
return enabled;
}
/**
* Set fast mode enable status.
*
* @param enabled true to enable fast mode
*/
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
@Override
public boolean setBlock(Vector location, BaseBlock block) throws WorldEditException {
if (enabled) {
dirtyChunks.add(new BlockVector2D(location.getBlockX() >> 4, location.getBlockZ() >> 4));
return world.setBlock(location, block, false);
} else {
return world.setBlock(location, block, true);
}
}
@Override
protected Operation commitBefore() {
return new Operation() {
@Override
public Operation resume(RunContext run) throws WorldEditException {
if (!dirtyChunks.isEmpty()) {
world.fixAfterFastMode(dirtyChunks);
}
return null;
}
@Override
public void cancel() {
}
};
}
}

View File

@ -0,0 +1,92 @@
/*
* 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.extent.world;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.blocks.BlockID;
import com.sk89q.worldedit.extent.AbstractDelegateExtent;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.world.World;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Makes changes to the world as if a player had done so during survival mode.
*
* <p>Note that this extent may choose to not call the underlying
* extent and may instead call methods on the {@link World} that is passed
* in the constructor. For that reason, if you wish to "catch" changes, you
* should catch them before the changes reach this extent.</p>
*/
public class SurvivalModeExtent extends AbstractDelegateExtent {
private final World world;
private boolean toolUse = false;
/**
* Create a new instance.
*
* @param extent the extent
* @param world the world
*/
public SurvivalModeExtent(Extent extent, World world) {
super(extent);
checkNotNull(world);
this.world = world;
}
/**
* Return whether changes to the world should be simulated with the
* use of game tools (such as pickaxes) whenever possible and reasonable.
*
* <p>For example, we could pretend that the act of setting a coal ore block
* to air (nothing) was the act of a player mining that coal ore block
* with a pickaxe, which would mean that a coal item would be dropped.</p>
*
* @return true if tool use is to be simulated
*/
public boolean hasToolUse() {
return toolUse;
}
/**
* Set whether changes to the world should be simulated with the
* use of game tools (such as pickaxes) whenever possible and reasonable.
*
* @param toolUse true if tool use is to be simulated
* @see #hasToolUse() for an explanation
*/
public void setToolUse(boolean toolUse) {
this.toolUse = toolUse;
}
@Override
public boolean setBlock(Vector location, BaseBlock block) throws WorldEditException {
if (toolUse && block.getType() == BlockID.AIR) {
world.simulateBlockMine(location);
return true;
} else {
return super.setBlock(location, block);
}
}
}