
de.adrodoc55.minecraft.mpl.placement.MplChainPlacer Maven / Gradle / Ivy
/*
* Minecraft Programming Language (MPL): A language for easy development of command block
* applications including an IDE.
*
* © Copyright (C) 2016 Adrodoc55
*
* This file is part of MPL.
*
* MPL is free software: you can redistribute it and/or modify it under the terms of the GNU General
* Public License as published by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MPL 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 General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with MPL. If not, see
* .
*
*
*
* Minecraft Programming Language (MPL): Eine Sprache für die einfache Entwicklung von Commandoblock
* Anwendungen, inklusive einer IDE.
*
* © Copyright (C) 2016 Adrodoc55
*
* Diese Datei ist Teil von MPL.
*
* MPL ist freie Software: Sie können diese unter den Bedingungen der GNU General Public License,
* wie von der Free Software Foundation, Version 3 der Lizenz oder (nach Ihrer Wahl) jeder späteren
* veröffentlichten Version, weiterverbreiten und/oder modifizieren.
*
* MPL wird in der Hoffnung, dass es nützlich sein wird, aber OHNE JEDE GEWÄHRLEISTUNG,
* bereitgestellt; sogar ohne die implizite Gewährleistung der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN
* BESTIMMTEN ZWECK. Siehe die GNU General Public License für weitere Details.
*
* Sie sollten eine Kopie der GNU General Public License zusammen mit MPL erhalten haben. Wenn
* nicht, siehe .
*/
package de.adrodoc55.minecraft.mpl.placement;
import static de.adrodoc55.minecraft.mpl.compilation.CompilerOptions.CompilerOption.DEBUG;
import static de.adrodoc55.minecraft.mpl.compilation.CompilerOptions.CompilerOption.TRANSMITTER;
import static de.kussm.direction.Direction.EAST;
import static de.kussm.direction.Direction.NORTH;
import static de.kussm.direction.Direction.WEST;
import static de.kussm.direction.Directions.$;
import static java.util.Comparator.naturalOrder;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import de.adrodoc55.minecraft.coordinate.Axis3D;
import de.adrodoc55.minecraft.coordinate.Coordinate3D;
import de.adrodoc55.minecraft.coordinate.Direction3D;
import de.adrodoc55.minecraft.coordinate.Orientation3D;
import de.adrodoc55.minecraft.mpl.MplUtils;
import de.adrodoc55.minecraft.mpl.blocks.AirBlock;
import de.adrodoc55.minecraft.mpl.blocks.CommandBlock;
import de.adrodoc55.minecraft.mpl.blocks.MplBlock;
import de.adrodoc55.minecraft.mpl.blocks.Transmitter;
import de.adrodoc55.minecraft.mpl.chain.ChainContainer;
import de.adrodoc55.minecraft.mpl.chain.CommandBlockChain;
import de.adrodoc55.minecraft.mpl.chain.CommandChain;
import de.adrodoc55.minecraft.mpl.commands.Mode;
import de.adrodoc55.minecraft.mpl.commands.chainlinks.ChainLink;
import de.adrodoc55.minecraft.mpl.commands.chainlinks.Command;
import de.adrodoc55.minecraft.mpl.commands.chainlinks.MplSkip;
import de.adrodoc55.minecraft.mpl.commands.chainlinks.NoOperationCommand;
import de.adrodoc55.minecraft.mpl.compilation.CompilerOptions;
import de.adrodoc55.minecraft.mpl.compilation.CompilerOptions.CompilerOption;
import de.kussm.chain.Chain;
import de.kussm.chain.ChainLayouter;
import de.kussm.chain.ChainLinkType;
import de.kussm.direction.Directions;
import de.kussm.position.Position;
/**
* @author Adrodoc55
*/
public abstract class MplChainPlacer {
protected final ChainContainer container;
protected final CompilerOptions options;
protected final List chains = new ArrayList<>();
protected MplChainPlacer(ChainContainer container, CompilerOptions options) {
this.container = container;
this.options = options;
}
protected Orientation3D getOrientation() {
return container.getOrientation();
}
protected CommandChain getInstall() {
return container.getInstall();
}
protected CommandChain getUninstall() {
return container.getUninstall();
}
public abstract List place() throws NotEnoughSpaceException;
/**
* Generates a flat {@link CommandBlockChain}. Flat means, that the chain will not have any width
* in the c direction of the orientation.
* The chain will not have any illegal transmitter or receiver regarding all chains that have
* already been added to {@link #chains}. Also the chain will not have any illegally placed
* conditional command blocks.
*
* @param chain the {@link CommandChain} to be placed
* @param start the starting coordinate of the chain
* @param template along which the chain should be placed
* @return a valid placed {@link CommandBlockChain}
* @throws NotEnoughSpaceException if the template is to small to allow a valid placement
*/
public CommandBlockChain generateFlat(CommandChain chain, Coordinate3D start, Directions template)
throws NotEnoughSpaceException {
LinkedHashMap placed = place(chain, start, template);
List blocks = toBlocks(chain.getCommands(), placed);
CommandBlockChain result = new CommandBlockChain(chain.getName(), blocks);
result.move(start);
return result;
}
/**
* Places the given chain. The placement will not have any illegal transmitter or receiver
* regarding all chains that have already been added to {@link #chains}. Also the chain will not
* have any illegally placed conditional command blocks.
*
* @param chain the {@link CommandChain} to be placed
* @param start the starting coordinate of the chain
* @param template along which the chain should be placed
* @return a {@link LinkedHashMap} containing the {@link Position}s and {@link ChainLinkType}s of
* the valid placed chain
* @throws NotEnoughSpaceException if the template is to small to allow a valid placement
*/
protected LinkedHashMap place(CommandChain chain, Coordinate3D start,
Directions template) throws NotEnoughSpaceException {
Chain chainLinkChain = toChainLinkChain(chain.getCommands());
Set forbiddenReceiver = new HashSet<>();
Set forbiddenTransmitter = new HashSet<>();
fillForbiddenPositions(start, forbiddenReceiver, forbiddenTransmitter);
return place(chainLinkChain, template, forbiddenReceiver, forbiddenTransmitter);
}
private void fillForbiddenPositions(Coordinate3D start, Set forbiddenReceiver,
Set forbiddenTransmitter) {
Orientation3D orientation = getOrientation();
Axis3D cAxis = orientation.getC().getAxis();
int startC = start.get(cAxis);
for (CommandBlockChain materialized : chains) {
for (MplBlock block : materialized.getBlocks()) {
Coordinate3D currentCoord = block.getCoordinate();
int currentC = currentCoord.get(cAxis);
Position pos = toPosition(currentCoord.minus(start), orientation);
ImmutableSet illegalPositions;
if (startC - 1 == currentC || currentC == startC + 1) {
illegalPositions = ImmutableSet.of(pos);
} else if (startC == currentC) {
illegalPositions = pos.neighbours();
} else {
continue;
}
for (Position illegalPos : illegalPositions) {
// Only look at positive positions
if (illegalPos.getX() >= 0 && illegalPos.getY() >= 0) {
if (isTransmitter(block)) {
forbiddenReceiver.add(illegalPos);
} else if (isReceiver(block)) {
forbiddenTransmitter.add(illegalPos);
}
}
}
}
}
}
/**
* Places the chain according to the template WITHOUT regarding forbidden transmitter/receiver
* positions. The chain will not have any illegally placed conditional command blocks.
*
* @param chain the {@link Chain} to place
* @param template along which the chain should be placed
* @return a {@link LinkedHashMap} containing the {@link Position}s and {@link ChainLinkType}s of
* the placed chain
* @throws NotEnoughSpaceException if the template is to small to allow a placement
*/
public LinkedHashMap place(Chain chain, Directions template)
throws NotEnoughSpaceException {
return place(chain, template, new HashSet<>(), new HashSet<>());
}
/**
* Places the given chain. The chain will not have any illegally placed conditional command
* blocks.
* If this placement runs with {@link CompilerOption#TRANSMITTER}, the result will not have any
* illegal transmitter or receiver regarding the parameters. Also there will be no receiver at x=0
* and no transmitter at x=1.
*
* @param chain the {@link Chain} to be placed
* @param template along which the should will be placed
* @param forbiddenReceivers a {@link Set} containing all {@link Position}s that may not contain a
* receiver
* @param forbiddenTransmitters a {@link Set} containing all {@link Position}s that may not
* contain a transmitter
* @return a {@link LinkedHashMap} containing the {@link Position}s and {@link ChainLinkType}s of
* the valid placed chain
* @throws NotEnoughSpaceException if the template is to small to allow a valid placement
*/
protected LinkedHashMap place(Chain chain, Directions template,
Set forbiddenReceivers, Set forbiddenTransmitters)
throws NotEnoughSpaceException {
if (options.hasOption(TRANSMITTER)) {
// receivers are not allowed at x=0 because the start transmitters of all chains are at x=0
Predicate isReceiverAllowed =
pos -> !forbiddenReceivers.contains(pos) && pos.getX() != 0;
// transmitters are not allowed at x=1 because the start receivers of all chains are at x=1
Predicate isTransmitterAllowed =
pos -> !forbiddenTransmitters.contains(pos) && pos.getX() != 1;
return ChainLayouter.place(chain, template, isReceiverAllowed, isTransmitterAllowed);
} else {
return ChainLayouter.place(chain, template);
}
}
protected CommandChain getPopulatedInstall() {
List commands = getInstall().getCommands();
ArrayList result = new ArrayList<>(commands.size() + chains.size());
result.addAll(commands);
for (CommandBlockChain chain : chains) {
String name = chain.getName();
if (name == null || name == "install" || name == "uninstall") {
continue;
}
Coordinate3D chainStart = chain.getBlocks().get(0).getCoordinate();
int index = options.hasOption(TRANSMITTER) ? 2 : 1;
result.add(index,
new Command("/summon ArmorStand ${origin + (" + chainStart.toAbsoluteString()
+ ")} {CustomName:" + name + ",Tags:[" + container.getHashCode()
+ "],NoGravity:1b,Invisible:1b,Invulnerable:1b,Marker:1b"
+ (options.hasOption(DEBUG) ? ",CustomNameVisible:1" : "") + "}"));
}
return new CommandChain(getInstall().getName(), result);
}
protected CommandChain getPopulatedUninstall() {
List commands = getUninstall().getCommands();
ArrayList result = new ArrayList<>(commands.size() + chains.size());
result.addAll(commands);
result.add(new Command("/kill @e[type=ArmorStand,tag=" + container.getHashCode() + "]"));
return new CommandChain(getUninstall().getName(), result);
}
protected List toBlocks(List commands,
LinkedHashMap placed) {
LinkedList chainLinks = new LinkedList<>(commands);
Orientation3D orientation = getOrientation();
LinkedList> entries =
placed.entrySet().stream().collect(Collectors.toCollection(LinkedList::new));
List blocks = new LinkedList<>();
while (entries.size() > 1) {
Entry entry = entries.pop();
Position pos = entry.getKey();
Position nextPos = entries.peek().getKey();
Direction3D d = getDirection(pos, nextPos, orientation);
Coordinate3D coord = toCoordinate(pos, orientation);
if (entry.getValue() == ChainLinkType.NO_OPERATION) {
blocks.add(new CommandBlock(new NoOperationCommand(), d, coord));
} else {
ChainLink chainLink = chainLinks.pop();
if (chainLink instanceof Command) {
blocks.add(new CommandBlock((Command) chainLink, d, coord));
} else if (chainLink instanceof MplSkip) {
blocks.add(new Transmitter(((MplSkip) chainLink).isInternal(), coord));
}
}
}
// last block is always air
Position lastPos = entries.pop().getKey();
blocks.add(new AirBlock(toCoordinate(lastPos, orientation)));
return blocks;
}
public int getLongestSuccessiveConditionalCount() {
return Stream
.concat(container.getChains().stream().map(c -> c.getCommands()),
Stream.of(getInstall().getCommands(), getUninstall().getCommands()))
.map(commands -> getLongestSuccessiveConditionalCount(commands)).max(naturalOrder())
.orElse(0);
}
protected String getDeleteCommand() {
Coordinate3D max = getBoundaries();
StringBuilder sb = new StringBuilder();
sb.append("fill ${origin}").append(' ');
sb.append("${origin + (").append(max.toAbsoluteString()).append(")}").append(' ');
sb.append("air");
return sb.toString();
}
protected Coordinate3D getBoundaries() {
return MplUtils.getBoundaries(getOrientation(),
chains.stream()//
.map(c -> c.getBlocks())//
.flatMap(bs -> bs.stream())//
.map(b -> b.getCoordinate())//
.collect(Collectors.toList()));
}
public static int getLongestSuccessiveConditionalCount(List extends ChainLink> chainLinks) {
Preconditions.checkNotNull(chainLinks, "chainLinks == null!");
int result = 0;
int successiveConditionalCount = 0;
for (ChainLink chainLink : chainLinks) {
if (chainLink instanceof Command && ((Command) chainLink).isConditional()) {
successiveConditionalCount++;
} else {
result = Math.max(result, successiveConditionalCount);
successiveConditionalCount = 0;
}
}
result = Math.max(result, successiveConditionalCount);
return result;
}
/**
* Converts the {@link Position} to a {@link Coordinate3D} using the {@link Orientation3D}. The
* conversion is done along the Axis x → a and y → b.
* The resulting coordinate will have the value 0 for the c axis of the orientation.
*
* @param pos to convert
* @param orientation along which to perform the conversion
* @return a {@link Coordinate3D} representing the given {@link Position}
*/
public static Coordinate3D toCoordinate(Position pos, Orientation3D orientation) {
Coordinate3D xDir = orientation.getA().toCoordinate();
Coordinate3D yDir = orientation.getB().toCoordinate();
Coordinate3D coord = xDir.mult(pos.getX()).plus(yDir.mult(pos.getY()));
return coord;
}
/**
* Converts the {@link Coordinate3D} to a {@link Position} using the {@link Orientation3D}. The
* conversion is done along the Axis a → x and b → y.
*
* @param coord to convert
* @param orientation along which to perform the conversion
* @return a {@link Position} representing the given {@link Coordinate3D}
*/
public static Position toPosition(Coordinate3D coord, Orientation3D orientation) {
Direction3D a = orientation.getA();
Direction3D b = orientation.getB();
int x = coord.get(a.getAxis());
int y = coord.get(b.getAxis());
return Position.at(x, y);
}
/**
* @param cp current position
* @param np next position
* @param orientation along which to perform the conversion
* @return the direction from the current position to the next position
* @throws IllegalArgumentException if the two positions are not next to each other
*/
protected static Direction3D getDirection(Position cp, Position np, Orientation3D orientation)
throws IllegalArgumentException {
// current coordinate
Coordinate3D cc = toCoordinate(cp, orientation);
// next coordinate
Coordinate3D nc = toCoordinate(np, orientation);
return Direction3D.valueOf(nc.minus(cc));
}
public static boolean isTransmitter(MplBlock block) {
return block instanceof Transmitter;
}
public static boolean isTransmitter(ChainLink chainLink) {
return chainLink instanceof MplSkip;
}
public static boolean isReceiver(MplBlock block) {
if (block instanceof CommandBlock) {
CommandBlock commandBlock = (CommandBlock) block;
return isReceiver(commandBlock.toCommand());
} else {
return false;
}
}
public static boolean isReceiver(ChainLink chainLink) {
if (chainLink instanceof Command) {
Command command = (Command) chainLink;
return command.getMode() != Mode.CHAIN;
} else {
return false;
}
}
public static ChainLinkType getType(ChainLink chainLink) {
if (isTransmitter(chainLink)) {
return ChainLinkType.TRANSMITTER;
} else if (isReceiver(chainLink)) {
return ChainLinkType.RECEIVER;
} else if (chainLink instanceof Command) {
Command command = (Command) chainLink;
if (command.isConditional()) {
return ChainLinkType.CONDITIONAL;
}
}
return ChainLinkType.NORMAL;
}
protected static Chain toChainLinkChain(List chainLinks) {
ArrayList chainLinkTypes = new ArrayList(chainLinks.size());
for (ChainLink chainLink : chainLinks) {
chainLinkTypes.add(getType(chainLink));
}
// add 1 normal ChainLink to the end (1 block air must be at the end in order to prevent chain
// looping)
chainLinkTypes.add(ChainLinkType.NORMAL);
return Chain.of(chainLinkTypes.toArray(new ChainLinkType[0]));
}
protected static Directions newTemplate(int a) {
return $(EAST.repeat(a - 1), NORTH, WEST.repeat(a - 1), NORTH).repeat();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy