diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitCommandSender.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitCommandSender.java index b82dfe458a..2d4ea70cf2 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitCommandSender.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitCommandSender.java @@ -26,7 +26,7 @@ import com.sk89q.worldedit.internal.cui.CUIEvent; import com.sk89q.worldedit.session.SessionKey; import com.sk89q.worldedit.util.auth.AuthorizationException; -import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.util.formatting.text.adapter.bukkit.TextAdapter; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -94,7 +94,7 @@ public void printError(String msg) { } @Override - public void print(TextComponent component) { + public void print(Component component) { TextAdapter.sendComponent(sender, component); } diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java index 71941a69dd..12d7551de7 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitPlayer.java @@ -31,6 +31,7 @@ import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.session.SessionKey; import com.sk89q.worldedit.util.HandSide; +import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockStateHolder; @@ -127,7 +128,7 @@ public void printError(String msg) { } @Override - public void print(TextComponent component) { + public void print(Component component) { TextAdapter.sendComponent(player, component); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java index f6aad8a0db..b751dfefe9 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java @@ -19,6 +19,8 @@ package com.sk89q.worldedit.command; +import static com.sk89q.worldedit.command.util.Logging.LogMode.REGION; + import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.WorldEdit; @@ -42,6 +44,8 @@ import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.Regions; import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.util.formatting.component.PaginationBox; +import com.sk89q.worldedit.util.formatting.text.TextComponent; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.biome.BiomeData; import com.sk89q.worldedit.world.biome.BiomeType; @@ -51,11 +55,10 @@ import org.enginehub.piston.annotation.param.Arg; import org.enginehub.piston.annotation.param.Switch; -import java.util.Collection; import java.util.HashSet; +import java.util.List; import java.util.Set; - -import static com.sk89q.worldedit.command.util.Logging.LogMode.REGION; +import java.util.stream.Collectors; /** * Implements biome-related commands such as "/biomelist". @@ -76,38 +79,15 @@ public BiomeCommands() { ) @CommandPermissions("worldedit.biome.list") public void biomeList(Player player, - @Arg(desc = "Page number.", def = "0") + @Arg(desc = "Page number.", def = "1") int page) throws WorldEditException { - int offset; - int count = 0; - - if (page < 2) { - page = 1; - offset = 0; - } else { - offset = (page - 1) * 19; - } - BiomeRegistry biomeRegistry = WorldEdit.getInstance().getPlatformManager() .queryCapability(Capability.GAME_HOOKS).getRegistries().getBiomeRegistry(); - Collection biomes = BiomeType.REGISTRY.values(); - int totalPages = biomes.size() / 19 + 1; - player.print("Available Biomes (page " + page + "/" + totalPages + ") :"); - for (BiomeType biome : biomes) { - if (offset > 0) { - offset--; - } else { - BiomeData data = biomeRegistry.getData(biome); - if (data != null) { - player.print(" " + data.getName()); - if (++count == 19) { - break; - } - } else { - player.print(" "); - } - } - } + List biomes = BiomeType.REGISTRY.values().stream().map(biomeRegistry::getData).collect(Collectors.toList()); + + PaginationBox paginationBox = new PaginationBox("Available Biomes", "/biomelist %page%"); + paginationBox.setComponents(biomes.stream().map(biome -> TextComponent.of(biome.getName())).collect(Collectors.toList())); + player.print(paginationBox.create(page)); } @Command( diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ChunkCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ChunkCommands.java index 8f9dc6ceb1..5dcce16997 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ChunkCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ChunkCommands.java @@ -33,15 +33,19 @@ import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.MathUtils; import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.util.formatting.component.PaginationBox; +import com.sk89q.worldedit.util.formatting.text.TextComponent; import com.sk89q.worldedit.world.storage.LegacyChunkStore; import com.sk89q.worldedit.world.storage.McRegionChunkStore; import org.enginehub.piston.annotation.Command; import org.enginehub.piston.annotation.CommandContainer; +import org.enginehub.piston.annotation.param.Arg; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.util.Set; +import java.util.stream.Collectors; /** * Commands for working with chunks. @@ -82,12 +86,13 @@ public void chunkInfo(Player player) throws WorldEditException { desc = "List chunks that your selection includes" ) @CommandPermissions("worldedit.listchunks") - public void listChunks(Player player, LocalSession session) throws WorldEditException { + public void listChunks(Player player, LocalSession session, + @Arg(desc = "Page number.", def = "1") int page) throws WorldEditException { Set chunks = session.getSelection(player.getWorld()).getChunks(); - for (BlockVector2 chunk : chunks) { - player.print(LegacyChunkStore.getFilename(chunk)); - } + PaginationBox paginationBox = new PaginationBox("Selected Chunks", "/listchunks %page%"); + paginationBox.setComponents(chunks.stream().map(chunk -> TextComponent.of(chunk.toString())).collect(Collectors.toList())); + player.print(paginationBox.create(page)); } @Command( diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Actor.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Actor.java index 45160de669..319c9cb02d 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Actor.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/Actor.java @@ -23,6 +23,7 @@ import com.sk89q.worldedit.session.SessionOwner; import com.sk89q.worldedit.util.Identifiable; import com.sk89q.worldedit.util.auth.Subject; +import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.util.formatting.text.TextComponent; import java.io.File; @@ -77,11 +78,11 @@ default String getDisplayName() { void printError(String msg); /** - * Print a {@link TextComponent}. + * Print a {@link Component}. * * @param component The component to print */ - void print(TextComponent component); + void print(Component component); /** * Returns true if the actor can destroy bedrock. diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlayerProxy.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlayerProxy.java index 2159a2fce8..8ecf3cc107 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlayerProxy.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlayerProxy.java @@ -31,10 +31,10 @@ import com.sk89q.worldedit.session.SessionKey; import com.sk89q.worldedit.util.HandSide; import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.gamemode.GameMode; -import com.sk89q.worldedit.util.formatting.text.TextComponent; import java.util.UUID; @@ -134,7 +134,7 @@ public void printError(String msg) { } @Override - public void print(TextComponent component) { + public void print(Component component) { basePlayer.print(component); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/InvalidComponentException.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/InvalidComponentException.java new file mode 100644 index 0000000000..bcdfb7f102 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/InvalidComponentException.java @@ -0,0 +1,29 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * 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 . + */ + +package com.sk89q.worldedit.util.formatting.component; + +import com.sk89q.worldedit.WorldEditException; + +public class InvalidComponentException extends WorldEditException { + + public InvalidComponentException(String message) { + super(message); + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/MessageBox.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/MessageBox.java index 6828589bc6..5499435416 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/MessageBox.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/MessageBox.java @@ -32,7 +32,7 @@ public class MessageBox extends TextComponentProducer { private static final int GUARANTEED_NO_WRAP_CHAT_PAGE_WIDTH = 47; - private final TextComponentProducer contents; + private TextComponentProducer contents; /** * Create a new box. diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/PaginationBox.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/PaginationBox.java new file mode 100644 index 0000000000..fecdac40ce --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/PaginationBox.java @@ -0,0 +1,119 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * 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 . + */ + +package com.sk89q.worldedit.util.formatting.component; + +import com.sk89q.worldedit.util.formatting.text.Component; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.util.formatting.text.event.ClickEvent; +import com.sk89q.worldedit.util.formatting.text.event.HoverEvent; +import com.sk89q.worldedit.util.formatting.text.format.TextColor; + +import java.util.List; + +public class PaginationBox extends MessageBox { + + public static final int IDEAL_ROWS_FOR_PLAYER = 8; + + private String pageCommand; + private List components; + private int componentsPerPage = IDEAL_ROWS_FOR_PLAYER; + + /** + * Creates a Paginated component + * + * @param title The title + */ + public PaginationBox(String title) { + this(title, null); + } + + /** + * Sets the components to create pages for. + * + * @param components The components + */ + public void setComponents(List components) { + this.components = components; + } + + public void setComponentsPerPage(int componentsPerPage) { + this.componentsPerPage = componentsPerPage; + } + + /** + * Creates a Paginated component + * + * @param title The title + * @param pageCommand The command to run to switch page, with %page% representing page number + */ + public PaginationBox(String title, String pageCommand) { + super(title, new TextComponentProducer()); + + if (pageCommand != null && !pageCommand.contains("%page%")) { + throw new IllegalArgumentException("pageCommand must contain %page% if provided."); + } + this.pageCommand = pageCommand; + } + + public TextComponent create(int page) throws InvalidComponentException { + if (components == null) { + throw new IllegalStateException("You must provide components before creating."); + } + if (page == 1 && components.isEmpty()) { + return getContents().reset().append("There's nothing to see here").create(); + } + int pageCount = (int) Math.ceil(components.size() / (double) componentsPerPage); + if (page < 1 || page > pageCount) { + throw new InvalidComponentException("Invalid page number."); + } + getContents().reset(); + for (int i = (page - 1) * componentsPerPage; i < Math.min(page * componentsPerPage, components.size()); i++) { + getContents().append(components.get(i)).newline(); + } + TextComponent pageNumberComponent = TextComponent.of("Page ", TextColor.YELLOW) + .append(TextComponent.of(String.valueOf(page), TextColor.GOLD)) + .append(TextComponent.of(" of ")) + .append(TextComponent.of(String.valueOf(pageCount), TextColor.GOLD)); + + if (pageCommand != null) { + if (page > 1) { + TextComponent prevComponent = TextComponent.of("<<< ", TextColor.GOLD) + .clickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, pageCommand.replace("%page%", String.valueOf(page - 1)))) + .hoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextComponent.of("Click to navigate"))); + getContents().append(prevComponent); + } + getContents().append(pageNumberComponent); + if (page < pageCount) { + TextComponent nextComponent = TextComponent.of(" >>>", TextColor.GOLD) + .clickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, pageCommand.replace("%page%", String.valueOf(page + 1)))) + .hoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextComponent.of("Click to navigate"))); + getContents().append(nextComponent); + } + } else { + getContents().append(pageNumberComponent); + } + return TextComponent.of("").append(Component.newline()).append(super.create()); + } + + @Override + public TextComponent create() { + throw new IllegalStateException("Pagination components must be created with a page"); + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/TextComponentProducer.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/TextComponentProducer.java index 32a9f8d30f..fed34ea239 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/TextComponentProducer.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/formatting/component/TextComponentProducer.java @@ -62,7 +62,22 @@ public TextComponentProducer newline() { return this; } + /** + * Create a TextComponent from this producer. + * + * @return The component + */ public TextComponent create() { return builder.build(); } + + /** + * Reset the producer to a clean slate. + * + * @return The producer, for chaining + */ + public TextComponentProducer reset() { + builder = TextComponent.builder().content(""); + return this; + } } diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlayer.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlayer.java index edc5275ab4..c478934c53 100644 --- a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlayer.java +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlayer.java @@ -31,7 +31,7 @@ import com.sk89q.worldedit.session.SessionKey; import com.sk89q.worldedit.util.HandSide; import com.sk89q.worldedit.util.Location; -import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.util.formatting.text.serializer.gson.GsonComponentSerializer; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockStateHolder; @@ -143,7 +143,7 @@ public void printError(String msg) { } @Override - public void print(TextComponent component) { + public void print(Component component) { this.player.sendMessage(ITextComponent.Serializer.fromJson(GsonComponentSerializer.INSTANCE.serialize(component))); } diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeCommandSender.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeCommandSender.java index e44146b3c0..e6eac1f838 100644 --- a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeCommandSender.java +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeCommandSender.java @@ -26,7 +26,7 @@ import com.sk89q.worldedit.internal.cui.CUIEvent; import com.sk89q.worldedit.session.SessionKey; import com.sk89q.worldedit.util.auth.AuthorizationException; -import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.util.formatting.text.adapter.spongeapi.TextAdapter; import org.spongepowered.api.command.CommandSource; import org.spongepowered.api.entity.living.player.Player; @@ -92,7 +92,7 @@ public void printError(String msg) { } @Override - public void print(TextComponent component) { + public void print(Component component) { TextAdapter.sendComponent(sender, component); } diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongePlayer.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongePlayer.java index 099780b3d4..82723fe935 100644 --- a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongePlayer.java +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongePlayer.java @@ -31,13 +31,12 @@ import com.sk89q.worldedit.session.SessionKey; import com.sk89q.worldedit.util.HandSide; import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.util.formatting.text.Component; +import com.sk89q.worldedit.util.formatting.text.adapter.spongeapi.TextAdapter; import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.gamemode.GameMode; import com.sk89q.worldedit.world.gamemode.GameModes; import com.sk89q.worldedit.world.item.ItemTypes; - -import com.sk89q.worldedit.util.formatting.text.TextComponent; -import com.sk89q.worldedit.util.formatting.text.adapter.spongeapi.TextAdapter; import org.spongepowered.api.Sponge; import org.spongepowered.api.data.type.HandTypes; import org.spongepowered.api.entity.living.player.Player; @@ -152,7 +151,7 @@ public void printError(String msg) { } @Override - public void print(TextComponent component) { + public void print(Component component) { TextAdapter.sendComponent(player, component); }