diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..832296bd5 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,108 @@ +name: Build Gradle and Publish Docker image + +on: + push: + branches: + - 'master' + tags: + - 'v*' + pull_request: + branches: + - 'master' + +env: + REGISTRY: ghcr.io + IMAGE_NAME: luckperms-standalone + +jobs: + build-gradle: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: '17' + + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 + + - name: Run build with Gradle wrapper + run: ./gradlew build + + - name: Upload all artifacts + uses: actions/upload-artifact@v3 + with: + name: jars + path: | + bukkit/loader/build/libs/LuckPerms-Bukkit-*.jar + bukkit-legacy/loader/build/libs/LuckPerms-Bukkit-Legacy-*.jar + bungee/loader/build/libs/LuckPerms-Bungee-*.jar + sponge/loader/build/libs/LuckPerms-Sponge-*.jar + nukkit/loader/build/libs/LuckPerms-Nukkit-*.jar + velocity/build/libs/LuckPerms-Velocity-*.jar + fabric/build/libs/LuckPerms-Fabric-*.jar + forge/loader/build/libs/LuckPerms-Forge-*.jar + standalone/loader/build/libs/LuckPerms-Standalone-*.jar + + - name: Upload standalone artifact + uses: actions/upload-artifact@v3 + with: + name: standalone-binary + path: standalone/loader/build/libs/LuckPerms-Standalone-*.jar + + + build-docker: + needs: build-gradle + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Retrieve saved standalone jar artifact + uses: actions/download-artifact@v3 + with: + name: standalone-binary + path: standalone/docker/ + + - name: Display structure of downloaded files + run: ls -R + working-directory: standalone/docker/ + + - name: Set up QEMU + uses: docker/setup-qemu-action@v1 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + + - name: Log in to the Container registry + uses: docker/login-action@v1 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata for Docker + id: meta + uses: docker/metadata-action@v3 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + flavor: | + latest=${{ github.ref == 'refs/heads/master' }} + + - name: Build and push Docker image + uses: docker/build-push-action@v2 + with: + context: standalone/docker/ + platforms: linux/amd64,linux/arm64 + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/api/src/main/java/net/luckperms/api/platform/Platform.java b/api/src/main/java/net/luckperms/api/platform/Platform.java index 92b4fd09f..bc31a34a8 100644 --- a/api/src/main/java/net/luckperms/api/platform/Platform.java +++ b/api/src/main/java/net/luckperms/api/platform/Platform.java @@ -76,7 +76,8 @@ public interface Platform { NUKKIT("Nukkit"), VELOCITY("Velocity"), FABRIC("Fabric"), - FORGE("Forge"); + FORGE("Forge"), + STANDALONE("Standalone"); private final String friendlyName; diff --git a/build.gradle b/build.gradle index 44022515d..d8ac4cb96 100644 --- a/build.gradle +++ b/build.gradle @@ -52,7 +52,8 @@ subprojects { repositories { mavenCentral() - maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } + maven { url 'https://s01.oss.sonatype.org/content/repositories/snapshots/' } + maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' } maven { url 'https://repo.lucko.me/' } maven { url 'https://libraries.minecraft.net/' } } diff --git a/settings.gradle b/settings.gradle index 06b95aad0..b03ef276d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -30,5 +30,8 @@ include ( 'sponge:loader', 'sponge:sponge-service', 'sponge:sponge-service-api8', - 'velocity' + 'velocity', + 'standalone', + 'standalone:loader', + 'standalone:app' ) diff --git a/standalone/app/build.gradle b/standalone/app/build.gradle new file mode 100644 index 000000000..91f342aab --- /dev/null +++ b/standalone/app/build.gradle @@ -0,0 +1,41 @@ +plugins { + id 'net.kyori.blossom' version '1.3.0' + id 'java-library' +} + +dependencies { + implementation project(':api') + + api 'org.apache.logging.log4j:log4j-core:2.17.2' + api 'org.apache.logging.log4j:log4j-slf4j-impl:2.17.2' + api 'net.minecrell:terminalconsoleappender:1.3.0' + api 'org.jline:jline-terminal-jansi:3.20.0' + + api 'com.google.code.gson:gson:2.9.0' + api 'com.google.guava:guava:31.1-jre' + + api('net.kyori:adventure-api:4.11.0') { + exclude(module: 'adventure-bom') + exclude(module: 'checker-qual') + exclude(module: 'annotations') + } + api('net.kyori:adventure-text-serializer-gson:4.11.0') { + exclude(module: 'adventure-bom') + exclude(module: 'adventure-api') + exclude(module: 'gson') + } + api('net.kyori:adventure-text-serializer-legacy:4.11.0') { + exclude(module: 'adventure-bom') + exclude(module: 'adventure-api') + } + api('net.kyori:adventure-text-serializer-plain:4.11.0') { + exclude(module: 'adventure-bom') + exclude(module: 'adventure-api') + } + api('net.kyori:ansi:1.0.0-SNAPSHOT') +} + +blossom { + replaceTokenIn('src/main/java/me/lucko/luckperms/standalone/app/LuckPermsApplication.java') + replaceToken '@version@', project.ext.fullVersion +} \ No newline at end of file diff --git a/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/LuckPermsApplication.java b/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/LuckPermsApplication.java new file mode 100644 index 000000000..a3da8e1e5 --- /dev/null +++ b/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/LuckPermsApplication.java @@ -0,0 +1,118 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.standalone.app; + +import me.lucko.luckperms.standalone.app.integration.CommandExecutor; +import me.lucko.luckperms.standalone.app.integration.DockerCommandSocket; +import me.lucko.luckperms.standalone.app.integration.ShutdownCallback; +import me.lucko.luckperms.standalone.app.integration.TerminalInterface; + +import net.luckperms.api.LuckPerms; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * The LuckPerms standalone application. + */ +public class LuckPermsApplication implements AutoCloseable { + + /** A logger instance */ + public static final Logger LOGGER = LogManager.getLogger(LuckPermsApplication.class); + + /** A callback to shutdown the application via the loader bootstrap. */ + private final ShutdownCallback shutdownCallback; + + /** The instance of the LuckPerms API available within the app */ + private LuckPerms luckPermsApi; + /** A command executor interface to run LuckPerms commands */ + private CommandExecutor commandExecutor; + + /** If the application is running */ + private final AtomicBoolean running = new AtomicBoolean(true); + + /** The docker command socket */ + private DockerCommandSocket dockerCommandSocket; + + public LuckPermsApplication(ShutdownCallback shutdownCallback) { + this.shutdownCallback = shutdownCallback; + } + + /** + * Start the app + */ + public void start(String[] args) { + TerminalInterface terminal = new TerminalInterface(this, this.commandExecutor); + + List arguments = Arrays.asList(args); + if (arguments.contains("--docker")) { + this.dockerCommandSocket = DockerCommandSocket.createAndStart(3000, terminal); + } + + terminal.start(); // blocking + } + + public void requestShutdown() { + this.shutdownCallback.shutdown(); + } + + @Override + public void close() { + this.running.set(false); + + if (this.dockerCommandSocket != null) { + try { + this.dockerCommandSocket.close(); + } catch (IOException e) { + LOGGER.warn(e); + } + } + } + + public AtomicBoolean runningState() { + return this.running; + } + + // called before start() + public void setApi(LuckPerms luckPermsApi) { + this.luckPermsApi = luckPermsApi; + } + + // called before start() + public void setCommandExecutor(CommandExecutor commandExecutor) { + this.commandExecutor = commandExecutor; + } + + public String getVersion() { + return "@version@"; + } + +} diff --git a/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/CommandExecutor.java b/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/CommandExecutor.java new file mode 100644 index 000000000..8cdec14c9 --- /dev/null +++ b/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/CommandExecutor.java @@ -0,0 +1,40 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.standalone.app.integration; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +/** + * Minimal command executor interface. + */ +public interface CommandExecutor { + + CompletableFuture execute(String command); + + List tabComplete(String command); + +} diff --git a/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/DockerCommandSocket.java b/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/DockerCommandSocket.java new file mode 100644 index 000000000..f331d9aa1 --- /dev/null +++ b/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/DockerCommandSocket.java @@ -0,0 +1,93 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.standalone.app.integration; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; +import java.util.function.Consumer; + +/** + * Simple/dumb socket that listens for connections on a given port, + * reads the input to a string, then executes it as a command. + * + * Combined with a small sh/nc program, this makes it easy to execute + * commands against a standalone instance of LP in a Docker container. + */ +public class DockerCommandSocket extends ServerSocket implements Runnable { + private static final Logger LOGGER = LogManager.getLogger(DockerCommandSocket.class); + + public static DockerCommandSocket createAndStart(int port, TerminalInterface terminal) { + DockerCommandSocket socket = null; + + try { + socket = new DockerCommandSocket(port, terminal::runCommand); + + Thread thread = new Thread(socket, "docker-command-socket"); + thread.setDaemon(true); + thread.start(); + + LOGGER.info("Created Docker command socket on port 3000"); + } catch (Exception e) { + LOGGER.error("Error starting docker command socket", e); + } + + return socket; + } + + private final Consumer callback; + + public DockerCommandSocket(int port, Consumer callback) throws IOException { + super(port); + this.callback = callback; + } + + @Override + public void run() { + while (!isClosed()) { + try (Socket socket = accept()) { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()))) { + String cmd; + while ((cmd = reader.readLine()) != null) { + LOGGER.info("Executing command from Docker: " + cmd); + this.callback.accept(cmd); + } + } + } catch (IOException e) { + if (e instanceof SocketException && e.getMessage().equals("Socket closed")) { + return; + } + LOGGER.error("Error processing input from the Docker socket", e); + } + } + } +} diff --git a/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/ShutdownCallback.java b/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/ShutdownCallback.java new file mode 100644 index 000000000..1fcbd0212 --- /dev/null +++ b/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/ShutdownCallback.java @@ -0,0 +1,37 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.standalone.app.integration; + +/** + * Shutdown callback for the whole standalone app. + * + * (in practice this is always implemented by the StandaloneLoader class) + */ +public interface ShutdownCallback { + + void shutdown(); + +} diff --git a/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/SingletonPlayer.java b/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/SingletonPlayer.java new file mode 100644 index 000000000..9e195ffd1 --- /dev/null +++ b/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/SingletonPlayer.java @@ -0,0 +1,58 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.standalone.app.integration; + +import me.lucko.luckperms.standalone.app.LuckPermsApplication; +import me.lucko.luckperms.standalone.app.utils.AnsiUtils; + +import net.kyori.adventure.text.Component; + +import java.util.UUID; + +/** + * Dummy/singleton player class used by the standalone plugin. + * + * In various places (ContextManager, SenderFactory, ..) the platform "player" type is used + * as a generic parameter. This class acts as this type for the standalone plugin. + */ +public class SingletonPlayer { + public static final SingletonPlayer INSTANCE = new SingletonPlayer(); + + private static final UUID UUID = new UUID(0, 0); + + public String getName() { + return "StandaloneUser"; + } + + public UUID getUniqueId() { + return UUID; + } + + public void printStdout(Component component) { + LuckPermsApplication.LOGGER.info(AnsiUtils.format(component)); + } + +} diff --git a/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/TerminalInterface.java b/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/TerminalInterface.java new file mode 100644 index 000000000..f55a94c59 --- /dev/null +++ b/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/integration/TerminalInterface.java @@ -0,0 +1,99 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.standalone.app.integration; + +import me.lucko.luckperms.standalone.app.LuckPermsApplication; + +import net.minecrell.terminalconsole.SimpleTerminalConsole; + +import org.jline.reader.Candidate; +import org.jline.reader.LineReader; +import org.jline.reader.LineReaderBuilder; +import org.jline.reader.ParsedLine; + +import java.util.List; + +/** + * The terminal/console-style interface presented to the user. + */ +public class TerminalInterface extends SimpleTerminalConsole { + private final LuckPermsApplication application; + private final CommandExecutor commandExecutor; + + public TerminalInterface(LuckPermsApplication application, CommandExecutor commandExecutor) { + this.application = application; + this.commandExecutor = commandExecutor; + } + + @Override + protected LineReader buildReader(LineReaderBuilder builder) { + return super.buildReader(builder + .appName("LuckPerms") + .completer(this::completeCommand) + ); + } + + @Override + protected boolean isRunning() { + return this.application.runningState().get(); + } + + @Override + protected void shutdown() { + this.application.requestShutdown(); + } + + @Override + public void runCommand(String command) { + command = stripSlashLp(command); + + if (command.equals("stop") || command.equals("exit")) { + this.application.requestShutdown(); + return; + } + + this.commandExecutor.execute(command); + } + + private void completeCommand(LineReader reader, ParsedLine line, List candidates) { + String cmdLine = stripSlashLp(line.line()); + + for (String suggestion : this.commandExecutor.tabComplete(cmdLine)) { + candidates.add(new Candidate(suggestion)); + } + } + + private static String stripSlashLp(String command) { + if (command.startsWith("/")) { + command = command.substring(1); + } + if (command.startsWith("lp ")) { + command = command.substring(3); + } + return command; + } + +} diff --git a/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/utils/AnsiUtils.java b/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/utils/AnsiUtils.java new file mode 100644 index 000000000..ab2ac5086 --- /dev/null +++ b/standalone/app/src/main/java/me/lucko/luckperms/standalone/app/utils/AnsiUtils.java @@ -0,0 +1,131 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.standalone.app.utils; + +import net.kyori.adventure.key.Key; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.flattener.ComponentFlattener; +import net.kyori.adventure.text.flattener.FlattenerListener; +import net.kyori.adventure.text.format.Style; +import net.kyori.adventure.text.format.TextColor; +import net.kyori.adventure.text.format.TextDecoration; +import net.kyori.ansi.ANSIComponentRenderer; +import net.kyori.ansi.StyleOps; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Range; + +/** + * Utility to format a {@link Component} as an ANSI string. + */ +public final class AnsiUtils { + private AnsiUtils() {} + + public static String format(Component component) { + ANSIComponentRenderer.ToString