diff --git a/src/main/java/fr/themode/demo/PlayerInit.java b/src/main/java/fr/themode/demo/PlayerInit.java
index e1f688c2e..583b94199 100644
--- a/src/main/java/fr/themode/demo/PlayerInit.java
+++ b/src/main/java/fr/themode/demo/PlayerInit.java
@@ -7,9 +7,7 @@ import net.minestom.server.advancements.FrameType;
import net.minestom.server.benchmark.BenchmarkManager;
import net.minestom.server.benchmark.ThreadResult;
import net.minestom.server.chat.ChatColor;
-import net.minestom.server.chat.ChatHoverEvent;
import net.minestom.server.chat.ColoredText;
-import net.minestom.server.chat.RichMessage;
import net.minestom.server.entity.*;
import net.minestom.server.entity.damage.DamageType;
import net.minestom.server.event.entity.EntityAttackEvent;
@@ -335,12 +333,6 @@ public class PlayerInit {
WorldBorder worldBorder = instance.getWorldBorder();
worldBorder.setDiameter(30);
- RichMessage richMessage = RichMessage.of(ColoredText.of(ChatColor.RED + "test item"));
- richMessage.setHoverEvent(ChatHoverEvent.showEntity(player));
- richMessage.setInsertion("Test Insert");
- System.out.println(richMessage.toString());
- player.sendMessage(richMessage);
-
//EntityBoat entityBoat = new EntityBoat(player.getPosition());
//entityBoat.setInstance(player.getInstance());
//entityBoat.addPassenger(player);
diff --git a/src/main/java/fr/themode/demo/commands/SimpleCommand.java b/src/main/java/fr/themode/demo/commands/SimpleCommand.java
index fb15b9394..94b9facb4 100644
--- a/src/main/java/fr/themode/demo/commands/SimpleCommand.java
+++ b/src/main/java/fr/themode/demo/commands/SimpleCommand.java
@@ -10,8 +10,6 @@ import net.minestom.server.command.CommandSender;
import net.minestom.server.entity.Player;
import net.minestom.server.item.Material;
-import java.util.Arrays;
-
public class SimpleCommand implements CommandProcessor {
@Override
public String getCommandName() {
@@ -20,7 +18,7 @@ public class SimpleCommand implements CommandProcessor {
@Override
public String[] getAliases() {
- return new String[0];
+ return new String[]{"alias"};
}
@Override
@@ -66,8 +64,8 @@ public class SimpleCommand implements CommandProcessor {
final Notification notification = new Notification(ColoredText.of(ChatColor.BRIGHT_GREEN + "Welcome to Minestom!"),
FrameType.TASK, Material.APPLE);
- NotificationCenter.send(notification, Arrays.asList(player));
- NotificationCenter.send(notification, Arrays.asList(player));
+ NotificationCenter.send(notification, player);
+ NotificationCenter.send(notification, player);
return true;
}
@@ -76,4 +74,9 @@ public class SimpleCommand implements CommandProcessor {
public boolean hasAccess(Player player) {
return true;
}
+
+ @Override
+ public String[] onWrite(String text) {
+ return new String[]{"Complete1", "Complete2"};
+ }
}
diff --git a/src/main/java/net/minestom/server/command/CommandManager.java b/src/main/java/net/minestom/server/command/CommandManager.java
index 930fdd3a7..91386df48 100644
--- a/src/main/java/net/minestom/server/command/CommandManager.java
+++ b/src/main/java/net/minestom/server/command/CommandManager.java
@@ -27,8 +27,9 @@ import java.util.function.Consumer;
public class CommandManager {
+ public static final String COMMAND_PREFIX = "/";
+
private boolean running;
- private String commandPrefix = "/";
private ConsoleSender consoleSender = new ConsoleSender();
@@ -43,9 +44,9 @@ public class CommandManager {
while (running) {
if (scanner.hasNext()) {
String command = scanner.nextLine();
- if (!command.startsWith(commandPrefix))
+ if (!command.startsWith(COMMAND_PREFIX))
continue;
- command = command.replaceFirst(commandPrefix, "");
+ command = command.replaceFirst(COMMAND_PREFIX, "");
execute(consoleSender, command);
}
}
@@ -79,6 +80,23 @@ public class CommandManager {
*/
public void register(CommandProcessor commandProcessor) {
this.commandProcessorMap.put(commandProcessor.getCommandName().toLowerCase(), commandProcessor);
+ // Register aliases
+ final String[] aliases = commandProcessor.getAliases();
+ if (aliases != null && aliases.length > 0) {
+ for (String alias : aliases) {
+ this.commandProcessorMap.put(alias.toLowerCase(), commandProcessor);
+ }
+ }
+ }
+
+ /**
+ * Get the command register by {@link #register(CommandProcessor)}
+ *
+ * @param commandName the command name
+ * @return the command associated with the name, null if not any
+ */
+ public CommandProcessor getCommandProcessor(String commandName) {
+ return commandProcessorMap.get(commandName.toLowerCase());
}
/**
@@ -86,7 +104,7 @@ public class CommandManager {
*
* @param sender the sender of the command
* @param command the raw command string (without the command prefix)
- * @return
+ * @return true if the command hadn't been cancelled and has been successful
*/
public boolean execute(CommandSender sender, String command) {
Check.notNull(sender, "Source cannot be null");
@@ -124,26 +142,6 @@ public class CommandManager {
}
}
- /**
- * Get the current command prefix (what should always be before the command name, ie: '/')
- *
- * @return the command prefix
- */
- public String getCommandPrefix() {
- return commandPrefix;
- }
-
- /**
- * Change the command prefix
- *
- * This field can be changed half-way, but the client auto-completion still expect the '/' char
- *
- * @param commandPrefix the new command prefix
- */
- public void setCommandPrefix(String commandPrefix) {
- this.commandPrefix = commandPrefix;
- }
-
/**
* Get the console sender (which is used as a {@link CommandSender})
*
@@ -219,12 +217,23 @@ public class CommandManager {
}
for (String simpleCommand : simpleCommands) {
- // TODO server suggestion
+ // Server suggestion (ask_server)
+ {
+ DeclareCommandsPacket.Node tabNode = new DeclareCommandsPacket.Node();
+ tabNode.flags = getFlag(NodeType.ARGUMENT, true, true, true);
+ tabNode.name = "tab_completion";
+ tabNode.parser = "brigadier:string";
+ tabNode.properties = packetWriter -> packetWriter.writeVarInt(2); // Greedy phrase
+ tabNode.children = new int[0];
+ tabNode.suggestionsType = "minecraft:ask_server";
+
+ nodes.add(tabNode);
+ }
+
DeclareCommandsPacket.Node literalNode = new DeclareCommandsPacket.Node();
literalNode.flags = getFlag(NodeType.LITERAL, true, false, false);
literalNode.name = simpleCommand;
- literalNode.children = new int[0];
- //literalNode.suggestionsType = "minecraft:ask_server";
+ literalNode.children = new int[]{nodes.size() - 1};
rootChildren.add(nodes.size());
nodes.add(literalNode);
@@ -272,7 +281,7 @@ public class CommandManager {
final boolean isLast = i == arguments.length - 1;
- List argumentNodes = toNodes(argument, isLast);
+ final List argumentNodes = toNodes(argument, isLast);
for (DeclareCommandsPacket.Node node : argumentNodes) {
final int childId = nodes.size();
@@ -501,11 +510,11 @@ public class CommandManager {
byte result = (byte) type.mask;
if (executable) {
- result |= 0x4;
+ result |= 0x04;
}
if (redirect) {
- result |= 0x8;
+ result |= 0x08;
}
if (suggestionType) {
diff --git a/src/main/java/net/minestom/server/command/CommandProcessor.java b/src/main/java/net/minestom/server/command/CommandProcessor.java
index c7caab8c2..e32e69d5a 100644
--- a/src/main/java/net/minestom/server/command/CommandProcessor.java
+++ b/src/main/java/net/minestom/server/command/CommandProcessor.java
@@ -43,4 +43,14 @@ public interface CommandProcessor {
* @return true if the player has access to the command, false otherwise
*/
boolean hasAccess(Player player);
+
+ /**
+ * Allow for tab auto completion, this is called everytime the player press a key in the chat
+ *
+ * @param text the whole player text
+ * @return the array containing all the suggestion for the current arg (split " ")
+ */
+ default String[] onWrite(String text) {
+ return null;
+ }
}
diff --git a/src/main/java/net/minestom/server/entity/LivingEntity.java b/src/main/java/net/minestom/server/entity/LivingEntity.java
index a66eb5f26..bafc7ba4c 100644
--- a/src/main/java/net/minestom/server/entity/LivingEntity.java
+++ b/src/main/java/net/minestom/server/entity/LivingEntity.java
@@ -233,7 +233,7 @@ public abstract class LivingEntity extends Entity implements EquipmentHandler {
return false;
}
- EntityDamageEvent entityDamageEvent = new EntityDamageEvent(type, value, this);
+ EntityDamageEvent entityDamageEvent = new EntityDamageEvent(this, type, value);
callCancellableEvent(EntityDamageEvent.class, entityDamageEvent, () -> {
float damage = entityDamageEvent.getDamage();
diff --git a/src/main/java/net/minestom/server/event/entity/EntityDamageEvent.java b/src/main/java/net/minestom/server/event/entity/EntityDamageEvent.java
index c675327f5..189917dff 100644
--- a/src/main/java/net/minestom/server/event/entity/EntityDamageEvent.java
+++ b/src/main/java/net/minestom/server/event/entity/EntityDamageEvent.java
@@ -1,6 +1,5 @@
package net.minestom.server.event.entity;
-import net.minestom.server.entity.Entity;
import net.minestom.server.entity.LivingEntity;
import net.minestom.server.entity.damage.DamageType;
import net.minestom.server.event.CancellableEvent;
@@ -10,17 +9,28 @@ import net.minestom.server.event.CancellableEvent;
*/
public class EntityDamageEvent extends CancellableEvent {
- private DamageType damageType;
+ private final LivingEntity entity;
+ private final DamageType damageType;
private float damage;
- private LivingEntity entity;
- public EntityDamageEvent(DamageType damageType, float damage, LivingEntity entity) {
+ public EntityDamageEvent(LivingEntity entity, DamageType damageType, float damage) {
+ this.entity = entity;
this.damageType = damageType;
this.damage = damage;
- this.entity = entity;
}
/**
+ * Get the damaged entity
+ *
+ * @return the damaged entity
+ */
+ public LivingEntity getEntity() {
+ return entity;
+ }
+
+ /**
+ * Get the damage type
+ *
* @return the damage type
*/
public DamageType getDamageType() {
@@ -28,6 +38,8 @@ public class EntityDamageEvent extends CancellableEvent {
}
/**
+ * Get the damage amount
+ *
* @return the damage amount
*/
public float getDamage() {
@@ -35,13 +47,11 @@ public class EntityDamageEvent extends CancellableEvent {
}
/**
+ * Change the damage amount
+ *
* @param damage the new damage amount
*/
public void setDamage(float damage) {
this.damage = damage;
}
-
- public LivingEntity getEntity() {
- return entity;
- }
}
diff --git a/src/main/java/net/minestom/server/listener/ChatMessageListener.java b/src/main/java/net/minestom/server/listener/ChatMessageListener.java
index 2b58fb1cf..470b09767 100644
--- a/src/main/java/net/minestom/server/listener/ChatMessageListener.java
+++ b/src/main/java/net/minestom/server/listener/ChatMessageListener.java
@@ -24,7 +24,7 @@ public class ChatMessageListener {
public static void listener(ClientChatMessagePacket packet, Player player) {
String message = packet.message;
- String cmdPrefix = COMMAND_MANAGER.getCommandPrefix();
+ final String cmdPrefix = CommandManager.COMMAND_PREFIX;
if (message.startsWith(cmdPrefix)) {
// The message is a command
message = message.replaceFirst(cmdPrefix, "");
diff --git a/src/main/java/net/minestom/server/listener/TabCompleteListener.java b/src/main/java/net/minestom/server/listener/TabCompleteListener.java
index 57c21e265..46856580c 100644
--- a/src/main/java/net/minestom/server/listener/TabCompleteListener.java
+++ b/src/main/java/net/minestom/server/listener/TabCompleteListener.java
@@ -1,13 +1,63 @@
package net.minestom.server.listener;
+import net.minestom.server.MinecraftServer;
+import net.minestom.server.command.CommandManager;
+import net.minestom.server.command.CommandProcessor;
import net.minestom.server.entity.Player;
import net.minestom.server.network.packet.client.play.ClientTabCompletePacket;
+import net.minestom.server.network.packet.server.play.TabCompletePacket;
+
+import java.util.regex.Pattern;
public class TabCompleteListener {
+ private static final CommandManager COMMAND_MANAGER = MinecraftServer.getCommandManager();
+
public static void listener(ClientTabCompletePacket packet, Player player) {
- // TODO when is it called?
- System.out.println("text: " + packet.text);
+ final String text = packet.text;
+
+ final String[] split = packet.text.split(Pattern.quote(" "));
+
+ final String commandName = split[0].replaceFirst(CommandManager.COMMAND_PREFIX, "");
+
+ // Tab complete for CommandProcessor
+ final CommandProcessor commandProcessor = COMMAND_MANAGER.getCommandProcessor(commandName);
+ if (commandProcessor != null) {
+ final boolean endSpace = text.endsWith(" ");
+
+ int start;
+
+ if (endSpace) {
+ start = text.length();
+ } else {
+ final String lastArg = split[split.length - 1];
+ start = text.indexOf(lastArg);
+ }
+
+ final String[] matches = commandProcessor.onWrite(text);
+
+ if (matches != null && matches.length > 0) {
+ TabCompletePacket tabCompletePacket = new TabCompletePacket();
+ tabCompletePacket.transactionId = packet.transactionId;
+ tabCompletePacket.start = start;
+ tabCompletePacket.length = 20;
+
+ TabCompletePacket.Match[] matchesArray = new TabCompletePacket.Match[matches.length];
+ for (int i = 0; i < matchesArray.length; i++) {
+ TabCompletePacket.Match match = new TabCompletePacket.Match();
+ match.match = matches[i];
+ matchesArray[i] = match;
+ }
+
+ tabCompletePacket.matches = matchesArray;
+
+ player.getPlayerConnection().sendPacket(tabCompletePacket);
+ }
+ }
+
+ // TODO tab complete for Command (TabArgument)
+
+
}
diff --git a/src/main/java/net/minestom/server/network/packet/server/play/TabCompletePacket.java b/src/main/java/net/minestom/server/network/packet/server/play/TabCompletePacket.java
index 38f808d2d..4dcc96b3c 100644
--- a/src/main/java/net/minestom/server/network/packet/server/play/TabCompletePacket.java
+++ b/src/main/java/net/minestom/server/network/packet/server/play/TabCompletePacket.java
@@ -1,5 +1,6 @@
package net.minestom.server.network.packet.server.play;
+import net.minestom.server.chat.ColoredText;
import net.minestom.server.network.packet.PacketWriter;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
@@ -22,7 +23,7 @@ public class TabCompletePacket implements ServerPacket {
writer.writeSizedString(match.match);
writer.writeBoolean(match.hasTooltip);
if (match.hasTooltip)
- writer.writeSizedString(match.tooltip);
+ writer.writeSizedString(match.tooltip.toString());
}
}
@@ -34,7 +35,7 @@ public class TabCompletePacket implements ServerPacket {
public static class Match {
public String match;
public boolean hasTooltip;
- public String tooltip; // Chat
+ public ColoredText tooltip;
}
}