Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor commands #14

Draft
wants to merge 11 commits into
base: master
Choose a base branch
from
35 changes: 32 additions & 3 deletions src/main/java/de/btegermany/terraplusminus/Terraplusminus.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@
import de.btegermany.terraplusminus.utils.FileBuilder;
import de.btegermany.terraplusminus.utils.PlayerHashMapManagement;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.command.*;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Entity;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.world.WorldInitEvent;
Expand All @@ -21,6 +24,9 @@

import java.io.*;
import java.util.logging.Level;
import java.util.logging.Logger;

import static java.lang.String.format;

public final class Terraplusminus extends JavaPlugin implements Listener {
public static FileConfiguration config;
Expand Down Expand Up @@ -76,9 +82,9 @@ public void onEnable() {
// --------------------------

// Registering commands
getCommand("tpll").setExecutor(new TpllCommand());
getCommand("where").setExecutor(new WhereCommand());
getCommand("offset").setExecutor(new OffsetCommand());
this.registerCommand("tpll", new TpllCommand());
this.registerCommand("where", new WhereCommand());
this.registerCommand("offset", new OffsetCommand());
// --------------------------

Bukkit.getLogger().log(Level.INFO, "[T+-] Terraplusminus successfully enabled");
Expand Down Expand Up @@ -190,4 +196,27 @@ private void updateConfig() {
}
}

private void registerCommand(String commandName, CommandExecutor executor) {
Logger logger = this.getLogger();
PluginCommand command = this.getCommand(commandName);
if (command == null) {
logger.warning(format("Could not register command %s. Command is unknown to bukkit, has it been properly registered in plugin.yml?", commandName));
return;
}
command.setExecutor(executor);
logger.fine(format("Registered command executor for /%s command", commandName));
if (executor instanceof TabCompleter) {
command.setTabCompleter((TabCompleter) executor);
logger.fine(format("Registered tab completer for /%s command", commandName));
}
}

public static boolean isTerraWorld(World world) {
return world.getGenerator() instanceof RealWorldGenerator;
}

public static boolean isInTerraWorld(Entity entity) {
return isTerraWorld(entity.getWorld());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package de.btegermany.terraplusminus.commands;

import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

import static java.util.Comparator.comparing;
import static java.util.Objects.requireNonNullElseGet;

/**
* Utility class to help with commands.
*
* @author Smyler
*/
public final class CommandHelper {

/**
* Parses a <a href="https://minecraft.fandom.com/wiki/Target_selectors">command target selector</a> like '@a'.
* Currently supported selectors:
* <ul>
* <li><code>@a</code></li>
* <li><code>@p</code> (doesn't select the sender)</li>
* </ul>
* Target selector arguments are not supported.
* It's a shame that Bukkit does not expose the vanilla command system...
*
* @param sender the {@link CommandSender command sender}
* @param selector the selector string
* @return the collection of {@link Entity entities} that match the selector
*
* @throws InvalidTargetSelectorException if the selector is invalid, either syntactically or in the specific context
*/
public static Collection<Entity> parseTargetSelector(@NotNull CommandSender sender, String selector) throws InvalidTargetSelectorException {
if (selector.startsWith("@") && selector.length() >= 2) {
char selectorChar = selector.charAt(1);
if (selectorChar == 'a') {
return Bukkit.getOnlinePlayers().stream()
.map(p -> (Entity)p)
.collect(Collectors.toList());
} else if (selectorChar == 'p' && sender instanceof Entity) {
Entity entitySender = (Entity) sender;
Location senderLocation = entitySender.getLocation();
return Collections.singleton(
Bukkit.getOnlinePlayers().stream()
.filter(p -> p != sender)
.min(comparing(p -> p.getLocation().distanceSquared(senderLocation)))
.orElseThrow(InvalidTargetSelectorException::new)
);
}
} else {
Player player = Bukkit.getPlayerExact(selector);
if (player == null || !player.isOnline()) {
throw new InvalidTargetSelectorException();
} else {
return List.of(player);
}
}

throw new InvalidTargetSelectorException();

}

/**
* Checks if a collection of entities is comprised solely of a single command sender (which may appear multiple times).
* This is useful for permission checks where one might allow a sender to execute a command on themselves,
* but not on other entities.
*
* @param sender the {@link CommandSender sender} executing the command
* @param targets the {@link Collection collection} of targets
*
* @return whether a {@link CommandSender sender} is the only entity in a {@link Collection collection} of targets
*/
public static boolean senderIsSoleTarget(CommandSender sender, @NotNull Collection<Entity> targets) {
return !targets.stream().allMatch(target -> target == sender);
}

public static String formatTargetName(Entity target) {
return requireNonNullElseGet(target.getCustomName(), target::getName);
}

public static class InvalidTargetSelectorException extends Exception {

}

private CommandHelper() {
throw new IllegalStateException();
}

}
Original file line number Diff line number Diff line change
@@ -1,27 +1,92 @@
package de.btegermany.terraplusminus.commands;

import de.btegermany.terraplusminus.Terraplusminus;
import de.btegermany.terraplusminus.gen.RealWorldGenerator;
import net.buildtheearth.terraminusminus.projection.GeographicProjection;
import net.buildtheearth.terraminusminus.projection.transform.OffsetProjectionTransform;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
import org.bukkit.entity.Player;
import org.bukkit.generator.ChunkGenerator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.List;

import static java.lang.Math.round;
import static java.util.Collections.emptyList;
import static java.util.stream.Collectors.toList;
import static org.bukkit.ChatColor.*;

public class OffsetCommand implements TabExecutor {

public class OffsetCommand implements CommandExecutor {
@Override
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String[] strings) {
if (command.getName().equalsIgnoreCase("offset")) {
Player player = (Player) commandSender;
if (player.hasPermission("t+-.offset")) {
player.sendMessage(Terraplusminus.config.getString("prefix") + "§7Offsets:");
player.sendMessage(Terraplusminus.config.getString("prefix") + "§7 | X: §8" + Terraplusminus.config.getInt("terrain_offset.x"));
player.sendMessage(Terraplusminus.config.getString("prefix") + "§7 | Y: §8" + Terraplusminus.config.getInt("terrain_offset.y"));
player.sendMessage(Terraplusminus.config.getString("prefix") + "§7 | Z: §8" + Terraplusminus.config.getInt("terrain_offset.z"));
} else {
player.sendMessage(Terraplusminus.config.getString("prefix") + "§7No permission for /offset");
return true;
}
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String... arguments) {

String prefix = Terraplusminus.config.getString("prefix");

if (!sender.hasPermission("t+-.offset")) {
sender.sendMessage(Terraplusminus.config.getString("prefix") + GRAY + "No permission for /offset");
return true;
}

World world;
if (arguments.length == 1) {
String worldName = arguments[0];
world = Bukkit.getWorld(worldName);
} else if (arguments.length == 0 && sender instanceof Player) {
Player player = (Player) sender;
world = player.getWorld();
} else {
return false;
}

if (world == null) {
sender.sendMessage(prefix + RED + "No such world");
return true;
}

ChunkGenerator generator = world.getGenerator();
if (!(generator instanceof RealWorldGenerator)) {
sender.sendMessage(prefix + GRAY + world.getName() + RED + " is not a Terra+- world");
return true;
}
RealWorldGenerator realWorldGenerator = (RealWorldGenerator) generator;
GeographicProjection projection = realWorldGenerator.getSettings().projection();

int offsetY = realWorldGenerator.getYOffset();
int offsetX, offsetZ;
if (projection instanceof OffsetProjectionTransform) {
OffsetProjectionTransform transform = (OffsetProjectionTransform) projection;
// We assume there is ony one offset transform, or that it is the only one the user cares about
// We can safely round to int as we are only dealing with blocks at that scale
offsetX = (int) round(transform.dx());
offsetZ = (int) round(transform.dy());
} else {
offsetX = offsetZ = 0;
}

sender.sendMessage(prefix + GRAY + "Offsets for world \"" + DARK_GRAY + world.getName() + GRAY + "\":");
sender.sendMessage(prefix + GRAY + " | X: " + DARK_GRAY + offsetX);
sender.sendMessage(prefix + GRAY + " | Y: " + DARK_GRAY + offsetY);
sender.sendMessage(prefix + GRAY + " | Z: " + DARK_GRAY + offsetZ);
return true;
}

@Nullable
@Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) {
if (args.length > 1 || !sender.hasPermission("t+-.offset")) {
return emptyList();
}

return Bukkit.getWorlds().stream()
.filter(Terraplusminus::isTerraWorld)
.map(World::getName)
.collect(toList());
}

}
Loading