mirror of
https://github.com/plexusorg/Plex-FAWE.git
synced 2025-07-10 04:48:34 +00:00
Switch to Gradle. Use git log --follow for history.
This converts the project into a multi-module Gradle build. By default, Git does not show history past a rename, so use git log --follow to see further history.
This commit is contained in:
@ -0,0 +1,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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
||||
}
|
@ -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);
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
68
worldedit-core/src/main/java/com/sk89q/worldedit/extent/cache/LastAccessExtentCache.java
vendored
Normal file
68
worldedit-core/src/main/java/com/sk89q/worldedit/extent/cache/LastAccessExtentCache.java
vendored
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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 {
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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 {
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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 {
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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 {
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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() {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user