mirror of
https://github.com/plexusorg/Plex-FAWE.git
synced 2024-09-19 13:58:22 +00:00
a629d15c74
- so certain people can look at the diff and complain about my sloppy code :( Signed-off-by: Jesse Boyd <jessepaleg@gmail.com>
646 lines
20 KiB
Java
646 lines
20 KiB
Java
package com.sk89q.worldedit.command.tool;
|
|
|
|
import com.boydti.fawe.Fawe;
|
|
import com.boydti.fawe.FaweCache;
|
|
import com.boydti.fawe.config.BBC;
|
|
import com.boydti.fawe.object.FawePlayer;
|
|
import com.boydti.fawe.object.RunnableVal;
|
|
import com.boydti.fawe.object.brush.BrushSettings;
|
|
import com.boydti.fawe.object.brush.MovableTool;
|
|
import com.boydti.fawe.object.brush.ResettableTool;
|
|
import com.boydti.fawe.object.brush.TargetMode;
|
|
import com.boydti.fawe.object.brush.scroll.ScrollAction;
|
|
import com.boydti.fawe.object.brush.scroll.ScrollTool;
|
|
import com.boydti.fawe.object.brush.visualization.VisualChunk;
|
|
import com.boydti.fawe.object.brush.visualization.VisualExtent;
|
|
import com.boydti.fawe.object.brush.visualization.VisualMode;
|
|
import com.boydti.fawe.object.extent.ResettableExtent;
|
|
import com.boydti.fawe.object.mask.MaskedTargetBlock;
|
|
import com.boydti.fawe.object.pattern.PatternTraverser;
|
|
import com.boydti.fawe.util.*;
|
|
import com.google.gson.Gson;
|
|
import com.google.gson.reflect.TypeToken;
|
|
import com.sk89q.minecraft.util.commands.CommandException;
|
|
import com.sk89q.worldedit.*;
|
|
import com.sk89q.worldedit.blocks.BaseBlock;
|
|
import com.sk89q.worldedit.world.block.BlockState;
|
|
import com.sk89q.worldedit.blocks.BaseItem;
|
|
import com.sk89q.worldedit.command.tool.brush.Brush;
|
|
import com.sk89q.worldedit.entity.Player;
|
|
import com.sk89q.worldedit.extension.input.InputParseException;
|
|
import com.sk89q.worldedit.extension.platform.Actor;
|
|
import com.sk89q.worldedit.extension.platform.Platform;
|
|
import com.sk89q.worldedit.extent.inventory.BlockBag;
|
|
import com.sk89q.worldedit.function.mask.Mask;
|
|
import com.sk89q.worldedit.function.mask.MaskIntersection;
|
|
import com.sk89q.worldedit.function.mask.SolidBlockMask;
|
|
import com.sk89q.worldedit.function.pattern.Pattern;
|
|
import com.sk89q.worldedit.session.request.Request;
|
|
import com.sk89q.worldedit.util.Location;
|
|
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
|
import com.sk89q.worldedit.world.block.BlockType;
|
|
|
|
import java.io.IOException;
|
|
import java.io.Serializable;
|
|
import java.lang.reflect.Type;
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
import java.util.concurrent.locks.Lock;
|
|
import java.util.concurrent.locks.ReentrantLock;
|
|
import javax.annotation.Nullable;
|
|
|
|
|
|
import static com.boydti.fawe.object.brush.TargetMode.TARGET_FACE_RANGE;
|
|
import static com.google.common.base.Preconditions.checkNotNull;
|
|
|
|
public class BrushTool implements DoubleActionTraceTool, ScrollTool, MovableTool, ResettableTool, Serializable {
|
|
// TODO:
|
|
// Serialize methods
|
|
// serialize BrushSettings (primary and secondary only if different)
|
|
// set transient values e.g. context
|
|
|
|
public enum BrushAction {
|
|
PRIMARY,
|
|
SECONDARY
|
|
}
|
|
|
|
protected static int MAX_RANGE = 500;
|
|
protected int range = 240;
|
|
private VisualMode visualMode = VisualMode.NONE;
|
|
private TargetMode targetMode = TargetMode.TARGET_BLOCK_RANGE;
|
|
private Mask targetMask = null;
|
|
private int targetOffset;
|
|
|
|
private transient BrushSettings primary = new BrushSettings();
|
|
private transient BrushSettings secondary = new BrushSettings();
|
|
private transient BrushSettings context = primary;
|
|
|
|
private transient VisualExtent visualExtent;
|
|
private transient Lock lock = new ReentrantLock();
|
|
|
|
private transient BaseItem holder;
|
|
|
|
public BrushTool(String permission) {
|
|
getContext().addPermission(permission);
|
|
}
|
|
|
|
public BrushTool() {
|
|
}
|
|
|
|
public static BrushTool fromString(Player player, LocalSession session, String json) throws CommandException, InputParseException {
|
|
Gson gson = new Gson();
|
|
Type type = new TypeToken<Map<String, Object>>() {
|
|
}.getType();
|
|
Map<String, Object> root = gson.fromJson(json, type);
|
|
if (root == null) {
|
|
Fawe.debug("Failed to load " + json);
|
|
return new BrushTool();
|
|
}
|
|
Map<String, Object> primary = (Map<String, Object>) root.get("primary");
|
|
Map<String, Object> secondary = (Map<String, Object>) root.getOrDefault("secondary", primary);
|
|
|
|
VisualMode visual = VisualMode.valueOf((String) root.getOrDefault("visual", "NONE"));
|
|
TargetMode target = TargetMode.valueOf((String) root.getOrDefault("target", "TARGET_BLOCK_RANGE"));
|
|
int range = ((Number) root.getOrDefault("range", -1)).intValue();
|
|
int offset = ((Number) root.getOrDefault("offset", 0)).intValue();
|
|
|
|
BrushTool tool = new BrushTool();
|
|
tool.visualMode = visual;
|
|
tool.targetMode = target;
|
|
tool.range = range;
|
|
tool.targetOffset = offset;
|
|
|
|
BrushSettings primarySettings = BrushSettings.get(tool, player, session, primary);
|
|
tool.setPrimary(primarySettings);
|
|
if (primary != secondary) {
|
|
BrushSettings secondarySettings = BrushSettings.get(tool, player, session, secondary);
|
|
tool.setSecondary(secondarySettings);
|
|
}
|
|
|
|
return tool;
|
|
}
|
|
|
|
public void setHolder(BaseItem holder) {
|
|
this.holder = holder;
|
|
}
|
|
|
|
public boolean isSet() {
|
|
return primary.getBrush() != null || secondary.getBrush() != null;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return toString(new Gson());
|
|
}
|
|
|
|
public String toString(Gson gson) {
|
|
HashMap<String, Object> map = new HashMap<>();
|
|
map.put("primary", primary.getSettings());
|
|
if (primary != secondary) {
|
|
map.put("secondary", secondary.getSettings());
|
|
}
|
|
if (visualMode != null && visualMode != VisualMode.NONE) {
|
|
map.put("visual", visualMode);
|
|
}
|
|
if (targetMode != TargetMode.TARGET_BLOCK_RANGE) {
|
|
map.put("target", targetMode);
|
|
}
|
|
if (range != -1 && range != 240) {
|
|
map.put("range", range);
|
|
}
|
|
if (targetOffset != 0) {
|
|
map.put("offset", targetOffset);
|
|
}
|
|
return gson.toJson(map);
|
|
}
|
|
|
|
public void update() {
|
|
if (holder != null) {
|
|
BrushCache.setTool(holder, this);
|
|
}
|
|
}
|
|
|
|
private void writeObject(java.io.ObjectOutputStream stream) throws IOException {
|
|
stream.defaultWriteObject();
|
|
stream.writeBoolean(primary == secondary);
|
|
stream.writeObject(primary);
|
|
if (primary != secondary) {
|
|
stream.writeObject(secondary);
|
|
}
|
|
}
|
|
|
|
private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException {
|
|
lock = new ReentrantLock();
|
|
boolean multi = stream.readBoolean();
|
|
primary = (BrushSettings) stream.readObject();
|
|
if (multi) {
|
|
secondary = (BrushSettings) stream.readObject();
|
|
} else {
|
|
secondary = primary;
|
|
}
|
|
context = primary;
|
|
}
|
|
|
|
public BrushSettings getContext() {
|
|
BrushSettings tmp = context;
|
|
if (tmp == null) {
|
|
context = tmp = primary;
|
|
}
|
|
return tmp;
|
|
}
|
|
|
|
public void setContext(BrushSettings context) {
|
|
this.context = context;
|
|
}
|
|
|
|
@Override
|
|
public boolean canUse(Actor player) {
|
|
if (primary == secondary) {
|
|
return primary.canUse(player);
|
|
}
|
|
return primary.canUse(player) && secondary.canUse(player);
|
|
}
|
|
|
|
public ResettableExtent getTransform() {
|
|
return getContext().getTransform();
|
|
}
|
|
|
|
public BrushSettings getPrimary() {
|
|
return primary;
|
|
}
|
|
|
|
public BrushSettings getSecondary() {
|
|
return secondary;
|
|
}
|
|
|
|
public BrushSettings getOffHand() {
|
|
return context == primary ? secondary : primary;
|
|
}
|
|
|
|
public void setPrimary(BrushSettings primary) {
|
|
checkNotNull(primary);
|
|
this.primary = primary;
|
|
this.context = primary;
|
|
update();
|
|
}
|
|
|
|
public void setSecondary(BrushSettings secondary) {
|
|
checkNotNull(secondary);
|
|
this.secondary = secondary;
|
|
this.context = secondary;
|
|
update();
|
|
}
|
|
|
|
public void setTransform(ResettableExtent transform) {
|
|
getContext().setTransform(transform);
|
|
update();
|
|
}
|
|
|
|
/**
|
|
* Get the filter.
|
|
*
|
|
* @return the filter
|
|
*/
|
|
public Mask getMask() {
|
|
return getContext().getMask();
|
|
}
|
|
|
|
/**
|
|
* Get the filter.
|
|
*
|
|
* @return the filter
|
|
*/
|
|
public Mask getSourceMask() {
|
|
return getContext().getSourceMask();
|
|
}
|
|
|
|
@Override
|
|
public boolean reset() {
|
|
Brush br = getBrush();
|
|
if (br instanceof ResettableTool) {
|
|
return ((ResettableTool) br).reset();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Set the block filter used for identifying blocks to replace.
|
|
*
|
|
* @param filter the filter to set
|
|
*/
|
|
public void setMask(Mask filter) {
|
|
this.getContext().setMask(filter);
|
|
update();
|
|
}
|
|
|
|
/**
|
|
* Set the block filter used for identifying blocks to replace.
|
|
*
|
|
* @param filter the filter to set
|
|
*/
|
|
public void setSourceMask(Mask filter) {
|
|
this.getContext().setSourceMask(filter);
|
|
update();
|
|
}
|
|
|
|
/**
|
|
* Set the brush.
|
|
*
|
|
* @param brush tbe brush
|
|
* @param permission the permission
|
|
*/
|
|
@Deprecated
|
|
public void setBrush(Brush brush, String permission) {
|
|
setBrush(brush, permission, null);
|
|
update();
|
|
}
|
|
|
|
@Deprecated
|
|
public void setBrush(Brush brush, String permission, Player player) {
|
|
if (player != null) clear(player);
|
|
BrushSettings current = getContext();
|
|
current.clearPerms();
|
|
current.setBrush(brush);
|
|
current.addPermission(permission);
|
|
update();
|
|
}
|
|
|
|
/**
|
|
* Get the current brush.
|
|
*
|
|
* @return the current brush
|
|
*/
|
|
public Brush getBrush() {
|
|
return getContext().getBrush();
|
|
}
|
|
|
|
/**
|
|
* Set the material.
|
|
*
|
|
* @param material the material
|
|
*/
|
|
public void setFill(@Nullable Pattern material) {
|
|
this.getContext().setFill(material);
|
|
}
|
|
|
|
/**
|
|
* Get the material.
|
|
*
|
|
* @return the material
|
|
*/
|
|
@Nullable
|
|
public Pattern getMaterial() {
|
|
return getContext().getMaterial();
|
|
}
|
|
|
|
/**
|
|
* Get the set brush size.
|
|
*
|
|
* @return a radius
|
|
*/
|
|
public double getSize() {
|
|
return getContext().getSize();
|
|
}
|
|
|
|
/**
|
|
* Set the set brush size.
|
|
*
|
|
* @param radius a radius
|
|
*/
|
|
public void setSize(double radius) {
|
|
this.getContext().setSize(radius);
|
|
}
|
|
|
|
/**
|
|
* Get the set brush range.
|
|
*
|
|
* @return the range of the brush in blocks
|
|
*/
|
|
public int getRange() {
|
|
return (range < 0) ? MAX_RANGE : Math.min(range, MAX_RANGE);
|
|
}
|
|
|
|
/**
|
|
* Set the set brush range.
|
|
*
|
|
* @param range the range of the brush in blocks
|
|
*/
|
|
public void setRange(int range) {
|
|
this.range = range;
|
|
}
|
|
|
|
public Vector getPosition(EditSession editSession, Player player) {
|
|
Location loc = player.getLocation();
|
|
switch (targetMode) {
|
|
case TARGET_BLOCK_RANGE:
|
|
return offset(new MutableBlockVector(trace(editSession, player, getRange(), true)), loc.toVector());
|
|
case FOWARD_POINT_PITCH: {
|
|
int d = 0;
|
|
float pitch = loc.getPitch();
|
|
pitch = 23 - (pitch / 4);
|
|
d += (int) (Math.sin(Math.toRadians(pitch)) * 50);
|
|
final Vector vector = loc.getDirection().setY(0).normalize().multiply(d);
|
|
vector.add(loc.getX(), loc.getY(), loc.getZ()).toBlockVector();
|
|
return offset(new MutableBlockVector(vector), loc.toVector());
|
|
}
|
|
case TARGET_POINT_HEIGHT: {
|
|
final int height = loc.getBlockY();
|
|
final int x = loc.getBlockX();
|
|
final int z = loc.getBlockZ();
|
|
int y;
|
|
for (y = height; y > 0; y--) {
|
|
BlockType block = editSession.getBlockType(x, y, z);
|
|
if (block.getMaterial().isMovementBlocker()) {
|
|
break;
|
|
}
|
|
}
|
|
final int distance = (height - y) + 8;
|
|
return offset(new MutableBlockVector(trace(editSession, player, distance, true)), loc.toVector());
|
|
}
|
|
case TARGET_FACE_RANGE:
|
|
return offset(new MutableBlockVector(trace(editSession, player, getRange(), true)), loc.toVector());
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private Vector offset(Vector target, Vector playerPos) {
|
|
if (targetOffset == 0) return target;
|
|
return target.subtract(target.subtract(playerPos).normalize().multiply(targetOffset));
|
|
}
|
|
|
|
private Vector trace(EditSession editSession, Player player, int range, boolean useLastBlock) {
|
|
Mask mask = targetMask == null ? new SolidBlockMask(editSession) : targetMask;
|
|
new MaskTraverser(mask).reset(editSession);
|
|
MaskedTargetBlock tb = new MaskedTargetBlock(mask, player, range, 0.2);
|
|
return TaskManager.IMP.sync(new RunnableVal<Vector>() {
|
|
@Override
|
|
public void run(Vector value) {
|
|
Location result = tb.getMaskedTargetBlock(useLastBlock);
|
|
this.value = result.toVector();
|
|
}
|
|
});
|
|
}
|
|
|
|
public boolean act(BrushAction action, Platform server, LocalConfiguration config, Player player, LocalSession session) {
|
|
switch (action) {
|
|
case PRIMARY:
|
|
setContext(primary);
|
|
break;
|
|
case SECONDARY:
|
|
setContext(secondary);
|
|
break;
|
|
}
|
|
|
|
|
|
BrushSettings current = getContext();
|
|
Brush brush = current.getBrush();
|
|
if (brush == null) return false;
|
|
|
|
EditSession editSession = session.createEditSession(player);
|
|
if (current.setWorld(editSession.getWorld().getName()) && !current.canUse(player)) {
|
|
BBC.NO_PERM.send(player, StringMan.join(current.getPermissions(), ","));
|
|
return false;
|
|
}
|
|
|
|
Vector target = getPosition(editSession, player);
|
|
|
|
if (target == null) {
|
|
editSession.cancel();
|
|
BBC.NO_BLOCK.send(player);
|
|
return false;
|
|
}
|
|
BlockBag bag = session.getBlockBag(player);
|
|
Request.request().setEditSession(editSession);
|
|
Mask mask = current.getMask();
|
|
if (mask != null) {
|
|
Mask existingMask = editSession.getMask();
|
|
|
|
if (existingMask == null) {
|
|
editSession.setMask(mask);
|
|
} else if (existingMask instanceof MaskIntersection) {
|
|
((MaskIntersection) existingMask).add(mask);
|
|
} else {
|
|
MaskIntersection newMask = new MaskIntersection(existingMask);
|
|
newMask.add(mask);
|
|
editSession.setMask(newMask);
|
|
}
|
|
}
|
|
Mask sourceMask = current.getSourceMask();
|
|
if (sourceMask != null) {
|
|
editSession.addSourceMask(sourceMask);
|
|
}
|
|
ResettableExtent transform = current.getTransform();
|
|
if (transform != null) {
|
|
editSession.addTransform(transform);
|
|
}
|
|
try {
|
|
new PatternTraverser(current).reset(editSession);
|
|
brush.build(editSession, target, current.getMaterial(), current.getSize());
|
|
} catch (WorldEditException e) {
|
|
player.printError("Max blocks change limit reached."); // Never happens
|
|
} finally {
|
|
if (bag != null) {
|
|
bag.flushChanges();
|
|
}
|
|
session.remember(editSession);
|
|
Request.reset();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean actPrimary(Platform server, LocalConfiguration config, Player player, LocalSession session) {
|
|
return act(BrushAction.PRIMARY, server, config, player, session);
|
|
}
|
|
|
|
@Override
|
|
public boolean actSecondary(Platform server, LocalConfiguration config, Player player, LocalSession session) {
|
|
return act(BrushAction.SECONDARY, server, config, player, session);
|
|
}
|
|
|
|
public static Class<?> inject() {
|
|
return BrushTool.class;
|
|
}
|
|
|
|
public void setScrollAction(ScrollAction scrollAction) {
|
|
this.getContext().setScrollAction(scrollAction);
|
|
update();
|
|
}
|
|
|
|
public void setTargetOffset(int targetOffset) {
|
|
this.targetOffset = targetOffset;
|
|
update();
|
|
}
|
|
|
|
public void setTargetMode(TargetMode targetMode) {
|
|
this.targetMode = targetMode != null ? targetMode : TargetMode.TARGET_BLOCK_RANGE;
|
|
update();
|
|
}
|
|
|
|
public void setTargetMask(Mask mask) {
|
|
this.targetMask = mask;
|
|
update();
|
|
}
|
|
|
|
public void setVisualMode(Player player, VisualMode visualMode) {
|
|
if (visualMode == null) visualMode = VisualMode.NONE;
|
|
if (this.visualMode != visualMode) {
|
|
if (this.visualMode != VisualMode.NONE) {
|
|
clear(player);
|
|
}
|
|
this.visualMode = visualMode != null ? visualMode : VisualMode.NONE;
|
|
if (visualMode != VisualMode.NONE) {
|
|
try {
|
|
queueVisualization(FawePlayer.wrap(player));
|
|
} catch (Throwable e) {
|
|
WorldEdit.getInstance().getPlatformManager().handleThrowable(e, player);
|
|
}
|
|
}
|
|
}
|
|
update();
|
|
}
|
|
|
|
public TargetMode getTargetMode() {
|
|
return targetMode;
|
|
}
|
|
|
|
public int getTargetOffset() {
|
|
return targetOffset;
|
|
}
|
|
|
|
public Mask getTargetMask() {
|
|
return targetMask;
|
|
}
|
|
|
|
public VisualMode getVisualMode() {
|
|
return visualMode;
|
|
}
|
|
|
|
@Override
|
|
public boolean increment(Player player, int amount) {
|
|
BrushSettings current = getContext();
|
|
ScrollAction tmp = current.getScrollAction();
|
|
if (tmp != null) {
|
|
tmp.setTool(this);
|
|
if (tmp.increment(player, amount)) {
|
|
if (visualMode != VisualMode.NONE) {
|
|
try {
|
|
queueVisualization(FawePlayer.wrap(player));
|
|
} catch (Throwable e) {
|
|
WorldEdit.getInstance().getPlatformManager().handleThrowable(e, player);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
if (visualMode != VisualMode.NONE) {
|
|
clear(player);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public void queueVisualization(FawePlayer player) {
|
|
Fawe.get().getVisualQueue().queue(player);
|
|
}
|
|
|
|
@Deprecated
|
|
public synchronized void visualize(BrushTool.BrushAction action, Player player) throws WorldEditException {
|
|
VisualMode mode = getVisualMode();
|
|
if (mode == VisualMode.NONE) {
|
|
return;
|
|
}
|
|
BrushSettings current = getContext();
|
|
Brush brush = current.getBrush();
|
|
if (brush == null) return;
|
|
FawePlayer<Object> fp = FawePlayer.wrap(player);
|
|
EditSession editSession = new EditSessionBuilder(player.getWorld())
|
|
.player(fp)
|
|
.allowedRegionsEverywhere()
|
|
.autoQueue(false)
|
|
.blockBag(null)
|
|
.changeSetNull()
|
|
.combineStages(false)
|
|
.build();
|
|
VisualExtent newVisualExtent = new VisualExtent(editSession.getExtent(), editSession.getQueue());
|
|
Vector position = getPosition(editSession, player);
|
|
if (position != null) {
|
|
editSession.setExtent(newVisualExtent);
|
|
switch (mode) {
|
|
case POINT: {
|
|
editSession.setBlock(position, VisualChunk.VISUALIZE_BLOCK);
|
|
break;
|
|
}
|
|
case OUTLINE: {
|
|
new PatternTraverser(current).reset(editSession);
|
|
brush.build(editSession, position, current.getMaterial(), current.getSize());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (visualExtent != null) {
|
|
// clear old data
|
|
visualExtent.clear(newVisualExtent, fp);
|
|
}
|
|
visualExtent = newVisualExtent;
|
|
newVisualExtent.visualize(fp);
|
|
}
|
|
|
|
public void clear(Player player) {
|
|
FawePlayer<Object> fp = FawePlayer.wrap(player);
|
|
Fawe.get().getVisualQueue().dequeue(fp);
|
|
if (visualExtent != null) {
|
|
visualExtent.clear(null, fp);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean move(Player player) {
|
|
if (visualMode != VisualMode.NONE) {
|
|
queueVisualization(FawePlayer.wrap(player));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|