├── .gitignore ├── README.md ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src └── main ├── java └── com │ └── james090500 │ └── VelocityGUI │ ├── VelocityGUI.java │ ├── commands │ └── CommandHandler.java │ ├── config │ └── Configs.java │ └── helpers │ ├── ChatControlHelper.java │ ├── InventoryBuilder.java │ ├── InventoryClickHandler.java │ ├── InventoryLauncher.java │ ├── LuckPermsHelper.java │ └── PlaceholderParser.java └── resources └── example.toml /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .gradle/ 3 | run/ 4 | libs/ 5 | build/ 6 | VelocityGUI.iml -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # 📑 VelocityGUI 3 | A Proxy wide GUI for Velocity 4 | **Requires [Protocolize](https://simplixsoft.com/protocolize)** 5 | 6 | ## Permissions 7 | | Permission | Purpose | 8 | |--|--| 9 | | `vgui.admin` | Needed for all `/vgui` commands | 10 | 11 | ## Commands 12 | | Command | Response | 13 | |--|--| 14 | | `/vgui` | Info command | 15 | | `/vgui panel` | Lists all panels | 16 | | `/vgui panel ` | Loads up a specific panel | 17 | | `/vgui reload` | Reloads the panels and config 18 | 19 | ## Config 20 | ```toml 21 | #The Name of the panel 22 | name = "example" 23 | 24 | #The permission needed to open the panel (Can be anything) 25 | perm = "default" 26 | 27 | #The rows in the GUI (Max 9) 28 | rows = 3 29 | 30 | #The GUI Title 31 | title = "&dVelocityGUI" 32 | 33 | #Whats empty slots should be filled with (AIR for empty) 34 | empty = "GREEN_STAINED_GLASS_PANE" 35 | 36 | #Sound when opening the GUI 37 | sound = "ENTITY_ARROW_HIT_PLAYER" 38 | 39 | #The commands to open the gui (/rules /version etc) 40 | commands = [ 41 | "rules", "version", "plugins", "help", "pl"] 42 | 43 | #The Items in the gui 44 | [items] 45 | 46 | #This is in the 13th slot 47 | [items.13] 48 | #The item material 49 | material = "OAK_SIGN" 50 | #The item amount 51 | stack = 1 52 | #The item name 53 | name = "&dVelocityGUI" 54 | #The items lore 55 | lore = [ 56 | "&eA Velociy Side GUI", "&eFor all your servers"] 57 | #Is the item enchanted? 58 | enchanted = true 59 | #Commands to run 60 | commands = [ 61 | "sudo= I love VelocityGUI"] 62 | ``` 63 | 64 | ## Item Commands 65 | | Command | Example | Response | 66 | |--|--|--| 67 | | `open` | `open= rules` | Open another panel | 68 | | `close` | `close` | Closes the current panel | 69 | | `sudo` | `sudo= Chat` `sudo= /command` | Run chat or a command as the player on a server | 70 | | `vsudo` | `vsudo= /command` | Runs a command as the player on the proxy | 71 | | `server` | `server= lobby`| Connects the player to a server 72 | 73 | ## Placeholders 74 | | Placeholder | Value | 75 | |--|--| 76 | | `%username%` | Get players username | 77 | | `%server_name%` | Get players server name | 78 | | `%chatcontrolred_nick%` | Get the players ChatControlRed nick name | 79 | | `%luckperms_meta_{meta}%` | Get luckperms meta value eg `luckperms_meta_home` | 80 | 81 | The only placeholder support currently is for ChatControl Red nick names. You can use `%chatcontrolred_nick%` for this. Open an issue if you'd like others to be added. -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | id 'maven-publish' 4 | 5 | id("io.freefair.lombok") version "8.4" 6 | } 7 | 8 | repositories { 9 | mavenLocal() 10 | maven { 11 | url = uri('https://repo.papermc.io/repository/maven-public/') 12 | } 13 | 14 | maven { 15 | url = uri('https://mvn.exceptionflug.de/repository/exceptionflug-public/') 16 | } 17 | } 18 | 19 | dependencies { 20 | compileOnly("com.velocitypowered:velocity-api:3.2.0-SNAPSHOT") 21 | annotationProcessor("com.velocitypowered:velocity-api:3.2.0-SNAPSHOT") 22 | 23 | compileOnly 'dev.simplix:protocolize-api:2.3.3' 24 | compileOnly 'net.luckperms:api:5.4' 25 | 26 | compileOnly files('libs/VelocityControl-3.11.7-SNAPSHOT.jar') 27 | } 28 | 29 | group = 'com.james090500.VelocityGUI' 30 | version = '1.2.6' 31 | description = 'VelocityGUI' 32 | 33 | tasks.withType(JavaCompile).configureEach { 34 | options.encoding = 'UTF-8' 35 | } 36 | 37 | tasks.withType(Javadoc).configureEach { 38 | options.encoding = 'UTF-8' 39 | } 40 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/james090500/VelocityGUI/8e46661b7e86169301208a975ee1686a9ef3d891/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 147 | # shellcheck disable=SC3045 148 | MAX_FD=$( ulimit -H -n ) || 149 | warn "Could not query maximum file descriptor limit" 150 | esac 151 | case $MAX_FD in #( 152 | '' | soft) :;; #( 153 | *) 154 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 155 | # shellcheck disable=SC3045 156 | ulimit -n "$MAX_FD" || 157 | warn "Could not set maximum file descriptor limit to $MAX_FD" 158 | esac 159 | fi 160 | 161 | # Collect all arguments for the java command, stacking in reverse order: 162 | # * args from the command line 163 | # * the main class name 164 | # * -classpath 165 | # * -D...appname settings 166 | # * --module-path (only if needed) 167 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 168 | 169 | # For Cygwin or MSYS, switch paths to Windows format before running java 170 | if "$cygwin" || "$msys" ; then 171 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 172 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 173 | 174 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 175 | 176 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 177 | for arg do 178 | if 179 | case $arg in #( 180 | -*) false ;; # don't mess with options #( 181 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 182 | [ -e "$t" ] ;; #( 183 | *) false ;; 184 | esac 185 | then 186 | arg=$( cygpath --path --ignore --mixed "$arg" ) 187 | fi 188 | # Roll the args list around exactly as many times as the number of 189 | # args, so each arg winds up back in the position where it started, but 190 | # possibly modified. 191 | # 192 | # NB: a `for` loop captures its iteration list before it begins, so 193 | # changing the positional parameters here affects neither the number of 194 | # iterations, nor the values presented in `arg`. 195 | shift # remove old arg 196 | set -- "$@" "$arg" # push replacement arg 197 | done 198 | fi 199 | 200 | # Collect all arguments for the java command; 201 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 202 | # shell script including quotes and variable substitutions, so put them in 203 | # double quotes to make sure that they get re-expanded; and 204 | # * put everything else in single quotes, so that it's not re-expanded. 205 | 206 | set -- \ 207 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 208 | -classpath "$CLASSPATH" \ 209 | org.gradle.wrapper.GradleWrapperMain \ 210 | "$@" 211 | 212 | # Stop when "xargs" is not available. 213 | if ! command -v xargs >/dev/null 2>&1 214 | then 215 | die "xargs is not available" 216 | fi 217 | 218 | # Use "xargs" to parse quoted args. 219 | # 220 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 221 | # 222 | # In Bash we could simply go: 223 | # 224 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 225 | # set -- "${ARGS[@]}" "$@" 226 | # 227 | # but POSIX shell has neither arrays nor command substitution, so instead we 228 | # post-process each arg (as a line of input to sed) to backslash-escape any 229 | # character that might be a shell metacharacter, then use eval to reverse 230 | # that process (while maintaining the separation between arguments), and wrap 231 | # the whole thing up as a single "set" statement. 232 | # 233 | # This will of course break if any of these variables contains a newline or 234 | # an unmatched quote. 235 | # 236 | 237 | eval "set -- $( 238 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 239 | xargs -n1 | 240 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 241 | tr '\n' ' ' 242 | )" '"$@"' 243 | 244 | exec "$JAVACMD" "$@" 245 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * This file was generated by the Gradle 'init' task. 3 | */ 4 | 5 | rootProject.name = 'VelocityGUI' 6 | -------------------------------------------------------------------------------- /src/main/java/com/james090500/VelocityGUI/VelocityGUI.java: -------------------------------------------------------------------------------- 1 | package com.james090500.VelocityGUI; 2 | 3 | import com.google.inject.Inject; 4 | import com.james090500.VelocityGUI.commands.CommandHandler; 5 | import com.james090500.VelocityGUI.config.Configs; 6 | import com.james090500.VelocityGUI.helpers.InventoryLauncher; 7 | import com.mojang.brigadier.arguments.StringArgumentType; 8 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 9 | import com.mojang.brigadier.builder.RequiredArgumentBuilder; 10 | import com.velocitypowered.api.command.BrigadierCommand; 11 | import com.velocitypowered.api.command.CommandMeta; 12 | import com.velocitypowered.api.command.CommandSource; 13 | import com.velocitypowered.api.command.SimpleCommand; 14 | import com.velocitypowered.api.event.Subscribe; 15 | import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; 16 | import com.velocitypowered.api.plugin.Plugin; 17 | import com.velocitypowered.api.plugin.annotation.DataDirectory; 18 | import com.velocitypowered.api.proxy.Player; 19 | import com.velocitypowered.api.proxy.ProxyServer; 20 | import lombok.Getter; 21 | import org.slf4j.Logger; 22 | 23 | import java.nio.file.Path; 24 | 25 | @Plugin(id = "velocitygui", name = "VelocityGUI", version = "1.2.6", description = "GUIs for the entire Proxy", authors = { "james095000" }) 26 | public class VelocityGUI { 27 | 28 | public final String PREFIX = "&a[VelocityGUI] "; 29 | @Getter private final ProxyServer server; 30 | @Getter private final Logger logger; 31 | @Getter private final Path dataDirectory; 32 | 33 | @Inject 34 | public VelocityGUI(ProxyServer server, Logger logger, @DataDirectory Path dataDirectory) { 35 | this.server = server; 36 | this.logger = logger; 37 | this.dataDirectory = dataDirectory; 38 | } 39 | 40 | @Subscribe 41 | public void onProxyInitialization(ProxyInitializeEvent event) { 42 | Configs.loadConfigs(this); 43 | 44 | //Setup command flow 45 | final CommandHandler handler = new CommandHandler(this); 46 | server.getCommandManager().register(server.getCommandManager().metaBuilder("vgui").build(), new BrigadierCommand( 47 | LiteralArgumentBuilder.literal("vgui").executes(handler::about) 48 | .then(LiteralArgumentBuilder.literal("panel").executes(handler::panel)) 49 | .then(LiteralArgumentBuilder.literal("panel").then(RequiredArgumentBuilder.argument("name", StringArgumentType.word()).executes(handler::panel))) 50 | .then(LiteralArgumentBuilder.literal("reload").requires(source -> source.hasPermission("vgui.admin")).executes(handler::reload)) 51 | )); 52 | 53 | //Register command aliases for panels 54 | Configs.getPanels().forEach((name, panel) -> { 55 | String[] commands = panel.getCommands(); 56 | if(commands == null || commands.length == 0) return; 57 | 58 | CommandMeta.Builder commandBuilder = server.getCommandManager().metaBuilder(commands[0]); 59 | for(String commannd : commands) { 60 | commandBuilder.aliases(commannd); 61 | } 62 | 63 | server.getCommandManager().register(commandBuilder.build(), (SimpleCommand) invocation -> { 64 | new InventoryLauncher(this).execute(panel.getName(), (Player) invocation.source()); 65 | }); 66 | }); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/james090500/VelocityGUI/commands/CommandHandler.java: -------------------------------------------------------------------------------- 1 | package com.james090500.VelocityGUI.commands; 2 | 3 | import com.james090500.VelocityGUI.VelocityGUI; 4 | import com.james090500.VelocityGUI.config.Configs; 5 | import com.james090500.VelocityGUI.helpers.InventoryLauncher; 6 | import com.mojang.brigadier.context.CommandContext; 7 | import com.mojang.brigadier.context.ParsedArgument; 8 | import com.velocitypowered.api.command.CommandSource; 9 | import com.velocitypowered.api.proxy.Player; 10 | import net.kyori.adventure.text.Component; 11 | import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; 12 | 13 | public class CommandHandler { 14 | 15 | private VelocityGUI velocityGUI; 16 | 17 | public CommandHandler(VelocityGUI velocityGUI) { 18 | this.velocityGUI = velocityGUI; 19 | } 20 | 21 | /** 22 | * The command for /vgui panel 23 | * Handles listing panel and passes a valid argument to the InventoryLauncher 24 | * @param commandSourceCommandContext 25 | * @return 26 | */ 27 | public int panel(CommandContext commandSourceCommandContext) { 28 | if(!(commandSourceCommandContext.getSource() instanceof Player)) { 29 | Component error = LegacyComponentSerializer.legacyAmpersand().deserialize(velocityGUI.PREFIX + "Only a player can run these commands"); 30 | commandSourceCommandContext.getSource().sendMessage(error); 31 | return 0; 32 | } 33 | 34 | Player player = (Player) commandSourceCommandContext.getSource(); 35 | ParsedArgument nameArgument = commandSourceCommandContext.getArguments().get("name"); 36 | if(nameArgument == null) { 37 | player.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(velocityGUI.PREFIX + "Available panels")); 38 | Configs.getPanels().forEach((title, panel) -> { 39 | //Hide panels with no permissions 40 | if(panel.getPerm().equalsIgnoreCase("default") || player.hasPermission(panel.getPerm())) { 41 | player.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(velocityGUI.PREFIX + title)); 42 | } 43 | }); 44 | return 1; 45 | } 46 | 47 | new InventoryLauncher(velocityGUI).execute((String) nameArgument.getResult(), player); 48 | return 1; 49 | } 50 | 51 | /** 52 | * Reloads the configs 53 | * @param commandSourceCommandContext 54 | * @return 55 | */ 56 | public int reload(CommandContext commandSourceCommandContext) { 57 | Configs.loadConfigs(velocityGUI); 58 | CommandSource source = commandSourceCommandContext.getSource(); 59 | source.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(velocityGUI.PREFIX + "Reloaded")); 60 | velocityGUI.getLogger().info("VelocityGUI Reloaded"); 61 | return 1; 62 | } 63 | 64 | /** 65 | * A bit of basic about information 66 | * @param commandSourceCommandContext 67 | * @return 68 | */ 69 | public int about(CommandContext commandSourceCommandContext) { 70 | CommandSource source = commandSourceCommandContext.getSource(); 71 | source.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(velocityGUI.PREFIX + "VelocityGUI by james090500")); 72 | return 1; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/james090500/VelocityGUI/config/Configs.java: -------------------------------------------------------------------------------- 1 | package com.james090500.VelocityGUI.config; 2 | 3 | import com.james090500.VelocityGUI.VelocityGUI; 4 | import com.moandjiezana.toml.Toml; 5 | import lombok.Getter; 6 | 7 | import java.io.File; 8 | import java.io.IOException; 9 | import java.io.InputStream; 10 | import java.nio.file.Files; 11 | import java.util.Arrays; 12 | import java.util.HashMap; 13 | 14 | public class Configs { 15 | 16 | @Getter private static HashMap panels = new HashMap<>(); 17 | 18 | /** 19 | * Loads the config files. 20 | * @param velocityGUI 21 | */ 22 | public static void loadConfigs(VelocityGUI velocityGUI) { 23 | //Create data directory 24 | if(!velocityGUI.getDataDirectory().toFile().exists()) { 25 | velocityGUI.getDataDirectory().toFile().mkdir(); 26 | } 27 | 28 | //Create panel directory 29 | File panelDir = new File(velocityGUI.getDataDirectory().toFile() + "/panels"); 30 | if(!panelDir.exists()) { 31 | panelDir.mkdir(); 32 | } 33 | 34 | if(panelDir.listFiles().length == 0) { 35 | try (InputStream in = VelocityGUI.class.getResourceAsStream("/example.toml")) { 36 | Files.copy(in, new File(panelDir + "/example.toml").toPath()); 37 | } catch (IOException e) { 38 | e.printStackTrace(); 39 | } 40 | } 41 | 42 | for(File file : panelDir.listFiles()) { 43 | Panel panel = new Toml().read(file).to(Panel.class); 44 | panels.put(panel.getName(), panel); 45 | } 46 | } 47 | 48 | public class Panel { 49 | 50 | @Getter private String name; 51 | @Getter private String perm; 52 | @Getter private int rows ; 53 | @Getter private String title; 54 | @Getter private String empty; 55 | @Getter private String sound; 56 | @Getter private String[] commands; 57 | @Getter private HashMap items; 58 | 59 | @Override 60 | public String toString() { 61 | return "Panel{" + 62 | "name='" + name + '\'' + 63 | ", perm='" + perm + '\'' + 64 | ", rows=" + rows + 65 | ", title='" + title + '\'' + 66 | ", empty='" + empty + '\'' + 67 | ", sound='" + sound + '\'' + 68 | ", items=" + items + 69 | '}'; 70 | } 71 | } 72 | 73 | public class Item { 74 | 75 | private String name; 76 | @Getter private String material; 77 | private byte stack; 78 | @Getter private String[] lore; 79 | @Getter private boolean enchanted; 80 | @Getter private String[] commands; 81 | 82 | /** 83 | * Return name or make empty if missed from config 84 | * @return 85 | */ 86 | public String getName() { 87 | return (name != null) ? name : "&f"; 88 | } 89 | 90 | /** 91 | * If stack is missed from config make it 1 92 | * @return 93 | */ 94 | public byte getStack() { 95 | return (stack > 0) ? stack : 1; 96 | } 97 | 98 | @Override 99 | public String toString() { 100 | return "GuiItem{" + 101 | "name='" + name + '\'' + 102 | ", material='" + material + '\'' + 103 | ", stack=" + stack + 104 | ", lore=" + Arrays.toString(lore) + 105 | ", enchanted=" + enchanted + 106 | '}'; 107 | } 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/com/james090500/VelocityGUI/helpers/ChatControlHelper.java: -------------------------------------------------------------------------------- 1 | package com.james090500.VelocityGUI.helpers; 2 | 3 | import com.velocitypowered.api.proxy.Player; 4 | import org.mineacademy.velocitycontrol.SyncedCache; 5 | 6 | public class ChatControlHelper { 7 | 8 | public static String getNick(Player player) { 9 | if(!doesClassExist("org.mineacademy.velocitycontrol.SyncedCache")) { 10 | return null; 11 | } 12 | 13 | String nickname = SyncedCache.fromName(player.getUsername()).getNick(); 14 | return nickname != null ? nickname : player.getUsername(); 15 | } 16 | 17 | /** 18 | * Checks if a class exists or not 19 | * @param name 20 | * @return 21 | */ 22 | private static boolean doesClassExist(String name) { 23 | try { 24 | Class c = Class.forName(name); 25 | if (c != null) { 26 | return true; 27 | } 28 | } catch (ClassNotFoundException e) {} 29 | return false; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/james090500/VelocityGUI/helpers/InventoryBuilder.java: -------------------------------------------------------------------------------- 1 | package com.james090500.VelocityGUI.helpers; 2 | 3 | import com.james090500.VelocityGUI.VelocityGUI; 4 | import com.james090500.VelocityGUI.config.Configs; 5 | import com.velocitypowered.api.proxy.Player; 6 | import dev.simplix.protocolize.api.chat.ChatElement; 7 | import dev.simplix.protocolize.api.inventory.Inventory; 8 | import dev.simplix.protocolize.api.item.BaseItemStack; 9 | import dev.simplix.protocolize.api.item.ItemStack; 10 | import dev.simplix.protocolize.data.ItemType; 11 | import dev.simplix.protocolize.data.inventory.InventoryType; 12 | import lombok.AccessLevel; 13 | import lombok.Getter; 14 | import net.kyori.adventure.text.Component; 15 | import net.querz.nbt.tag.*; 16 | 17 | import java.util.ArrayList; 18 | import java.util.HashMap; 19 | import java.util.List; 20 | 21 | @Getter 22 | public class InventoryBuilder { 23 | 24 | @Getter(AccessLevel.NONE) 25 | private final VelocityGUI velocityGUI; 26 | private final Player player; 27 | private InventoryType rows; 28 | private Component title; 29 | private final List emptyItems = new ArrayList<>(); 30 | private final HashMap items = new HashMap<>(); 31 | 32 | /** 33 | * The builder 34 | * @param velocityGUI The current gui 35 | * @param player The player 36 | */ 37 | public InventoryBuilder(VelocityGUI velocityGUI, Player player) { 38 | this.velocityGUI = velocityGUI; 39 | this.player = player; 40 | } 41 | 42 | /** 43 | * Sets the rows of the GUI to display 44 | * @param rows The amount of rows 45 | */ 46 | public void setRows(int rows) { 47 | this.rows = getInventoryType(rows); 48 | } 49 | 50 | /** 51 | * Sets the title and converts a string to a Component 52 | * @param title The gui title 53 | */ 54 | public void setTitle(String title) { 55 | this.title = PlaceholderParser.of(this.player, title); 56 | } 57 | 58 | /** 59 | * Set the empty item 60 | * @param item The empty item type 61 | */ 62 | public void setEmpty(String item) { 63 | ItemStack itemStack = new ItemStack(ItemType.valueOf(item)); 64 | itemStack.displayName(ChatElement.ofLegacyText("")); 65 | itemStack.amount((byte) 1); 66 | 67 | int totalSlots = this.getRows().getTypicalSize(player.getProtocolVersion().getProtocol()); 68 | for(int i = 0; i < totalSlots; i++) { 69 | emptyItems.add(itemStack); 70 | } 71 | } 72 | 73 | /** 74 | * Add items to the panel 75 | * @param guiItems The items in the gui 76 | */ 77 | public void setItems(HashMap guiItems) { 78 | guiItems.forEach((index, guiItem) -> { 79 | //Set the item Material, Name and Amount 80 | ItemStack itemStack; 81 | if(guiItem.getMaterial().startsWith("head=")) { 82 | itemStack = new ItemStack(ItemType.PLAYER_HEAD); 83 | } else { 84 | try { 85 | itemStack = new ItemStack(ItemType.valueOf(guiItem.getMaterial())); 86 | } catch (IllegalArgumentException e) { 87 | itemStack = new ItemStack(ItemType.STONE); 88 | this.velocityGUI.getLogger().error("Invalid Material! " + guiItem.getMaterial()); 89 | } 90 | } 91 | 92 | itemStack.displayName(ChatElement.of(PlaceholderParser.of(this.player, guiItem.getName()))); 93 | itemStack.amount(guiItem.getStack()); 94 | 95 | //Set any lore on the item 96 | if(guiItem.getLore() != null) { 97 | for (String lore : guiItem.getLore()) { 98 | itemStack.addToLore(ChatElement.of(PlaceholderParser.of(this.player, lore))); 99 | } 100 | } 101 | 102 | //Get the item NBT 103 | CompoundTag tag = itemStack.nbtData(); 104 | 105 | //Set enchantment on the item if needed 106 | if(guiItem.isEnchanted()) { 107 | ListTag enchantments = new ListTag<>(CompoundTag.class); 108 | CompoundTag enchantment = new CompoundTag(); 109 | enchantment.put("id", new StringTag("minecraft:unbreaking")); 110 | enchantment.put("lvl", new ShortTag((short) 1)); 111 | enchantments.add(enchantment); 112 | tag.put("Enchantments", enchantments); 113 | } 114 | 115 | //If a player heads lets do this 116 | if(guiItem.getMaterial().startsWith("head= ")) { 117 | String headData = guiItem.getMaterial().replace("head= ", ""); 118 | if(headData.equals("self")) { 119 | tag.put("SkullOwner", new StringTag(player.getUsername())); 120 | 121 | } else { 122 | CompoundTag skullOwnerTag = tag.getCompoundTag("SkullOwner"); 123 | CompoundTag propertiesTag = tag.getCompoundTag("Properties"); 124 | ListTag texturesTag = new ListTag<>(CompoundTag.class); 125 | CompoundTag textureTag = new CompoundTag(); 126 | 127 | if (skullOwnerTag == null) { 128 | skullOwnerTag = new CompoundTag(); 129 | } 130 | if (propertiesTag == null) { 131 | propertiesTag = new CompoundTag(); 132 | } 133 | 134 | textureTag.put("Value", new StringTag(headData)); 135 | texturesTag.add(textureTag); 136 | propertiesTag.put("textures", texturesTag); 137 | skullOwnerTag.put("Properties", propertiesTag); 138 | skullOwnerTag.put("Name", new StringTag(headData)); 139 | tag.put("SkullOwner", skullOwnerTag); 140 | } 141 | 142 | //Set item NBT 143 | itemStack.nbtData(tag); 144 | } 145 | 146 | tag.put("HideFlags", new IntTag(99)); 147 | tag.put("overrideMeta", new ByteTag((byte)1)); 148 | 149 | //Add to a hashmap 150 | items.put(index, itemStack); 151 | }); 152 | } 153 | 154 | /** 155 | * Get the type of inventory by the rows 156 | * @param value The rows in the inventory 157 | * @return The inventory type 158 | */ 159 | private InventoryType getInventoryType(int value) { 160 | return switch (value) { 161 | case 1 -> InventoryType.GENERIC_9X1; 162 | case 2 -> InventoryType.GENERIC_9X2; 163 | case 3 -> InventoryType.GENERIC_9X3; 164 | case 4 -> InventoryType.GENERIC_9X4; 165 | case 5 -> InventoryType.GENERIC_9X5; 166 | default -> InventoryType.GENERIC_9X6; 167 | }; 168 | } 169 | 170 | /** 171 | * Build the inventory 172 | * @return The inventory instance 173 | */ 174 | public Inventory build() { 175 | Inventory inventory = new Inventory(this.getRows()); 176 | inventory.title(ChatElement.of(this.getTitle())); 177 | inventory.items(this.getEmptyItems()); 178 | 179 | this.getItems().forEach(inventory::item); 180 | 181 | return inventory; 182 | } 183 | 184 | } 185 | -------------------------------------------------------------------------------- /src/main/java/com/james090500/VelocityGUI/helpers/InventoryClickHandler.java: -------------------------------------------------------------------------------- 1 | package com.james090500.VelocityGUI.helpers; 2 | 3 | import com.james090500.VelocityGUI.VelocityGUI; 4 | import com.velocitypowered.api.proxy.Player; 5 | import dev.simplix.protocolize.api.inventory.InventoryClick; 6 | 7 | public class InventoryClickHandler { 8 | 9 | private VelocityGUI velocityGUI; 10 | 11 | /** 12 | * Constructor 13 | * @param velocityGUI 14 | */ 15 | public InventoryClickHandler(VelocityGUI velocityGUI) { 16 | this.velocityGUI = velocityGUI; 17 | } 18 | 19 | /** 20 | * Handle the gui commands 21 | * @param commands 22 | * @param click 23 | */ 24 | public void execute(String[] commands, InventoryClick click) { 25 | Player player = velocityGUI.getServer().getPlayer(click.player().uniqueId()).get(); 26 | for(String command : commands) { 27 | String[] splitCommand = command.split("= "); 28 | switch(splitCommand[0]) { 29 | case "open": 30 | new InventoryLauncher(velocityGUI).execute(splitCommand[1], player); 31 | break; 32 | case "close": 33 | click.player().closeInventory(); 34 | break; 35 | case "sudo": 36 | player.spoofChatInput(splitCommand[1]); 37 | break; 38 | case "vsudo": 39 | velocityGUI.getServer().getCommandManager().executeAsync(player, splitCommand[1]); 40 | break; 41 | case "server": 42 | player.createConnectionRequest(velocityGUI.getServer().getServer(splitCommand[1]).get()).connect(); 43 | break; 44 | } 45 | } 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/james090500/VelocityGUI/helpers/InventoryLauncher.java: -------------------------------------------------------------------------------- 1 | package com.james090500.VelocityGUI.helpers; 2 | 3 | import com.james090500.VelocityGUI.VelocityGUI; 4 | import com.james090500.VelocityGUI.config.Configs; 5 | import com.velocitypowered.api.proxy.Player; 6 | import dev.simplix.protocolize.api.Protocolize; 7 | import dev.simplix.protocolize.api.SoundCategory; 8 | import dev.simplix.protocolize.api.inventory.Inventory; 9 | import dev.simplix.protocolize.api.player.ProtocolizePlayer; 10 | import dev.simplix.protocolize.data.Sound; 11 | import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; 12 | 13 | public class InventoryLauncher { 14 | 15 | private VelocityGUI velocityGUI; 16 | 17 | public InventoryLauncher(VelocityGUI velocityGUI) { 18 | this.velocityGUI = velocityGUI; 19 | } 20 | 21 | /** 22 | * Launches an inventory instance from a panel 23 | * @param panelName 24 | * @param player 25 | */ 26 | public void execute(String panelName, Player player) { 27 | ProtocolizePlayer protocolizePlayer = Protocolize.playerProvider().player(player.getUniqueId()); 28 | 29 | Configs.Panel panel = Configs.getPanels().get(panelName); 30 | if(panel == null) { 31 | protocolizePlayer.closeInventory(); 32 | player.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(velocityGUI.PREFIX + "Panel not found")); 33 | return; 34 | } 35 | 36 | //Stop players with no permissions 37 | if(!panel.getPerm().equalsIgnoreCase("default") && !player.hasPermission(panel.getPerm())) { 38 | protocolizePlayer.closeInventory(); 39 | player.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(velocityGUI.PREFIX + "Panel not found")); 40 | return; 41 | } 42 | 43 | protocolizePlayer.registeredInventories().clear(); 44 | 45 | InventoryBuilder inventoryBuilder = new InventoryBuilder(velocityGUI, player); 46 | inventoryBuilder.setRows(panel.getRows()); 47 | inventoryBuilder.setTitle(panel.getTitle()); 48 | inventoryBuilder.setEmpty(panel.getEmpty()); 49 | inventoryBuilder.setItems(panel.getItems()); 50 | Inventory inventory = inventoryBuilder.build(); 51 | inventory.onClick(click -> { 52 | click.cancelled(true); 53 | Configs.Item item = panel.getItems().get(click.slot()); 54 | if(item != null && item.getCommands() != null) { 55 | new InventoryClickHandler(velocityGUI).execute(item.getCommands(), click); 56 | } 57 | }); 58 | 59 | protocolizePlayer.openInventory(inventory); 60 | 61 | if(panel.getSound() != null) { 62 | protocolizePlayer.playSound(Sound.valueOf(panel.getSound()), SoundCategory.MASTER, 1f, 1f); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/james090500/VelocityGUI/helpers/LuckPermsHelper.java: -------------------------------------------------------------------------------- 1 | package com.james090500.VelocityGUI.helpers; 2 | 3 | import com.velocitypowered.api.proxy.Player; 4 | import net.luckperms.api.LuckPerms; 5 | import net.luckperms.api.LuckPermsProvider; 6 | 7 | public class LuckPermsHelper { 8 | 9 | public static String getMeta(Player player, String queryOption) { 10 | if(!doesClassExist("net.luckperms.api.LuckPerms")) { 11 | return null; 12 | } 13 | 14 | LuckPerms api = LuckPermsProvider.get(); 15 | String metaValue = api.getPlayerAdapter(Player.class).getMetaData(player).getMetaValue(queryOption); 16 | return metaValue == null ? "" : metaValue; 17 | } 18 | 19 | /** 20 | * Checks if a class exists or not 21 | * @param name 22 | * @return 23 | */ 24 | private static boolean doesClassExist(String name) { 25 | try { 26 | Class c = Class.forName(name); 27 | if (c != null) { 28 | return true; 29 | } 30 | } catch (ClassNotFoundException e) {} 31 | return false; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/james090500/VelocityGUI/helpers/PlaceholderParser.java: -------------------------------------------------------------------------------- 1 | package com.james090500.VelocityGUI.helpers; 2 | 3 | import com.velocitypowered.api.proxy.Player; 4 | import net.kyori.adventure.text.Component; 5 | import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; 6 | 7 | public class PlaceholderParser { 8 | 9 | public static Component of(Player player, String rawString) { 10 | //Username 11 | if(rawString.contains("%username%")) { 12 | rawString = rawString.replaceAll("%username%", player.getUsername()); 13 | } 14 | 15 | //Server Name 16 | if(rawString.contains("%server_name%")) { 17 | rawString = rawString.replaceAll("%server_name%", player.getCurrentServer().get().getServerInfo().getName()); 18 | } 19 | 20 | //ChatControlRed 21 | if(rawString.contains("%chatcontrolred_nick%")) { 22 | String nickname = ChatControlHelper.getNick(player); 23 | rawString = rawString.replaceAll("%chatcontrolred_nick%", nickname); 24 | } 25 | 26 | //LuckPerms Meta 27 | if(rawString.startsWith("%luckperms_meta")) { 28 | String queryOption = rawString.replaceAll("%", "").replaceAll("luckperms_meta_", ""); 29 | LuckPermsHelper.getMeta(player, queryOption); 30 | } 31 | 32 | Component component = LegacyComponentSerializer.legacyAmpersand().deserialize(rawString); 33 | return component; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/resources/example.toml: -------------------------------------------------------------------------------- 1 | name = "example" 2 | perm = "default" 3 | rows = 3 4 | title = "&dVelocityGUI" 5 | empty = "GREEN_STAINED_GLASS_PANE" 6 | sound = "ENTITY_ARROW_HIT_PLAYER" 7 | commands = [ 8 | "rules", 9 | "version", 10 | "plugins", 11 | "help", 12 | "pl" 13 | ] 14 | 15 | [items] 16 | [items.13] 17 | material = "OAK_SIGN" 18 | stack = 1 19 | name = "&dVelocityGUI" 20 | lore = [ 21 | "&eA Velociy Side GUI", 22 | "&eFor all your servers" 23 | ] 24 | enchanted = true 25 | commands = [ 26 | "sudo= I love VelocityGUI" 27 | ] --------------------------------------------------------------------------------