diff --git a/gateway/EaglercraftXBukkitAPI/.gitignore b/gateway/EaglercraftXBukkitAPI/.gitignore new file mode 100644 index 0000000..e30d0db --- /dev/null +++ b/gateway/EaglercraftXBukkitAPI/.gitignore @@ -0,0 +1,10 @@ +lib/* +.idea/* +*.iml +out/* +/.gradle/ +/.settings/ +.classpath +.project +/build/ +/bin/ diff --git a/gateway/EaglercraftXBukkitAPI/EaglercraftXBukkitAPI-Latest.jar b/gateway/EaglercraftXBukkitAPI/EaglercraftXBukkitAPI-Latest.jar new file mode 100644 index 0000000..5c6b63e Binary files /dev/null and b/gateway/EaglercraftXBukkitAPI/EaglercraftXBukkitAPI-Latest.jar differ diff --git a/gateway/EaglercraftXBukkitAPI/build.gradle b/gateway/EaglercraftXBukkitAPI/build.gradle new file mode 100644 index 0000000..eef042b --- /dev/null +++ b/gateway/EaglercraftXBukkitAPI/build.gradle @@ -0,0 +1,40 @@ +plugins { + id 'java' + id 'eclipse' +} + +group = 'net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper' +version = '' + +repositories { + mavenCentral() + maven { + url = 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' + } + maven { + url = "https://oss.sonatype.org/content/repositories/snapshots/" + } +} + +sourceSets { + main { + java { + srcDirs 'src/main/java' + srcDirs '../backend-rpc-protocol/src/backend-rpc-protocol/java' + } + } +} + +dependencies { + compileOnly 'org.spigotmc:spigot-api:1.8.8-R0.1-SNAPSHOT' +} + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + +jar { + compileJava.options.encoding = 'UTF-8' + javadoc.options.encoding = 'UTF-8' +} diff --git a/gateway/EaglercraftXBukkitAPI/gradle/wrapper/gradle-wrapper.jar b/gateway/EaglercraftXBukkitAPI/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..249e583 Binary files /dev/null and b/gateway/EaglercraftXBukkitAPI/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gateway/EaglercraftXBukkitAPI/gradle/wrapper/gradle-wrapper.properties b/gateway/EaglercraftXBukkitAPI/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..eb7f6f9 --- /dev/null +++ b/gateway/EaglercraftXBukkitAPI/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Thu Jun 20 10:14:47 CDT 2024 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gateway/EaglercraftXBukkitAPI/gradlew b/gateway/EaglercraftXBukkitAPI/gradlew new file mode 100644 index 0000000..1b6c787 --- /dev/null +++ b/gateway/EaglercraftXBukkitAPI/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gateway/EaglercraftXBukkitAPI/gradlew.bat b/gateway/EaglercraftXBukkitAPI/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/gateway/EaglercraftXBukkitAPI/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/gateway/EaglercraftXBukkitAPI/settings.gradle b/gateway/EaglercraftXBukkitAPI/settings.gradle new file mode 100644 index 0000000..6c86aaf --- /dev/null +++ b/gateway/EaglercraftXBukkitAPI/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'EaglercraftXBukkitAPI' + diff --git a/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/EaglerXBukkitAPIListener.java b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/EaglerXBukkitAPIListener.java new file mode 100644 index 0000000..29d5f8e --- /dev/null +++ b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/EaglerXBukkitAPIListener.java @@ -0,0 +1,58 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper; + +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerLoginEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.plugin.messaging.PluginMessageListener; + +import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.EaglerBackendRPCProtocol; +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.impl.PlayerDataObj; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class EaglerXBukkitAPIListener implements Listener, PluginMessageListener { + + @EventHandler + public void onLoginEvent(PlayerLoginEvent evt) { + PlayerDataObj.setupPlayer(evt.getPlayer()); + } + + @Override + public void onPluginMessageReceived(String channel, Player player, byte[] data) { + if(EaglerBackendRPCProtocol.CHANNEL_NAME.equals(channel)) { + PlayerDataObj dataObj = PlayerDataObj.getForPlayer(player); + if(dataObj != null) { + dataObj.firePluginMsgRecievedInternal(data); + } + }else if(EaglerBackendRPCProtocol.CHANNEL_NAME_READY.equals(channel)) { + PlayerDataObj dataObj = PlayerDataObj.getForPlayer(player); + if(dataObj != null) { + dataObj.firePluginReadyMsgRecieved(); + } + } + } + + @EventHandler + public void onQuitEvent(PlayerQuitEvent evt) { + PlayerDataObj dataObj = PlayerDataObj.getForPlayer(evt.getPlayer()); + if(dataObj != null) { + dataObj.firePlayerQuitEventInternal(); + } + } + +} diff --git a/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/EaglerXBukkitAPIPlugin.java b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/EaglerXBukkitAPIPlugin.java new file mode 100644 index 0000000..2a97fa4 --- /dev/null +++ b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/EaglerXBukkitAPIPlugin.java @@ -0,0 +1,86 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper; + +import java.util.Collection; +import java.util.Timer; +import java.util.TimerTask; +import java.util.logging.Logger; + +import org.bukkit.entity.Player; +import org.bukkit.plugin.java.JavaPlugin; + +import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.EaglerBackendRPCProtocol; +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.impl.PlayerDataObj; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class EaglerXBukkitAPIPlugin extends JavaPlugin { + + private static EaglerXBukkitAPIPlugin instance = null; + + private Timer timeoutHandler; + + public EaglerXBukkitAPIPlugin() { + instance = this; + } + + @Override + public void onLoad() { + + } + + @Override + public void onEnable() { + EaglerXBukkitAPIListener ls = new EaglerXBukkitAPIListener(); + getServer().getPluginManager().registerEvents(ls, this); + getServer().getMessenger().registerOutgoingPluginChannel(this, EaglerBackendRPCProtocol.CHANNEL_NAME); + getServer().getMessenger().registerIncomingPluginChannel(this, EaglerBackendRPCProtocol.CHANNEL_NAME, ls); + getServer().getMessenger().registerIncomingPluginChannel(this, EaglerBackendRPCProtocol.CHANNEL_NAME_READY, ls); + if(timeoutHandler == null) { + timeoutHandler = new Timer("EaglerXBukkitAPI: Timeout cleanup thread"); + timeoutHandler.scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + Collection pp = EaglerXBukkitAPIPlugin.this.getServer().getOnlinePlayers(); + if(!pp.isEmpty()) { + long now = System.nanoTime() / 1000000l; + for(Player p : pp) { + PlayerDataObj.getForPlayer(p).fireCheckRequestTimeoutsInternal(now); + } + } + } + }, 0l, 5000l); + } + } + + @Override + public void onDisable() { + getServer().getMessenger().unregisterOutgoingPluginChannel(this); + getServer().getMessenger().unregisterIncomingPluginChannel(this); + if(timeoutHandler != null) { + timeoutHandler.cancel(); + timeoutHandler = null; + } + } + + public static EaglerXBukkitAPIPlugin getEagler() { + return instance; + } + + public static Logger logger() { + return instance.getLogger(); + } + +} diff --git a/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/ClientBrandUUIDs.java b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/ClientBrandUUIDs.java new file mode 100644 index 0000000..e7662c4 --- /dev/null +++ b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/ClientBrandUUIDs.java @@ -0,0 +1,37 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api; + +import java.nio.charset.StandardCharsets; +import java.util.UUID; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class ClientBrandUUIDs { + + public static final UUID BRAND_NULL_UUID = new UUID(0l, 0l); + public static final UUID BRAND_VANILLA_UUID = new UUID(0x1DCE015CD384374El, 0x85030A4DE95E5736l); + + public static final UUID BRAND_EAGLERCRAFTX_V4_UUID = makeClientBrandUUID("EaglercraftX"); + public static final UUID BRAND_EAGLERCRAFTX_LEGACY_UUID = makeClientBrandUUIDLegacy("EaglercraftX"); + + public static UUID makeClientBrandUUID(String name) { + return UUID.nameUUIDFromBytes(("EaglercraftXClient:" + name).getBytes(StandardCharsets.UTF_8)); + } + + public static UUID makeClientBrandUUIDLegacy(String name) { + return UUID.nameUUIDFromBytes(("EaglercraftXClientOld:" + name).getBytes(StandardCharsets.UTF_8)); + } + +} diff --git a/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/EaglerRPCException.java b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/EaglerRPCException.java new file mode 100644 index 0000000..ba9d358 --- /dev/null +++ b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/EaglerRPCException.java @@ -0,0 +1,35 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class EaglerRPCException extends RuntimeException { + + public EaglerRPCException() { + } + + public EaglerRPCException(String message, Throwable cause) { + super(message, cause); + } + + public EaglerRPCException(String message) { + super(message); + } + + public EaglerRPCException(Throwable cause) { + super(cause); + } + +} diff --git a/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/EaglerRPCInitException.java b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/EaglerRPCInitException.java new file mode 100644 index 0000000..0429a60 --- /dev/null +++ b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/EaglerRPCInitException.java @@ -0,0 +1,45 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class EaglerRPCInitException extends EaglerRPCException { + + private int code; + + public EaglerRPCInitException(int code) { + this.code = code; + } + + public EaglerRPCInitException(int code, String message, Throwable cause) { + super(message, cause); + this.code = code; + } + + public EaglerRPCInitException(int code, String message) { + super(message); + this.code = code; + } + + public EaglerRPCInitException(int code, Throwable cause) { + super(cause); + this.code = code; + } + + public int getFailureCodeEagler() { + return code; + } + +} diff --git a/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/EnumSubscribeEvents.java b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/EnumSubscribeEvents.java new file mode 100644 index 0000000..63ca14f --- /dev/null +++ b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/EnumSubscribeEvents.java @@ -0,0 +1,39 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api; + +import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.client.CPacketRPCSubscribeEvents; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public enum EnumSubscribeEvents { + + /** @see net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.event.EventWebViewOpenClose */ + EVENT_WEBVIEW_OPEN_CLOSE(CPacketRPCSubscribeEvents.SUBSCRIBE_EVENT_WEBVIEW_OPEN_CLOSE), + + /** @see net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.event.EventWebViewMessage */ + EVENT_WEBVIEW_MESSAGE(CPacketRPCSubscribeEvents.SUBSCRIBE_EVENT_WEBVIEW_MESSAGE), + + /** @see net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.event.EventToggledVoice */ + EVENT_TOGGLE_VOICE(CPacketRPCSubscribeEvents.SUBSCRIBE_EVENT_TOGGLE_VOICE); + + protected static final EnumSubscribeEvents[] _VALUES = values(); + + public final int bit; + + private EnumSubscribeEvents(int bit) { + this.bit = bit; + } + +} diff --git a/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/IEaglerRPCCloseHandler.java b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/IEaglerRPCCloseHandler.java new file mode 100644 index 0000000..943f5cf --- /dev/null +++ b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/IEaglerRPCCloseHandler.java @@ -0,0 +1,22 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public interface IEaglerRPCCloseHandler { + + void handleEvent(IEaglerXBukkitAPI api); + +} diff --git a/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/IEaglerRPCEventHandler.java b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/IEaglerRPCEventHandler.java new file mode 100644 index 0000000..7f7299f --- /dev/null +++ b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/IEaglerRPCEventHandler.java @@ -0,0 +1,24 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api; + +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.event.IEaglerRPCEvent; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public interface IEaglerRPCEventHandler { + + void handleEvent(IEaglerXBukkitAPI api, EnumSubscribeEvents event, T data); + +} diff --git a/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/IEaglerRPCFuture.java b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/IEaglerRPCFuture.java new file mode 100644 index 0000000..b3b68e7 --- /dev/null +++ b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/IEaglerRPCFuture.java @@ -0,0 +1,48 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api; + +import java.util.concurrent.Executor; + +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public interface IEaglerRPCFuture extends ListenableFuture { + + public static final Executor SAME_THREAD_EXECUTOR = MoreExecutors.sameThreadExecutor(); + + /** + * Warning: Futures.addCallback is recommended! + */ + default void addListener(Runnable runnable) { + addListener(runnable, SAME_THREAD_EXECUTOR); + } + + default void addCallback(FutureCallback runnable, Executor executor) { + Futures.addCallback(this, runnable, executor); + } + + default void addCallback(FutureCallback runnable) { + Futures.addCallback(this, runnable, SAME_THREAD_EXECUTOR); + } + + void setExpiresMSFromNow(int millis); + + boolean hasExpired(); + +} diff --git a/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/IEaglerXBukkitAPI.java b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/IEaglerXBukkitAPI.java new file mode 100644 index 0000000..5036cf0 --- /dev/null +++ b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/IEaglerXBukkitAPI.java @@ -0,0 +1,243 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +import org.bukkit.entity.Player; + +import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.EaglerBackendRPCProtocol; +import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.EaglerBackendRPCPacket; +import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.client.CPacketRPCNotifBadgeShow; +import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.client.CPacketRPCSetPauseMenuCustom; +import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.util.NotificationBadgeBuilder; +import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.util.PacketImageData; +import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.util.SkinPacketHelper; +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.event.IEaglerRPCEvent; +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.response.ResponseBytes; +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.response.ResponseCookie; +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.response.ResponseString; +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.response.ResponseUUID; +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.response.ResponseVoiceStatus; +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.response.ResponseWebViewStatus; +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.impl.EaglerXBukkitImpl; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public interface IEaglerXBukkitAPI { + + public static IEaglerRPCFuture getAPI(Player player) { + IEaglerRPCFuture futureRet = EaglerXBukkitImpl.getAPI(player); + futureRet.setExpiresMSFromNow(EaglerXBukkitImpl.DEFAULT_TIMEOUT); + return futureRet; + } + + public static IEaglerRPCFuture getAPI(Player player, int timeoutMS) { + IEaglerRPCFuture futureRet = EaglerXBukkitImpl.getAPI(player); + futureRet.setExpiresMSFromNow(timeoutMS); + return futureRet; + } + + EaglerBackendRPCProtocol getRPCProtocolVersion(); + + int getEaglerProtocolVersion(); + + boolean isOpen(); + + void closeAPI(); + + Player getPlayer(); + + void sendRPCPacket(EaglerBackendRPCPacket packet); + + default void subscribeEvents(EnumSubscribeEvents...events) { + int bits = 0; + for(int i = 0; i < events.length; ++i) { + bits |= events[i].bit; + } + subscribeEvents(bits); + } + + void subscribeEvents(int events); + + default void unsubscribeEvents(EnumSubscribeEvents...events) { + int bits = 0; + for(int i = 0; i < events.length; ++i) { + bits |= events[i].bit; + } + unsubscribeEvents(bits); + } + + void unsubscribeEvents(int events); + + void unsubscribeAllEvents(); + + int getSubscribedEventsBits(); + + default Set getSubscribedEvents() { + Set ret = new HashSet<>(4); + int bits = getSubscribedEventsBits(); + EnumSubscribeEvents[] enums = EnumSubscribeEvents._VALUES; + for(int i = 0; i < enums.length; ++i) { + if((bits & enums[i].bit) != 0) { + ret.add(enums[i]); + } + } + return ret; + } + + void addEventListener(EnumSubscribeEvents eventType, IEaglerRPCEventHandler handler); + + void removeEventListener(EnumSubscribeEvents eventType, IEaglerRPCEventHandler handler); + + void removeEventListeners(EnumSubscribeEvents eventType); + + void addCloseListener(IEaglerRPCCloseHandler handler); + + void removeCloseListener(IEaglerRPCCloseHandler handler); + + void removeCloseListeners(); + + boolean redirectPlayerSupported(); + + void redirectPlayerToWebSocket(String webSocketURI); + + void setBaseRequestTimeout(int seconds); + + IEaglerRPCFuture requestPlayerProfileUUID(); + + IEaglerRPCFuture requestPlayerRealIP(); + + IEaglerRPCFuture requestPlayerOrigin(); + + IEaglerRPCFuture requestPlayerUserAgent(); + + IEaglerRPCFuture requestPlayerSkinData(); + + IEaglerRPCFuture requestPlayerCapeData(); + + IEaglerRPCFuture requestPlayerCookieData(); + + IEaglerRPCFuture requestPlayerClientBrandStr(); + + IEaglerRPCFuture requestPlayerClientVersionStr(); + + IEaglerRPCFuture requestPlayerClientBrandAndVersionStr(); + + IEaglerRPCFuture requestPlayerClientBrandUUID(); + + IEaglerRPCFuture requestPlayerVoiceStatus(); + + IEaglerRPCFuture requestPlayerWebViewStatus(); + + void sendRawCustomPayloadPacket(String channel, byte[] data); + + default void sendRawEaglerPacketV4(byte[] data) { + sendRawCustomPayloadPacket("EAG|1.8", data); + } + + boolean pauseMenuCustomizationSupported(); + + void setPauseMenuCustomizationState(CPacketRPCSetPauseMenuCustom packet); + + void sendWebViewMessageString(String channelName, String data); + + void sendWebViewMessageBytes(String channelName, byte[] data); + + void forcePlayerSkin(byte[] skinData, boolean notifyOthers); + + default void forcePlayerSkinPreset(int presetID, boolean notifyOthers) { + forcePlayerSkin(SkinPacketHelper.writePresetSkinPacket(presetID), notifyOthers); + } + + default void forcePlayerSkinCustom(int modelId, byte[] texture64x64, boolean notifyOthers) { + forcePlayerSkin(SkinPacketHelper.writeCustomSkinPacket(modelId, texture64x64), notifyOthers); + } + + void forcePlayerCape(byte[] capeData, boolean notifyOthers); + + default void forcePlayerCapePreset(int presetID, boolean notifyOthers) { + forcePlayerCape(SkinPacketHelper.writePresetCapePacket(presetID), notifyOthers); + } + + default void forcePlayerCapeCustom(byte[] texture32x32, boolean notifyOthers) { + forcePlayerCape(SkinPacketHelper.writeCustomCapePacket(texture32x32), notifyOthers); + } + + void setCookieData(byte[] cookieData, int expiresAfterSec, boolean revokeQuerySupported, boolean saveToDisk); + + default void setCookieData(byte[] cookieData, int expiresAfter, TimeUnit expiresTimeUnit, boolean revokeQuerySupported, boolean saveToDisk) { + setCookieData(cookieData, (int)expiresTimeUnit.toSeconds(expiresAfter), revokeQuerySupported, saveToDisk); + } + + default void setCookieData(byte[] cookieData, int expiresAfterSec, boolean revokeQuerySupported) { + setCookieData(cookieData, expiresAfterSec, revokeQuerySupported, true); + } + + default void setCookieData(byte[] cookieData, int expiresAfter, TimeUnit expiresTimeUnit, boolean revokeQuerySupported) { + setCookieData(cookieData, (int)expiresTimeUnit.toSeconds(expiresAfter), revokeQuerySupported, true); + } + + default void setCookieData(byte[] cookieData, int expiresAfterSec) { + setCookieData(cookieData, expiresAfterSec, false, true); + } + + default void setCookieData(byte[] cookieData, int expiresAfter, TimeUnit expiresTimeUnit) { + setCookieData(cookieData, (int)expiresTimeUnit.toSeconds(expiresAfter), false, true); + } + + default void clearCookieData() { + setCookieData(null, 0, false, false); + } + + void setFNAWSkinsEnabled(boolean enabled, boolean force); + + void setFNAWSkinsEnabled(boolean enabled); + + void resetForcedMulti(boolean resetSkin, boolean resetCape, boolean resetFNAWForce, boolean notifyOtherPlayers); + + void resetForcedSkin(boolean notifyOtherPlayers); + + void resetForcedCape(boolean notifyOtherPlayers); + + void resetForcedFNAW(); + + boolean notifSupported(); + + void notifIconRegister(Map iconsToRegister); + + void notifIconRegister(UUID iconUUID, PacketImageData imageData); + + void notifIconRelease(Collection iconsToRelease); + + void notifIconRelease(UUID iconUUID); + + void notifBadgeShow(CPacketRPCNotifBadgeShow packet); + + default void notifBadgeShow(NotificationBadgeBuilder packet) { + notifBadgeShow(packet.buildPacket()); + } + + void notifBadgeHide(UUID badgeUUID); + + void setMeta(String key, T value); + + T getMeta(String key); + +} diff --git a/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/ImageDataLoader.java b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/ImageDataLoader.java new file mode 100644 index 0000000..44bcb32 --- /dev/null +++ b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/ImageDataLoader.java @@ -0,0 +1,96 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +import javax.imageio.ImageIO; + +import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.util.PacketImageData; +import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.util.SkinPacketHelper; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class ImageDataLoader { + + public static PacketImageData loadPacketImageData(File img) throws IOException { + return loadPacketImageData(ImageIO.read(img), 255, 255); + } + + public static PacketImageData loadPacketImageData(File img, int maxWidth, int maxHeight) throws IOException { + return loadPacketImageData(ImageIO.read(img), maxWidth, maxHeight); + } + + public static PacketImageData loadPacketImageData(BufferedImage img) { + return loadPacketImageData(img, 255, 255); + } + + public static PacketImageData loadPacketImageData(BufferedImage img, int maxWidth, int maxHeight) { + int w = img.getWidth(); + int h = img.getHeight(); + if(w > maxWidth || h > maxHeight) { + float aspectRatio = (float)w / (float)h; + int nw, nh; + if(aspectRatio >= 1.0f) { + nw = (int)(maxWidth / aspectRatio); + nh = maxHeight; + }else { + nw = maxWidth; + nh = (int)(maxHeight * aspectRatio); + } + BufferedImage resized = new BufferedImage(nw, nh, BufferedImage.TYPE_INT_ARGB); + Graphics2D g = (Graphics2D) resized.getGraphics(); + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); + g.setBackground(new Color(0, true)); + g.clearRect(0, 0, nw, nh); + g.drawImage(img, 0, 0, nw, nh, 0, 0, w, h, null); + g.dispose(); + img = resized; + } + int[] pixels = new int[w * h]; + img.getRGB(0, 0, w, h, pixels, 0, w); + return new PacketImageData(w, h, pixels); + } + + public static byte[] loadCustomSkin(File texture64x64) throws IOException { + return SkinPacketHelper.loadCustomSkin(texture64x64); + } + + public static byte[] loadCustomSkin(InputStream texture64x64) throws IOException { + return SkinPacketHelper.loadCustomSkin(texture64x64); + } + + public static byte[] loadCustomSkin(BufferedImage texture64x64) { + return SkinPacketHelper.loadCustomSkin(texture64x64); + } + + public static byte[] loadCustomCape(File textureNx64) throws IOException { + return SkinPacketHelper.loadCustomCape(textureNx64); + } + + public static byte[] loadCustomCape(InputStream textureNx64) throws IOException { + return SkinPacketHelper.loadCustomCape(textureNx64); + } + + public static byte[] loadCustomCape(BufferedImage textureNx64) { + return SkinPacketHelper.loadCustomCape(textureNx64); + } + +} diff --git a/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/event/EventToggledVoice.java b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/event/EventToggledVoice.java new file mode 100644 index 0000000..dacb14f --- /dev/null +++ b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/event/EventToggledVoice.java @@ -0,0 +1,39 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.event; + +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.EnumSubscribeEvents; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class EventToggledVoice implements IEaglerRPCEvent { + + public static enum VoiceState { + SERVER_DISABLE, DISABLED, ENABLED; + } + + public final VoiceState oldVoiceState; + public final VoiceState newVoiceState; + + public EventToggledVoice(VoiceState oldVoiceState, VoiceState newVoiceState) { + this.oldVoiceState = oldVoiceState; + this.newVoiceState = newVoiceState; + } + + @Override + public EnumSubscribeEvents getType() { + return EnumSubscribeEvents.EVENT_TOGGLE_VOICE; + } + +} diff --git a/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/event/EventWebViewMessage.java b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/event/EventWebViewMessage.java new file mode 100644 index 0000000..5558023 --- /dev/null +++ b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/event/EventWebViewMessage.java @@ -0,0 +1,59 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.event; + +import java.nio.charset.StandardCharsets; + +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.EnumSubscribeEvents; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class EventWebViewMessage implements IEaglerRPCEvent { + + public static enum MessageType { + STRING, BINARY; + } + + public final String channelName; + public final MessageType messageType; + protected final byte[] messageContent; + protected String asString = null; + + public EventWebViewMessage(String channelName, MessageType messageType, byte[] messageContent) { + this.channelName = channelName; + this.messageType = messageType; + this.messageContent = messageContent; + } + + public String getContentStr() { + if(messageType == MessageType.STRING) { + if(asString == null) { + asString = new String(messageContent, StandardCharsets.UTF_8); + } + return asString; + }else { + return null; + } + } + + public byte[] getContentBytes() { + return messageType == MessageType.BINARY ? messageContent : null; + } + + @Override + public EnumSubscribeEvents getType() { + return EnumSubscribeEvents.EVENT_WEBVIEW_MESSAGE; + } + +} diff --git a/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/event/EventWebViewOpenClose.java b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/event/EventWebViewOpenClose.java new file mode 100644 index 0000000..c9419b3 --- /dev/null +++ b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/event/EventWebViewOpenClose.java @@ -0,0 +1,35 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.event; + +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.EnumSubscribeEvents; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class EventWebViewOpenClose implements IEaglerRPCEvent { + + public final String channelName; + public final boolean opened; + + public EventWebViewOpenClose(String channelName, boolean opened) { + this.channelName = channelName; + this.opened = opened; + } + + @Override + public EnumSubscribeEvents getType() { + return EnumSubscribeEvents.EVENT_WEBVIEW_OPEN_CLOSE; + } + +} diff --git a/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/event/IEaglerRPCEvent.java b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/event/IEaglerRPCEvent.java new file mode 100644 index 0000000..b3977fa --- /dev/null +++ b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/event/IEaglerRPCEvent.java @@ -0,0 +1,24 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.event; + +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.EnumSubscribeEvents; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public interface IEaglerRPCEvent { + + EnumSubscribeEvents getType(); + +} diff --git a/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/response/EaglerRPCResponseException.java b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/response/EaglerRPCResponseException.java new file mode 100644 index 0000000..69d5e34 --- /dev/null +++ b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/response/EaglerRPCResponseException.java @@ -0,0 +1,37 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.response; + +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.EaglerRPCException; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class EaglerRPCResponseException extends EaglerRPCException { + + public EaglerRPCResponseException() { + } + + public EaglerRPCResponseException(String message, Throwable cause) { + super(message, cause); + } + + public EaglerRPCResponseException(String message) { + super(message); + } + + public EaglerRPCResponseException(Throwable cause) { + super(cause); + } + +} diff --git a/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/response/EaglerRPCTimeoutException.java b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/response/EaglerRPCTimeoutException.java new file mode 100644 index 0000000..164831c --- /dev/null +++ b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/response/EaglerRPCTimeoutException.java @@ -0,0 +1,29 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.response; + +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.EaglerRPCException; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class EaglerRPCTimeoutException extends EaglerRPCException { + + public EaglerRPCTimeoutException() { + } + + public EaglerRPCTimeoutException(String message) { + super(message); + } + +} diff --git a/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/response/IEaglerRPCResponse.java b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/response/IEaglerRPCResponse.java new file mode 100644 index 0000000..2da386d --- /dev/null +++ b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/response/IEaglerRPCResponse.java @@ -0,0 +1,26 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.response; + +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.IEaglerXBukkitAPI; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public interface IEaglerRPCResponse { + + IEaglerXBukkitAPI getSource(); + + int getRequestID(); + +} diff --git a/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/response/ResponseBytes.java b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/response/ResponseBytes.java new file mode 100644 index 0000000..093da63 --- /dev/null +++ b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/response/ResponseBytes.java @@ -0,0 +1,42 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.response; + +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.IEaglerXBukkitAPI; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class ResponseBytes implements IEaglerRPCResponse { + + protected final IEaglerXBukkitAPI source; + protected final int requestID; + public final byte[] bytes; + + public ResponseBytes(IEaglerXBukkitAPI source, int requestID, byte[] bytes) { + this.source = source; + this.requestID = requestID; + this.bytes = bytes; + } + + @Override + public IEaglerXBukkitAPI getSource() { + return source; + } + + @Override + public int getRequestID() { + return requestID; + } + +} diff --git a/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/response/ResponseCookie.java b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/response/ResponseCookie.java new file mode 100644 index 0000000..357cdb2 --- /dev/null +++ b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/response/ResponseCookie.java @@ -0,0 +1,44 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.response; + +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.IEaglerXBukkitAPI; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class ResponseCookie implements IEaglerRPCResponse { + + protected final IEaglerXBukkitAPI source; + protected final int requestID; + public final boolean cookieEnabled; + public final byte[] cookieBytes; + + public ResponseCookie(IEaglerXBukkitAPI source, int requestID, boolean cookieEnabled, byte[] cookieBytes) { + this.source = source; + this.requestID = requestID; + this.cookieEnabled = cookieEnabled; + this.cookieBytes = cookieBytes; + } + + @Override + public IEaglerXBukkitAPI getSource() { + return source; + } + + @Override + public int getRequestID() { + return requestID; + } + +} diff --git a/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/response/ResponseString.java b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/response/ResponseString.java new file mode 100644 index 0000000..2d1c4e0 --- /dev/null +++ b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/response/ResponseString.java @@ -0,0 +1,42 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.response; + +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.IEaglerXBukkitAPI; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class ResponseString implements IEaglerRPCResponse { + + protected final IEaglerXBukkitAPI source; + protected final int requestID; + public final String string; + + public ResponseString(IEaglerXBukkitAPI source, int requestID, String string) { + this.source = source; + this.requestID = requestID; + this.string = string; + } + + @Override + public IEaglerXBukkitAPI getSource() { + return source; + } + + @Override + public int getRequestID() { + return requestID; + } + +} diff --git a/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/response/ResponseUUID.java b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/response/ResponseUUID.java new file mode 100644 index 0000000..811f666 --- /dev/null +++ b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/response/ResponseUUID.java @@ -0,0 +1,44 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.response; + +import java.util.UUID; + +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.IEaglerXBukkitAPI; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class ResponseUUID implements IEaglerRPCResponse { + + protected final IEaglerXBukkitAPI source; + protected final int requestID; + public final UUID uuid; + + public ResponseUUID(IEaglerXBukkitAPI source, int requestID, UUID uuid) { + this.source = source; + this.requestID = requestID; + this.uuid = uuid; + } + + @Override + public IEaglerXBukkitAPI getSource() { + return source; + } + + @Override + public int getRequestID() { + return requestID; + } + +} diff --git a/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/response/ResponseVoiceStatus.java b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/response/ResponseVoiceStatus.java new file mode 100644 index 0000000..da3013c --- /dev/null +++ b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/response/ResponseVoiceStatus.java @@ -0,0 +1,46 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.response; + +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.IEaglerXBukkitAPI; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class ResponseVoiceStatus implements IEaglerRPCResponse { + + public static enum VoiceState { + SERVER_DISABLE, DISABLED, ENABLED; + } + + protected final IEaglerXBukkitAPI source; + protected final int requestID; + public final VoiceState voiceState; + + public ResponseVoiceStatus(IEaglerXBukkitAPI source, int requestID, VoiceState voiceState) { + this.source = source; + this.requestID = requestID; + this.voiceState = voiceState; + } + + @Override + public IEaglerXBukkitAPI getSource() { + return source; + } + + @Override + public int getRequestID() { + return requestID; + } + +} diff --git a/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/response/ResponseWebViewStatus.java b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/response/ResponseWebViewStatus.java new file mode 100644 index 0000000..e2508d3 --- /dev/null +++ b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/api/response/ResponseWebViewStatus.java @@ -0,0 +1,48 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.response; + +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.IEaglerXBukkitAPI; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class ResponseWebViewStatus implements IEaglerRPCResponse { + + public static enum WebViewState { + NOT_SUPPORTED, SERVER_DISABLE, CHANNEL_CLOSED, CHANNEL_OPEN; + } + + protected final IEaglerXBukkitAPI source; + protected final int requestID; + public final WebViewState webviewState; + public final String channelName; + + public ResponseWebViewStatus(IEaglerXBukkitAPI source, int requestID, WebViewState webviewState, String channelName) { + this.source = source; + this.requestID = requestID; + this.webviewState = webviewState; + this.channelName = channelName; + } + + @Override + public IEaglerXBukkitAPI getSource() { + return source; + } + + @Override + public int getRequestID() { + return requestID; + } + +} diff --git a/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/impl/EaglerRPCFutureImpl.java b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/impl/EaglerRPCFutureImpl.java new file mode 100644 index 0000000..6590598 --- /dev/null +++ b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/impl/EaglerRPCFutureImpl.java @@ -0,0 +1,55 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.impl; + +import com.google.common.util.concurrent.AbstractFuture; + +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.IEaglerRPCFuture; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class EaglerRPCFutureImpl extends AbstractFuture implements IEaglerRPCFuture { + + private volatile long timeStart = -1l; + private volatile int timeoutAfter = -1; + + @Override + public void setExpiresMSFromNow(int millis) { + if(millis > 0) { + timeStart = System.nanoTime() / 1000000l; + timeoutAfter = millis; + }else { + timeStart = -1l; + timeoutAfter = -1; + } + } + + @Override + public boolean hasExpired() { + return (timeStart > 0l && timeoutAfter > 0) ? ((System.nanoTime() / 1000000l) - timeStart) > timeoutAfter : false; + } + + public boolean hasExpiredBetter(long now) { + return (timeStart > 0l && timeoutAfter > 0) ? (now - timeStart) > timeoutAfter : false; + } + + public void fireCompleteInternal(V value) { + this.set(value); + } + + public void fireExceptionInternal(Throwable value) { + this.setException(value); + } + +} diff --git a/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/impl/EaglerXBukkitImpl.java b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/impl/EaglerXBukkitImpl.java new file mode 100644 index 0000000..6db93be --- /dev/null +++ b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/impl/EaglerXBukkitImpl.java @@ -0,0 +1,788 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.impl; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.ReentrantLock; +import java.util.logging.Level; + +import org.bukkit.entity.Player; + +import com.google.common.collect.ListMultimap; +import com.google.common.collect.MultimapBuilder.ListMultimapBuilder; + +import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.EaglerBackendRPCProtocol; +import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.EaglerBackendRPCHandler; +import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.EaglerBackendRPCPacket; +import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.client.*; +import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.server.*; +import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.util.PacketImageData; +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.EaglerXBukkitAPIPlugin; +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.EaglerRPCException; +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.EnumSubscribeEvents; +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.IEaglerRPCCloseHandler; +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.IEaglerRPCEventHandler; +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.IEaglerRPCFuture; +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.IEaglerXBukkitAPI; +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.event.*; +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.response.*; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class EaglerXBukkitImpl implements IEaglerXBukkitAPI { + + public static final int DEFAULT_TIMEOUT = 10000; + + public static EaglerRPCFutureImpl getAPI(Player player) { + if(player == null) { + throw new NullPointerException("Player cannot be null!"); + } + PlayerDataObj data = PlayerDataObj.getForPlayer(player); + if(data == null) { + throw new IllegalStateException("Player object is not ready yet for EaglerXBukkitAPI.createAPI()! (Try hooking PlayerJoinEvent instead of PlayerLoginEvent you hooker)"); + } + boolean sendHello; + synchronized(data) { + if(data.openFuture != null) { + return data.openFuture; + } + if(data.currentAPI != null) { + EaglerRPCFutureImpl completeImmedately = new EaglerRPCFutureImpl(); + completeImmedately.fireCompleteInternal(data.currentAPI); + return completeImmedately; + } + data.openFuture = new EaglerRPCFutureImpl(); + sendHello = data.hasRecievedReady; + } + if(sendHello) { + sendHelloPacket(player); + } + return data.openFuture; + } + + protected static void sendHelloPacket(Player player) { + player.sendPluginMessage(EaglerXBukkitAPIPlugin.getEagler(), EaglerBackendRPCProtocol.CHANNEL_NAME, HelloPacketFactory.BASE_HELLO_PACKET); + } + + protected static EaglerXBukkitImpl createFromHandshakeInternal(PlayerDataObj playerDataObj, SPacketRPCEnabledSuccess pkt) { + return new EaglerXBukkitImpl(playerDataObj, EaglerBackendRPCProtocol.getByID(pkt.selectedRPCProtocol), pkt.playerClientProtocol); + } + + protected final PlayerDataObj playerDataObj; + protected final Player playerObj; + protected final EaglerBackendRPCProtocol protocol; + protected final int gameProtocol; + protected boolean open; + protected final Map metadata = new ConcurrentHashMap<>(4); + protected final EaglerXBukkitAPIHandler packetHandler; + protected int subscribedEvents = 0; + protected final ListMultimap> eventHandlers = ListMultimapBuilder + .hashKeys().arrayListValues().build(); + protected final List closeHandlers = new ArrayList<>(4); + protected final Map> waitingRequests = new ConcurrentHashMap<>(); + protected int baseTimeout = DEFAULT_TIMEOUT; + protected final AtomicInteger requestIDGenerator = new AtomicInteger(); + private final ReentrantLock inputStreamLock = new ReentrantLock(); + private final ReentrantLock outputStreamLock = new ReentrantLock(); + private final ReusableByteArrayInputStream reusableInputStream = new ReusableByteArrayInputStream(); + private final ReusableByteArrayOutputStream reusableOutputStream = new ReusableByteArrayOutputStream(); + private final DataInputStream dataInputStream = new DataInputStream(reusableInputStream); + private final DataOutputStream dataOutputStream = new DataOutputStream(reusableOutputStream); + + protected EaglerXBukkitImpl(PlayerDataObj playerDataObj, EaglerBackendRPCProtocol protocol, int gameProtocol) { + this.playerDataObj = playerDataObj; + this.playerObj = playerDataObj.player; + this.protocol = protocol; + this.gameProtocol = gameProtocol; + this.open = true; + this.packetHandler = new EaglerXBukkitAPIHandler(); + } + + @Override + public EaglerBackendRPCProtocol getRPCProtocolVersion() { + return protocol; + } + + @Override + public int getEaglerProtocolVersion() { + return gameProtocol; + } + + @Override + public boolean isOpen() { + return open; + } + + @Override + public void closeAPI() { + if(open) { + try { + sendRPCPacket(new CPacketRPCDisabled()); + }finally { + fireAPIClosedEventInternal(); + } + } + } + + protected void fireAPIClosedEventInternal() { + if(!open) return; + open = false; + synchronized(closeHandlers) { + for(int i = 0, l = closeHandlers.size(); i < l; ++i) { + IEaglerRPCCloseHandler hd = closeHandlers.get(i); + try { + hd.handleEvent(this); + }catch(Throwable t) { + EaglerXBukkitAPIPlugin.logger().log(Level.SEVERE, + "[" + playerObj.getName() + "] caught exception while firing close handler " + hd, t); + } + } + } + } + + protected void fireAPIPacketRecievedInternal(EaglerBackendRPCPacket ret) { + ret.handlePacket(packetHandler); + } + + protected void fireEventHandlers(T eventObj) { + EnumSubscribeEvents type = eventObj.getType(); + List> lst; + synchronized(eventHandlers) { + lst = eventHandlers.get(type); + } + for(int i = 0, l = lst.size(); i < l; ++i) { + IEaglerRPCEventHandler handler = (IEaglerRPCEventHandler) lst.get(i); + try { + handler.handleEvent(this, type, eventObj); + }catch(Throwable t) { + EaglerXBukkitAPIPlugin.logger().log(Level.SEVERE, + "[" + playerObj.getName() + "] caught exception while processing event type " + + type + " using handler " + handler, t); + } + } + } + + protected class EaglerXBukkitAPIHandler implements EaglerBackendRPCHandler { + + public void handleServer(SPacketRPCResponseTypeNull packet) { + EaglerRPCFutureImpl future = waitingRequests.remove(packet.requestID); + if(future != null) { + future.fireCompleteInternal(null); + } + } + + public void handleServer(SPacketRPCResponseTypeBytes packet) { + EaglerRPCFutureImpl future = (EaglerRPCFutureImpl)waitingRequests.remove(packet.requestID); + if(future != null) { + future.fireCompleteInternal(new ResponseBytes(EaglerXBukkitImpl.this, packet.requestID, packet.response)); + } + } + + public void handleServer(SPacketRPCResponseTypeString packet) { + EaglerRPCFutureImpl future = (EaglerRPCFutureImpl)waitingRequests.remove(packet.requestID); + if(future != null) { + future.fireCompleteInternal(new ResponseString(EaglerXBukkitImpl.this, packet.requestID, packet.response)); + } + } + + public void handleServer(SPacketRPCResponseTypeUUID packet) { + EaglerRPCFutureImpl future = (EaglerRPCFutureImpl)waitingRequests.remove(packet.requestID); + if(future != null) { + future.fireCompleteInternal(new ResponseUUID(EaglerXBukkitImpl.this, packet.requestID, packet.uuid)); + } + } + + public void handleServer(SPacketRPCResponseTypeCookie packet) { + EaglerRPCFutureImpl future = (EaglerRPCFutureImpl)waitingRequests.remove(packet.requestID); + if(future != null) { + future.fireCompleteInternal(new ResponseCookie(EaglerXBukkitImpl.this, packet.requestID, packet.cookiesEnabled, packet.cookieData)); + } + } + + public void handleServer(SPacketRPCResponseTypeVoiceStatus packet) { + EaglerRPCFutureImpl future = (EaglerRPCFutureImpl)waitingRequests.remove(packet.requestID); + if(future != null) { + future.fireCompleteInternal(new ResponseVoiceStatus(EaglerXBukkitImpl.this, packet.requestID, translateVoiceState2(packet.voiceState))); + } + } + + public void handleServer(SPacketRPCResponseTypeWebViewStatus packet) { + EaglerRPCFutureImpl future = (EaglerRPCFutureImpl)waitingRequests.remove(packet.requestID); + if(future != null) { + future.fireCompleteInternal(new ResponseWebViewStatus(EaglerXBukkitImpl.this, packet.requestID, translateWebViewState(packet.webviewState), packet.channelName)); + } + } + + public void handleServer(SPacketRPCResponseTypeError packet) { + EaglerRPCFutureImpl future = (EaglerRPCFutureImpl)waitingRequests.remove(packet.requestID); + if(future != null) { + future.fireExceptionInternal(new EaglerRPCResponseException(packet.errorMessage)); + } + } + + public void handleServer(SPacketRPCEventWebViewOpenClose packet) { + if((subscribedEvents & EnumSubscribeEvents.EVENT_WEBVIEW_OPEN_CLOSE.bit) != 0) { + fireEventHandlers(new EventWebViewOpenClose(packet.channelName, packet.channelOpen)); + } + } + + public void handleServer(SPacketRPCEventWebViewMessage packet) { + if((subscribedEvents & EnumSubscribeEvents.EVENT_WEBVIEW_MESSAGE.bit) != 0) { + EventWebViewMessage.MessageType mt; + switch(packet.messageType) { + case SPacketRPCEventWebViewMessage.MESSAGE_TYPE_STRING: + mt = EventWebViewMessage.MessageType.STRING; + break; + case SPacketRPCEventWebViewMessage.MESSAGE_TYPE_BINARY: + mt = EventWebViewMessage.MessageType.BINARY; + break; + default: + return; + } + fireEventHandlers(new EventWebViewMessage(packet.channelName, mt, packet.messageContent)); + } + } + + public void handleServer(SPacketRPCEventToggledVoice packet) { + if((subscribedEvents & EnumSubscribeEvents.EVENT_TOGGLE_VOICE.bit) != 0) { + EventToggledVoice.VoiceState vsOld = translateVoiceState(packet.oldVoiceState); + EventToggledVoice.VoiceState vsNew = translateVoiceState(packet.newVoiceState); + if(vsOld == null || vsNew == null) { + return; + } + fireEventHandlers(new EventToggledVoice(vsOld, vsNew)); + } + } + + } + + private static EventToggledVoice.VoiceState translateVoiceState(int vs) { + switch(vs) { + case SPacketRPCResponseTypeVoiceStatus.VOICE_STATE_SERVER_DISABLE: + return EventToggledVoice.VoiceState.SERVER_DISABLE; + case SPacketRPCResponseTypeVoiceStatus.VOICE_STATE_DISABLED: + return EventToggledVoice.VoiceState.DISABLED; + case SPacketRPCResponseTypeVoiceStatus.VOICE_STATE_ENABLED: + return EventToggledVoice.VoiceState.ENABLED; + default: + return null; + } + } + + private static ResponseVoiceStatus.VoiceState translateVoiceState2(int vs) { + switch(vs) { + case SPacketRPCEventToggledVoice.VOICE_STATE_SERVER_DISABLE: + default: + return ResponseVoiceStatus.VoiceState.SERVER_DISABLE; + case SPacketRPCEventToggledVoice.VOICE_STATE_DISABLED: + return ResponseVoiceStatus.VoiceState.DISABLED; + case SPacketRPCEventToggledVoice.VOICE_STATE_ENABLED: + return ResponseVoiceStatus.VoiceState.ENABLED; + } + } + + private static ResponseWebViewStatus.WebViewState translateWebViewState(int vs) { + switch(vs) { + case SPacketRPCResponseTypeWebViewStatus.WEBVIEW_STATE_NOT_SUPPORTED: + default: + return ResponseWebViewStatus.WebViewState.NOT_SUPPORTED; + case SPacketRPCResponseTypeWebViewStatus.WEBVIEW_STATE_SERVER_DISABLE: + return ResponseWebViewStatus.WebViewState.SERVER_DISABLE; + case SPacketRPCResponseTypeWebViewStatus.WEBVIEW_STATE_CHANNEL_CLOSED: + return ResponseWebViewStatus.WebViewState.CHANNEL_CLOSED; + case SPacketRPCResponseTypeWebViewStatus.WEBVIEW_STATE_CHANNEL_OPEN: + return ResponseWebViewStatus.WebViewState.CHANNEL_OPEN; + } + } + + @Override + public Player getPlayer() { + return playerObj; + } + + @Override + public void sendRPCPacket(EaglerBackendRPCPacket packet) { + if(!open) { + EaglerXBukkitAPIPlugin.logger().warning("[" + playerObj.getName() + "] Sent " + packet.getClass().getSimpleName() + " on a dead connection!"); + return; + } + if(packet == null) { + throw new NullPointerException("Packet cannot be null!"); + } + byte[] ret; + int len = packet.length() + 1; + if(outputStreamLock.tryLock()) { + try { + reusableOutputStream.feedBuffer(new byte[len > 0 ? len : 64]); + try { + protocol.writePacket(dataOutputStream, EaglerBackendRPCProtocol.CLIENT_TO_SERVER, packet); + }catch(IOException ex) { + throw new EaglerRPCException("Failed to serialize packet: " + packet.getClass().getSimpleName(), ex); + } + ret = reusableOutputStream.returnBuffer(); + }finally { + outputStreamLock.unlock(); + } + }else { + ReusableByteArrayOutputStream bao = new ReusableByteArrayOutputStream(); + bao.feedBuffer(new byte[len > 0 ? len : 64]); + try { + protocol.writePacket(new DataOutputStream(bao), EaglerBackendRPCProtocol.CLIENT_TO_SERVER, packet); + }catch(IOException ex) { + throw new EaglerRPCException("Failed to serialize packet: " + packet.getClass().getSimpleName(), ex); + } + ret = bao.returnBuffer(); + } + if(len > 0 && len != ret.length) { + EaglerXBukkitAPIPlugin.logger() + .warning("[" + playerObj.getName() + "] Packet type " + packet.getClass().getSimpleName() + + " was the wrong length after serialization: " + ret.length + " != " + len); + } + playerObj.sendPluginMessage(EaglerXBukkitAPIPlugin.getEagler(), EaglerBackendRPCProtocol.CHANNEL_NAME, ret); + } + + protected EaglerBackendRPCPacket decodePacket(byte[] data) throws IOException { + EaglerBackendRPCPacket ret; + if(inputStreamLock.tryLock()) { + try { + reusableInputStream.feedBuffer(data); + ret = protocol.readPacket(dataInputStream, EaglerBackendRPCProtocol.SERVER_TO_CLIENT); + }finally { + inputStreamLock.unlock(); + } + }else { + ReusableByteArrayInputStream bai = new ReusableByteArrayInputStream(); + bai.feedBuffer(data); + ret = protocol.readPacket(new DataInputStream(bai), EaglerBackendRPCProtocol.SERVER_TO_CLIENT); + } + return ret; + } + + @Override + public void subscribeEvents(int events) { + int newEvents = subscribedEvents | events; + if(newEvents != subscribedEvents) { + sendRPCPacket(new CPacketRPCSubscribeEvents(newEvents)); + subscribedEvents = events; + } + } + + @Override + public void unsubscribeEvents(int events) { + int newEvents = subscribedEvents & ~events; + if(newEvents != subscribedEvents) { + sendRPCPacket(new CPacketRPCSubscribeEvents(newEvents)); + subscribedEvents = events; + } + } + + @Override + public void unsubscribeAllEvents() { + if(subscribedEvents != 0) { + sendRPCPacket(new CPacketRPCSubscribeEvents(0)); + subscribedEvents = 0; + } + } + + @Override + public int getSubscribedEventsBits() { + return subscribedEvents; + } + + @Override + public void addEventListener(EnumSubscribeEvents eventType, + IEaglerRPCEventHandler handler) { + synchronized(eventHandlers) { + eventHandlers.put(eventType, handler); + } + } + + @Override + public void removeEventListener(EnumSubscribeEvents eventType, + IEaglerRPCEventHandler handler) { + synchronized(eventHandlers) { + eventHandlers.remove(eventType, handler); + } + } + + @Override + public void removeEventListeners(EnumSubscribeEvents eventType) { + synchronized(eventHandlers) { + eventHandlers.removeAll(eventType); + } + } + + @Override + public void addCloseListener(IEaglerRPCCloseHandler handler) { + synchronized(closeHandlers) { + closeHandlers.add(handler); + } + } + + @Override + public void removeCloseListener(IEaglerRPCCloseHandler handler) { + synchronized(closeHandlers) { + closeHandlers.remove(handler); + } + } + + @Override + public void removeCloseListeners() { + synchronized(closeHandlers) { + closeHandlers.clear(); + } + } + + @Override + public boolean redirectPlayerSupported() { + return gameProtocol >= 4; + } + + @Override + public void redirectPlayerToWebSocket(String webSocketURI) { + if(gameProtocol >= 4) { + if(webSocketURI == null) { + throw new NullPointerException("URI cannot be null!"); + } + sendRPCPacket(new CPacketRPCRedirectPlayer(webSocketURI)); + }else { + EaglerXBukkitAPIPlugin.logger() + .warning("[" + playerObj.getName() + "] some plugin tried to redirect player to \"" + webSocketURI + + "\" but that player's client does not support websocket redirects!"); + } + } + + @Override + public void setBaseRequestTimeout(int seconds) { + baseTimeout = seconds * 1000; + } + + protected IEaglerRPCFuture requestSendHelper(int type) { + EaglerRPCFutureImpl ret = new EaglerRPCFutureImpl(); + ret.setExpiresMSFromNow(baseTimeout); + int rqid = requestIDGenerator.incrementAndGet(); + sendRPCPacket(new CPacketRPCRequestPlayerInfo(rqid, type)); + waitingRequests.put(rqid, ret); + return ret; + } + + @Override + public IEaglerRPCFuture requestPlayerProfileUUID() { + return requestSendHelper(CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_REAL_UUID); + } + + @Override + public IEaglerRPCFuture requestPlayerRealIP() { + return requestSendHelper(CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_REAL_IP); + } + + @Override + public IEaglerRPCFuture requestPlayerOrigin() { + return requestSendHelper(CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_ORIGIN); + } + + @Override + public IEaglerRPCFuture requestPlayerUserAgent() { + return requestSendHelper(CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_USER_AGENT); + } + + @Override + public IEaglerRPCFuture requestPlayerSkinData() { + return requestSendHelper(CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_SKIN_DATA); + } + + @Override + public IEaglerRPCFuture requestPlayerCapeData() { + return requestSendHelper(CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_CAPE_DATA); + } + + @Override + public IEaglerRPCFuture requestPlayerCookieData() { + return requestSendHelper(CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_COOKIE); + } + + @Override + public IEaglerRPCFuture requestPlayerClientBrandStr() { + return requestSendHelper(CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_CLIENT_BRAND_STR); + } + + @Override + public IEaglerRPCFuture requestPlayerClientVersionStr() { + return requestSendHelper(CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_CLIENT_VERSION_STR); + } + + @Override + public IEaglerRPCFuture requestPlayerClientBrandAndVersionStr() { + return requestSendHelper(CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_CLIENT_BRAND_VERSION_STR); + } + + @Override + public IEaglerRPCFuture requestPlayerClientBrandUUID() { + return requestSendHelper(CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_CLIENT_BRAND_UUID); + } + + @Override + public IEaglerRPCFuture requestPlayerVoiceStatus() { + return requestSendHelper(CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_CLIENT_VOICE_STATUS); + } + + @Override + public IEaglerRPCFuture requestPlayerWebViewStatus() { + return requestSendHelper(CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_CLIENT_WEBVIEW_STATUS); + } + + protected void cleanupTimedOutRequests(long now) { + if(!waitingRequests.isEmpty()) { + List> expired = null; + Iterator> itr = waitingRequests.values().iterator(); + while(itr.hasNext()) { + EaglerRPCFutureImpl itm = itr.next(); + if(itm.hasExpiredBetter(now)) { + if(expired == null) { + expired = new ArrayList<>(4); + } + expired.add(itm); + try { + itr.remove(); + }catch(Throwable t) { + } + } + } + if(expired != null) { + for(int i = 0, l = expired.size(); i < l; ++i) { + try { + EaglerRPCFutureImpl itm = expired.get(i); + EaglerXBukkitAPIPlugin.logger().warning("[" + playerObj.getName() + "] An RPC request timed out before it could be completed!"); + itm.fireExceptionInternal(new EaglerRPCTimeoutException("The request was not completed in time!")); + }catch(Throwable t) { + EaglerXBukkitAPIPlugin.logger().log(Level.SEVERE, "[" + playerObj.getName() + "] An unhandled exception was thrown while firing request timeout signal!", t); + } + } + } + } + } + + @Override + public void sendRawCustomPayloadPacket(String channel, byte[] data) { + sendRPCPacket(new CPacketRPCSendRawMessage(channel, data)); + } + + @Override + public boolean pauseMenuCustomizationSupported() { + return gameProtocol >= 4; + } + + @Override + public void setPauseMenuCustomizationState(CPacketRPCSetPauseMenuCustom packet) { + if(gameProtocol >= 4) { + sendRPCPacket(packet); + }else { + EaglerXBukkitAPIPlugin.logger().warning("[" + playerObj.getName() + + "] some plugin tried to configure pause menu customization, but the player's client does not support that feature!"); + } + } + + @Override + public void sendWebViewMessageString(String channelName, String data) { + if(gameProtocol >= 4) { + if(channelName == null) { + throw new NullPointerException("Channel cannot be null!"); + } + if(data == null) { + throw new NullPointerException("Data cannot be null!"); + } + sendRPCPacket(new CPacketRPCSendWebViewMessage(channelName, + CPacketRPCSendWebViewMessage.MESSAGE_TYPE_STRING, data.getBytes(StandardCharsets.UTF_8))); + }else { + EaglerXBukkitAPIPlugin.logger().warning("[" + playerObj.getName() + + "] some plugin tried to send a webview channel message, but the player's client does not support that feature!"); + } + } + + @Override + public void sendWebViewMessageBytes(String channelName, byte[] data) { + if(gameProtocol >= 4) { + if(channelName == null) { + throw new NullPointerException("Channel cannot be null!"); + } + if(data == null) { + throw new NullPointerException("Data cannot be null!"); + } + sendRPCPacket(new CPacketRPCSendWebViewMessage(channelName, CPacketRPCSendWebViewMessage.MESSAGE_TYPE_BINARY, data)); + }else { + EaglerXBukkitAPIPlugin.logger().warning("[" + playerObj.getName() + + "] some plugin tried to send a webview channel message, but the player's client does not support that feature!"); + } + } + + @Override + public void forcePlayerSkin(byte[] skinData, boolean notifyOthers) { + if(skinData == null) { + throw new NullPointerException("Skin data cannot be null!"); + } + if(skinData.length > 32720) { + throw new IllegalArgumentException("Skin data cannot be more than 32720 bytes!"); + } + sendRPCPacket(new CPacketRPCSetPlayerSkin(notifyOthers, skinData)); + } + + @Override + public void forcePlayerCape(byte[] capeData, boolean notifyOthers) { + if(capeData == null) { + throw new NullPointerException("Cape data cannot be null!"); + } + if(capeData.length > 32720) { + throw new IllegalArgumentException("Cape data cannot be more than 32720 bytes!"); + } + sendRPCPacket(new CPacketRPCSetPlayerCape(notifyOthers, capeData)); + } + + @Override + public void setCookieData(byte[] cookieData, int expiresAfterSec, boolean revokeQuerySupported, + boolean saveToDisk) { + if(gameProtocol >= 4) { + sendRPCPacket(new CPacketRPCSetPlayerCookie(revokeQuerySupported, saveToDisk, expiresAfterSec, cookieData)); + }else { + EaglerXBukkitAPIPlugin.logger().warning("[" + playerObj.getName() + + "] some plugin tried to set a cookie, but the player's client does not support that feature!"); + } + } + + @Override + public void setFNAWSkinsEnabled(boolean enabled, boolean force) { + sendRPCPacket(new CPacketRPCSetPlayerFNAWEn(enabled, force)); + } + + @Override + public void setFNAWSkinsEnabled(boolean enabled) { + sendRPCPacket(new CPacketRPCSetPlayerFNAWEn(enabled, false)); + } + + @Override + public void resetForcedMulti(boolean resetSkin, boolean resetCape, boolean resetFNAWForce, boolean notifyOtherPlayers) { + sendRPCPacket(new CPacketRPCResetPlayerMulti(resetSkin, resetCape, resetFNAWForce, notifyOtherPlayers)); + } + + @Override + public void resetForcedSkin(boolean notifyOtherPlayers) { + sendRPCPacket(new CPacketRPCResetPlayerMulti(true, false, false, notifyOtherPlayers)); + } + + @Override + public void resetForcedCape(boolean notifyOtherPlayers) { + sendRPCPacket(new CPacketRPCResetPlayerMulti(false, true, false, notifyOtherPlayers)); + } + + @Override + public void resetForcedFNAW() { + sendRPCPacket(new CPacketRPCResetPlayerMulti(false, false, true, false)); + } + + @Override + public boolean notifSupported() { + return gameProtocol >= 4; + } + + @Override + public void notifIconRegister(Map iconsToRegister) { + if(gameProtocol >= 4) { + sendRPCPacket(new CPacketRPCNotifIconRegister(iconsToRegister)); + }else { + EaglerXBukkitAPIPlugin.logger().warning("[" + playerObj.getName() + + "] some plugin tried to register notification icons, but the player's client does not support that feature!"); + } + } + + @Override + public void notifIconRegister(UUID iconUUID, PacketImageData imageData) { + if(gameProtocol >= 4) { + Map toReg = new HashMap<>(1); + toReg.put(iconUUID, imageData); + sendRPCPacket(new CPacketRPCNotifIconRegister(toReg)); + }else { + EaglerXBukkitAPIPlugin.logger().warning("[" + playerObj.getName() + + "] some plugin tried to register notification icons, but the player's client does not support that feature!"); + } + } + + @Override + public void notifIconRelease(Collection iconsToRelease) { + if(gameProtocol >= 4) { + sendRPCPacket(new CPacketRPCNotifIconRelease(iconsToRelease)); + }else { + EaglerXBukkitAPIPlugin.logger().warning("[" + playerObj.getName() + + "] some plugin tried to release notification icons, but the player's client does not support that feature!"); + } + } + + @Override + public void notifIconRelease(UUID iconUUID) { + if(gameProtocol >= 4) { + sendRPCPacket(new CPacketRPCNotifIconRelease(Arrays.asList(iconUUID))); + }else { + EaglerXBukkitAPIPlugin.logger().warning("[" + playerObj.getName() + + "] some plugin tried to release notification icons, but the player's client does not support that feature!"); + } + } + + @Override + public void notifBadgeShow(CPacketRPCNotifBadgeShow packet) { + if(gameProtocol >= 4) { + sendRPCPacket(packet); + }else { + EaglerXBukkitAPIPlugin.logger().warning("[" + playerObj.getName() + + "] some plugin tried to show notification badge, but the player's client does not support that feature!"); + } + } + + @Override + public void notifBadgeHide(UUID badgeUUID) { + if(gameProtocol >= 4) { + sendRPCPacket(new CPacketRPCNotifBadgeHide(badgeUUID)); + }else { + EaglerXBukkitAPIPlugin.logger().warning("[" + playerObj.getName() + + "] some plugin tried to hide notification badge, but the player's client does not support that feature!"); + } + } + + @Override + public void setMeta(String key, T value) { + if(key == null) { + throw new NullPointerException("Key cannot be null!"); + } + metadata.put(key, value); + } + + @Override + public T getMeta(String key) { + if(key == null) { + throw new NullPointerException("Key cannot be null!"); + } + return (T)metadata.get(key); + } + +} diff --git a/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/impl/HelloPacketFactory.java b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/impl/HelloPacketFactory.java new file mode 100644 index 0000000..7b5a608 --- /dev/null +++ b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/impl/HelloPacketFactory.java @@ -0,0 +1,41 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.impl; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.EaglerBackendRPCProtocol; +import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.client.CPacketRPCEnabled; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class HelloPacketFactory { + + public static final byte[] BASE_HELLO_PACKET; + + static { + try { + ByteArrayOutputStream bao = new ByteArrayOutputStream(); + DataOutputStream dao = new DataOutputStream(bao); + CPacketRPCEnabled pkt = new CPacketRPCEnabled(new int[] { EaglerBackendRPCProtocol.V1.vers }); + EaglerBackendRPCProtocol.INIT.writePacket(dao, EaglerBackendRPCProtocol.CLIENT_TO_SERVER, pkt); + BASE_HELLO_PACKET = bao.toByteArray(); + }catch(IOException ex) { + throw new RuntimeException(ex); + } + } + +} diff --git a/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/impl/PlayerDataObj.java b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/impl/PlayerDataObj.java new file mode 100644 index 0000000..13b3c17 --- /dev/null +++ b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/impl/PlayerDataObj.java @@ -0,0 +1,196 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.impl; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.util.List; +import java.util.logging.Level; + +import org.bukkit.entity.Player; +import org.bukkit.metadata.LazyMetadataValue; +import org.bukkit.metadata.MetadataValue; + +import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.EaglerBackendRPCProtocol; +import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.EaglerBackendRPCPacket; +import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.server.SPacketRPCEnabledFailure; +import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.server.SPacketRPCEnabledSuccess; +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.EaglerXBukkitAPIPlugin; +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.EaglerRPCException; +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.EaglerRPCInitException; +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.IEaglerXBukkitAPI; +import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.response.EaglerRPCTimeoutException; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class PlayerDataObj { + + public static final String METADATA_BASE = "EXRPC_PDataObj"; + + public final Player player; + + public volatile boolean hasRecievedReady = false; + public volatile boolean isSupported = true; + public volatile EaglerXBukkitImpl currentAPI = null; + + public volatile EaglerRPCFutureImpl openFuture = null; + + public static PlayerDataObj getForPlayer(Player player) { + List vigg = player.getMetadata(METADATA_BASE); + return !vigg.isEmpty() ? (PlayerDataObj)vigg.get(0).value() : null; + } + + public static void setupPlayer(Player player) { + player.setMetadata(METADATA_BASE, new LazyMetadataValue(EaglerXBukkitAPIPlugin.getEagler(), () -> new PlayerDataObj(player))); + } + + protected PlayerDataObj(Player player) { + this.player = player; + } + + public void firePluginReadyMsgRecieved() { + synchronized(this) { + if(!hasRecievedReady) { + hasRecievedReady = true; + if(openFuture != null) { + EaglerXBukkitImpl.sendHelloPacket(player); + } + } + } + } + + public void firePluginMsgRecievedInternal(byte[] data) { + EaglerXBukkitImpl apiObj = null; + synchronized(this) { + if(openFuture != null) { + try { + handleOpenResult(openFuture, data); + }finally { + openFuture = null; + } + }else if(currentAPI != null) { + apiObj = currentAPI; + } + } + if(apiObj != null) { + handleAPIMessage(apiObj, data); + } + } + + public void firePlayerQuitEventInternal() { + synchronized(this) { + if(openFuture != null) { + try { + openFuture.fireExceptionInternal(new EaglerRPCException("Player quit before the connection could be established!")); + }finally { + openFuture = null; + } + }else if(currentAPI != null) { + currentAPI.fireAPIClosedEventInternal(); + } + } + } + + private void handleOpenResult(EaglerRPCFutureImpl apiFuture, byte[] data) { + EaglerBackendRPCPacket ret; + try { + ByteArrayInputStream bis = new ByteArrayInputStream(data); + ret = EaglerBackendRPCProtocol.INIT.readPacket(new DataInputStream(bis), EaglerBackendRPCProtocol.SERVER_TO_CLIENT); + if(bis.available() > 0) { + throw new IOException("There were " + bis.available() + " bytes available after reading packet \"" + ret.getClass().getSimpleName() + "\"!"); + } + }catch(IOException ex) { + EaglerXBukkitAPIPlugin.logger().log(Level.SEVERE, "[" + player.getName() + "] Could not parse incoming RPC packet from bungee/velocity server! (protocol: INIT)", ex); + apiFuture.fireExceptionInternal(ex); + return; + } + if(ret instanceof SPacketRPCEnabledSuccess) { + SPacketRPCEnabledSuccess pkt = (SPacketRPCEnabledSuccess)ret; + if(pkt.selectedRPCProtocol != EaglerBackendRPCProtocol.V1.vers) { + try { + // send raw CPacketRPCDisabled + player.sendPluginMessage(EaglerXBukkitAPIPlugin.getEagler(), EaglerBackendRPCProtocol.CHANNEL_NAME, new byte[] { 0x03 }); + }finally { + apiFuture.fireExceptionInternal(new EaglerRPCException("Server tried to select an unsupported protocol: " + pkt.selectedRPCProtocol)); + } + }else { + currentAPI = EaglerXBukkitImpl.createFromHandshakeInternal(this, pkt); + apiFuture.fireCompleteInternal(currentAPI); + } + }else if(ret instanceof SPacketRPCEnabledFailure) { + SPacketRPCEnabledFailure pkt = (SPacketRPCEnabledFailure)ret; + String msg = "Server responded with failure code: "; + switch(pkt.failureCode) { + case SPacketRPCEnabledFailure.FAILURE_CODE_NOT_ENABLED: + msg += "FAILURE_CODE_NOT_ENABLED"; + break; + case SPacketRPCEnabledFailure.FAILURE_CODE_NOT_EAGLER_PLAYER: + msg += "FAILURE_CODE_NOT_EAGLER_PLAYER"; + break; + case SPacketRPCEnabledFailure.FAILURE_CODE_OUTDATED_SERVER: + msg += "FAILURE_CODE_OUTDATED_SERVER"; + break; + case SPacketRPCEnabledFailure.FAILURE_CODE_OUTDATED_CLIENT: + msg += "FAILURE_CODE_OUTDATED_CLIENT"; + break; + case SPacketRPCEnabledFailure.FAILURE_CODE_INTERNAL_ERROR: + msg += "FAILURE_CODE_INTERNAL_ERROR"; + break; + default: + msg += pkt.failureCode; + break; + } + apiFuture.fireExceptionInternal(new EaglerRPCInitException(pkt.failureCode, msg)); + }else { + EaglerXBukkitAPIPlugin.logger().severe("[" + player.getName() + "] Unknown response type from bungee/velocity to API open request: " + ret.getClass().getSimpleName()); + } + } + + private void handleAPIMessage(EaglerXBukkitImpl apiObj, byte[] data) { + EaglerBackendRPCPacket ret; + try { + ret = apiObj.decodePacket(data); + }catch(IOException ex) { + EaglerXBukkitAPIPlugin.logger().log(Level.SEVERE, "[" + player.getName() + "] Could not parse incoming RPC packet from bungee/velocity server! (protocol: " + apiObj.getRPCProtocolVersion() + ")", ex); + return; + } + apiObj.fireAPIPacketRecievedInternal(ret); + } + + public void fireCheckRequestTimeoutsInternal(long now) { + EaglerXBukkitImpl apiObj; + synchronized(this) { + if(openFuture != null) { + if(openFuture.hasExpiredBetter(now)) { + try { + EaglerXBukkitAPIPlugin.logger().warning("[" + player.getName() + "] An RPC open request timed out before it could be completed!"); + openFuture.fireExceptionInternal(new EaglerRPCTimeoutException("The request was not completed in time!")); + }catch(Throwable t) { + EaglerXBukkitAPIPlugin.logger().log(Level.SEVERE, "[" + player.getName() + "] An unhandled exception was thrown while firing request timeout signal!", t); + }finally { + openFuture = null; + } + } + return; + }else { + apiObj = currentAPI; + } + } + if(apiObj != null) { + apiObj.cleanupTimedOutRequests(now); + } + } + +} diff --git a/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/impl/ReusableByteArrayInputStream.java b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/impl/ReusableByteArrayInputStream.java new file mode 100644 index 0000000..543797f --- /dev/null +++ b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/impl/ReusableByteArrayInputStream.java @@ -0,0 +1,80 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.impl; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class ReusableByteArrayInputStream extends InputStream { + + private volatile byte[] currentBuffer = null; + private int idx = 0; + private int markIDX = 0; + + public void feedBuffer(byte[] b) { + currentBuffer = b; + idx = 0; + markIDX = 0; + } + + @Override + public int read() throws IOException { + if(currentBuffer.length <= idx) throw new IOException("ReusableByteArrayInputStream buffer underflow, no bytes remaining"); + return (int)currentBuffer[idx++] & 0xFF; + } + + @Override + public int read(byte b[], int off, int len) throws IOException { + if(idx + len > currentBuffer.length) { + throw new IOException( + "ReusableByteArrayInputStream buffer underflow, tried to read " + len + " when there are only " + + (currentBuffer.length - idx) + " bytes remaining", + new ArrayIndexOutOfBoundsException(idx + len - 1)); + } + if(off + len > b.length) { + throw new ArrayIndexOutOfBoundsException(off + len - 1); + } + System.arraycopy(currentBuffer, idx, b, off, len); + idx += len; + return len; + } + + public void mark() { + markIDX = idx; + } + + public void reset() { + idx = markIDX; + } + + public int getReaderIndex() { + return idx; + } + + public int available() { + return Math.max(currentBuffer.length - idx, 0); + } + + public void setReaderIndex(int i) { + idx = i; + markIDX = idx; + } + + public boolean markSupported() { + return true; + } + +} diff --git a/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/impl/ReusableByteArrayOutputStream.java b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/impl/ReusableByteArrayOutputStream.java new file mode 100644 index 0000000..cbafe88 --- /dev/null +++ b/gateway/EaglercraftXBukkitAPI/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/bukkit_rpc_helper/impl/ReusableByteArrayOutputStream.java @@ -0,0 +1,81 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.impl; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Arrays; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class ReusableByteArrayOutputStream extends OutputStream { + + private volatile byte[] currentBuffer = null; + private int idx = 0; + private int originalSize = 0; + + public void feedBuffer(byte[] buf) { + currentBuffer = buf; + idx = 0; + originalSize = buf == null ? 0 : buf.length; + } + + public boolean hasGrown() { + return currentBuffer.length != originalSize; + } + + public byte[] returnBuffer() { + return currentBuffer.length == idx ? currentBuffer : Arrays.copyOf(currentBuffer, idx); + } + + private void growBuffer(int i) { + int ii = currentBuffer.length; + int iii = i - ii; + if(iii > 0) { + int j = ii + (ii >> 1); + while(j < i) { + j += (j >> 1); + } + byte[] n = new byte[j]; + System.arraycopy(currentBuffer, 0, n, 0, ii); + currentBuffer = n; + } + } + + public int getWriterIndex() { + return idx; + } + + public void setWriterIndex(int i) { + idx = i; + } + + @Override + public void write(int b) throws IOException { + if(idx >= currentBuffer.length) { + growBuffer(idx + 1); + } + currentBuffer[idx++] = (byte) b; + } + + @Override + public void write(byte b[], int off, int len) throws IOException { + if(idx + len > currentBuffer.length) { + growBuffer(idx + len); + } + System.arraycopy(b, off, currentBuffer, idx, len); + idx += len; + } + +} diff --git a/gateway/EaglercraftXBukkitAPI/src/main/resources/plugin.yml b/gateway/EaglercraftXBukkitAPI/src/main/resources/plugin.yml new file mode 100644 index 0000000..58d06d1 --- /dev/null +++ b/gateway/EaglercraftXBukkitAPI/src/main/resources/plugin.yml @@ -0,0 +1,6 @@ +name: EaglercraftXBukkitAPI +version: 1.0.0 +main: net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.EaglerXBukkitAPIPlugin +description: Official EaglercraftX API for Bukkit servers +author: lax1dude +api-version: 1.8 \ No newline at end of file