diff options
author | HacktheTime <l4bg0jb7@duck.com> | 2023-08-09 20:46:00 +0200 |
---|---|---|
committer | HacktheTime <l4bg0jb7@duck.com> | 2023-08-09 20:46:00 +0200 |
commit | 2cd5f8e6bd42a084022028adcc24ef566fddab95 (patch) | |
tree | e06a18df40629371c33b9efce971489812696ac0 | |
download | BBsentials-2cd5f8e6bd42a084022028adcc24ef566fddab95.tar.gz BBsentials-2cd5f8e6bd42a084022028adcc24ef566fddab95.tar.bz2 BBsentials-2cd5f8e6bd42a084022028adcc24ef566fddab95.zip |
Initial commit
34 files changed, 2599 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a5ec8b8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +# Default ignored files +*.DS_Store +.gradle +.settings +.project +.classpath +build +out +run +bin +logs +.github/devbuilds/node_modules +*.iws +*.ipr +*.iml +.idea/* +!.idea/copyright/* +!.idea/scopes/* +/run/usercache.json +/run/config/* +/run/config/
\ No newline at end of file @@ -0,0 +1,5 @@ +Copyright (c) 2023 +All rights reserved. + +Copying and changing code for own projects is allowed as long as you don't copy a lot and use it for different things (Inspiration, looking for solutions etc is normally fine). +Modifying this code to gain a personal advantage by abusing a feature unintentionally is STRICTLY forbidden. Sharing such modified code may even result in legal actions.
\ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..222e4a5 --- /dev/null +++ b/README.md @@ -0,0 +1,40 @@ +# BingoBrewers1.20 +The BingoBrewers mod for the newer versions of the game. +THIS MOD IS NOT AFFILIATED WITH BINGO BREWERS ANYMORE since indigo had a problem with me focusing +on 1.20 and refusing to collaborate with me on request. + +Worked on by +Hype_the_Time alias HacktheTime (Developer) + +IMPORTANT: +This mod is currently for 1.20.1 Fabric. + +I recommend using Fabulously Optimized together with this mod. +Some Features require at least Cit Resewn which is included there. + +Cit resewn is by default configured to check the item for custom textures every tick. +Put it at least to 5 ticks or above (Cit Resewn in mod menu) to have FPS while in Inventories. + +I worked together with ic22487, and he included a costume Splash Hub texture in his Hypixel+ texturepack. +(https://hypixel.net/threads/0-19-1-hypixel-for-1-8-1-12-and-1-20.4174260/) +If you don't want to use a Full texturepack there are texturepacks with only the hub ui and another with only Splash hub over in the release Section. + +**Do not expect any kind of support during Bingo times since those moderating it are likely to sweat it too. +If you have any kind of request** + +You may apply for preannouce¹ if you have a good reason. (Normally people who did something good for the community like splashes,... Specific Bingo maniacs may get it too.) +Do this BEFORE Bingo starts! Bribing (already try) on any Side will Result in a permanent unappealable ban! + + + + + + +¹ preannounce has a slight advantage in timing. (15 sec early chchest) (Splash is notified in intervals of how much time you have left. Preannounce has a slight buff here.) +Splash announce order: +pre with less than 10 mins +everybody with less than 1 min splash +everybody with less than 10 splash +pre with less then 25 mins of splash +everybody else with less than 25 mins remaining +Discord and everybody
\ No newline at end of file diff --git a/TODO.txt b/TODO.txt new file mode 100644 index 0000000..ae0d55f --- /dev/null +++ b/TODO.txt @@ -0,0 +1,17 @@ +master toggle for the mod, automatically disables after bingo but can be enabled manually between bingos, if manually turned off will remain off until turned back on manually +no pet equiped warning +batphone opener, trapper accepter +mining event click integration +chchest click integration +Main: +gui +chchest command (with auto chest coords, detect if structure spawned?) +~~coins/bingo point calculator on items in the bingo shop~~ +conerter of in 36 h into 1 d at 8 am etc +Optional +Social Menu? +bb!role claim for brank etc +mutliple accounts on something +Problem with data collection: +~~crowd source the approximate contributions required for each top x% contributions on community goals~~ + diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..475c32c --- /dev/null +++ b/build.gradle @@ -0,0 +1,89 @@ +plugins { + id 'fabric-loom' version '1.2-SNAPSHOT' + id 'maven-publish' +} + +version = project.mod_version +group = project.maven_group + +repositories { + // Add repositories to retrieve artifacts from in here. + // You should only use this when depending on other mods because + // Loom adds the essential maven repositories to download Minecraft and libraries from automatically. + // See https://docs.gradle.org/current/userguide/declaring_repositories.html + // for more information about repositories. +} + +dependencies { + // To change the versions see the gradle.properties file + minecraft "com.mojang:minecraft:${project.minecraft_version}" + mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" + modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + + // Fabric API. This is technically optional, but you probably want it anyway. + modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" + } + +processResources { + inputs.property "version", project.version + inputs.property "minecraft_version", project.minecraft_version + inputs.property "loader_version", project.loader_version + filteringCharset "UTF-8" + + filesMatching("fabric.mod.json") { + expand "version": project.version, + "minecraft_version": project.minecraft_version, + "loader_version": project.loader_version + } +} + +def targetJavaVersion = 17 +tasks.withType(JavaCompile).configureEach { + // ensure that the encoding is set to UTF-8, no matter what the system default is + // this fixes some edge cases with special characters not displaying correctly + // see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html + // If Javadoc is generated, this must be specified in that task too. + it.options.encoding = "UTF-8" + if (targetJavaVersion >= 10 || JavaVersion.current().isJava10Compatible()) { + it.options.release = targetJavaVersion + } +} + +java { + def javaVersion = JavaVersion.toVersion(targetJavaVersion) + if (JavaVersion.current() < javaVersion) { + toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion) + } + archivesBaseName = project.archives_base_name + // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task + // if it is present. + // If you remove this line, sources will not be generated. + withSourcesJar() +} + +jar { + from("LICENSE") { + rename { "${it}_${project.archivesBaseName}"} + } + manifest { + attributes 'Fabric-MixinConfigs': 'modid.mixin.json', + 'MixinConfigs': 'modid.mixin.json' + } +} + +// configure the maven publication +publishing { + publications { + mavenJava(MavenPublication) { + from components.java + } + } + + // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. + repositories { + // Add repositories to publish to here. + // Notice: This block does NOT have the same function as the block in the top level. + // The repositories here will be used for publishing your artifact, not for + // retrieving dependencies. + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..6ab6d5f --- /dev/null +++ b/gradle.properties @@ -0,0 +1,13 @@ +# Done to increase the memory available to gradle. +org.gradle.jvmargs=-Xmx1G +# Fabric Properties +# check these on https://modmuss50.me/fabric.html +minecraft_version=1.20.1 +yarn_mappings=1.20.1+build.2 +loader_version=0.14.21 +#Fabric api +fabric_version=0.83.1+1.20.1 +# Mod Properties +mod_version=1.0 +maven_group=de.hype +archives_base_name=bbsentials
\ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar Binary files differnew file mode 100644 index 0000000..c1962a7 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.jar diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..37aef8d --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip +networkTimeout=10000 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists @@ -0,0 +1,245 @@ +#!/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/HEAD/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 + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# 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*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + 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 + + +# 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"' + +# 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 \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# 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/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..93e3f59 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,92 @@ +@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=. +@rem This is normally unused +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% equ 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% equ 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! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..f91a4fe --- /dev/null +++ b/settings.gradle @@ -0,0 +1,9 @@ +pluginManagement { + repositories { + maven { + name = 'Fabric' + url = 'https://maven.fabricmc.net/' + } + gradlePluginPortal() + } +} diff --git a/src/main/java/de/hype/bbsentials/api/Discord.java b/src/main/java/de/hype/bbsentials/api/Discord.java new file mode 100644 index 0000000..e8c8887 --- /dev/null +++ b/src/main/java/de/hype/bbsentials/api/Discord.java @@ -0,0 +1,70 @@ +package de.hype.bbsentials.api; + +import de.hype.bbsentials.chat.Chat; +import de.hype.bbsentials.client.BBsentials; +import org.apache.http.HttpEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; + +public class Discord { + public static void sendWebhookMessage(String message) { + Thread thread = new Thread(new Runnable() { + @Override + public void run() { + sendWebhookMessageNoThread(message); + } + }); + thread.start(); + } + + + public static void sendWebhookMessageNoThread(String message) { + CloseableHttpClient httpClient = HttpClients.createDefault(); + String WEBHOOK_URL = "https://discord.com/api/v8/webhooks/1127524566407860276/" + BBsentials.getConfig().getApiKey(); + + + try { + HttpPost httpPost = new HttpPost(WEBHOOK_URL); + + StringEntity jsonEntity = new StringEntity("{\"content\": \"" + message + "\"}", StandardCharsets.UTF_8); + jsonEntity.setContentType("application/json"); + httpPost.setEntity(jsonEntity); + + CloseableHttpResponse response = httpClient.execute(httpPost); + HttpEntity responseEntity = response.getEntity(); + + if (responseEntity != null) { + InputStream inputStream = responseEntity.getContent(); + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); + + String line; + StringBuilder responseBuilder = new StringBuilder(); + while ((line = reader.readLine()) != null) { + responseBuilder.append(line); + } + + String responseString = responseBuilder.toString(); + Chat.sendPrivateMessageToSelf(responseString); + } + + response.close(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + httpClient.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } +} diff --git a/src/main/java/de/hype/bbsentials/api/ISimpleOption.java b/src/main/java/de/hype/bbsentials/api/ISimpleOption.java new file mode 100644 index 0000000..c6e1158 --- /dev/null +++ b/src/main/java/de/hype/bbsentials/api/ISimpleOption.java @@ -0,0 +1,5 @@ +package de.hype.bbsentials.api; + +public interface ISimpleOption { + void set(Object value); +} diff --git a/src/main/java/de/hype/bbsentials/api/Options.java b/src/main/java/de/hype/bbsentials/api/Options.java new file mode 100644 index 0000000..7dc4b0d --- /dev/null +++ b/src/main/java/de/hype/bbsentials/api/Options.java @@ -0,0 +1,13 @@ +package de.hype.bbsentials.api; + +import net.minecraft.client.MinecraftClient; + +public class Options{ + public static void setFov(int value) { + ((ISimpleOption) (Object) MinecraftClient.getInstance().options.getFov()).set(value); + } + public static void setGamma(double value) { + ((ISimpleOption) (Object) MinecraftClient.getInstance().options.getGamma()).set(value); + } +} + diff --git a/src/main/java/de/hype/bbsentials/chat/Chat.java b/src/main/java/de/hype/bbsentials/chat/Chat.java new file mode 100644 index 0000000..780baa5 --- /dev/null +++ b/src/main/java/de/hype/bbsentials/chat/Chat.java @@ -0,0 +1,579 @@ +package de.hype.bbsentials.chat; + +import de.hype.bbsentials.client.BBsentials; +import de.hype.bbsentials.client.Config; +import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; +import net.fabricmc.fabric.api.client.message.v1.ClientSendMessageEvents; +import net.minecraft.client.MinecraftClient; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.Clip; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; +import java.time.Instant; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.FutureTask; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static de.hype.bbsentials.client.BBsentials.config; +import static de.hype.bbsentials.client.BBsentials.getConfig; + +public class Chat { + public Chat() { + init(); + } + + public static String[] getVariableInfo(String packageName, String className) { + List<String> variableInfoList = new ArrayList<>(); + + // Combine the class name with the package name + String fullClassName = packageName + "." + className; + + // Load the class + Class<?> clazz = null; + try { + clazz = Class.forName(fullClassName); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + + // Extract fields of the class + Field[] fields = clazz.getDeclaredFields(); + + // Collect information for each field + for (Field field : fields) { + // Exclude transient fields + if (java.lang.reflect.Modifier.isTransient(field.getModifiers())) { + continue; + } + + String variableName = field.getName(); + String variablePackageName = clazz.getPackage().getName(); + String variableClassName = clazz.getSimpleName(); + + String variableInfo = variableName; + variableInfoList.add(variableInfo); + } + + return variableInfoList.toArray(new String[variableInfoList.size()]); + } + + public static void setVariableValue(Object obj, String variableName, String value) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException { + if (value == null) { + // Handle null value case + sendPrivateMessageToSelf(Formatting.RED + "Invalid value: null"); + return; + } + + Class<?> objClass = obj.getClass(); + Field field = objClass.getDeclaredField(variableName); + field.setAccessible(true); + + // Get the type of the field + Class<?> fieldType = field.getType(); + + // Convert the value to the appropriate type + Object convertedValue = parseValue(value, fieldType); + + if (Modifier.isStatic(field.getModifiers())) { + // If the field is static + field.set(null, convertedValue); + } + else { + field.set(obj, convertedValue); + } + + // Check and output the value of the variable + sendPrivateMessageToSelf(Formatting.GREEN + "The variable " + field.getName() + " is now: " + field.get(obj)); + } + + private static Object parseValue(String value, Class<?> targetType) { + if (targetType == int.class || targetType == Integer.class) { + return Integer.parseInt(value); + } + else if (targetType == double.class || targetType == Double.class) { + return Double.parseDouble(value); + } + else if (targetType == float.class || targetType == Float.class) { + return Float.parseFloat(value); + } + else if (targetType == long.class || targetType == Long.class) { + return Long.parseLong(value); + } + else if (targetType == boolean.class || targetType == Boolean.class) { + return Boolean.parseBoolean(value); + } + else { + // For other types, return the original string value + return value; + } + } + + public static void getVariableValue(Object object, String variableName) throws NoSuchFieldException, IllegalAccessException { + Class<?> objClass = object.getClass(); + Field field = objClass.getDeclaredField(variableName); + field.setAccessible(true); + sendPrivateMessageToSelf(Formatting.GREEN + "The variable " + field.getName() + " is: " + field.get(object)); + } + + private void init() { + // Register a callback for a custom message type + ClientReceiveMessageEvents.CHAT.register((message, signedMessage, sender, params, receptionTimestamp) -> { + onEvent(message); + }); + ClientReceiveMessageEvents.MODIFY_GAME.register((message, overlay) -> (onEvent(message))); + ClientSendMessageEvents.CHAT.register(message -> { + if (message.startsWith("/")) { + System.out.println("Sent command: " + message); + } + }); + } + + private Text onEvent(Text message) { + Callable<Text> callable = () -> { + if (!isSpam(message.toString())) { + if (getConfig().isDetailedDevModeEnabled()) { + System.out.println("got a message: " + Text.Serializer.toJson(message)); + } + return handleInClient(message); + } + return message; // Return the original message if it is spam + }; + + FutureTask<Text> future = new FutureTask<>(callable); + Thread thread = new Thread(future); + thread.start(); + + try { + return future.get(); // Retrieve the result from the background thread + } catch (InterruptedException | ExecutionException e) { + // Handle exceptions, if needed + e.printStackTrace(); + } + + return message; // Return the original message if an exception occurred + } + + //Handle in client + public Text handleInClient(Text messageOriginal) { + String message = extractPlainText(messageOriginal.toString()).trim(); + if (getConfig().messageFromAlreadyReported(message) && getPlayerNameFromMessage(message) != " " && getPlayerNameFromMessage(message) != "") { + System.out.println("Message: " + message); + sendPrivateMessageToSelf(Formatting.RED + "B: " + message); + return null; + } + if (getConfig().isDevModeEnabled()) { + System.out.println("Got message to analyse internally: " + message); + } + //party accepter + if (message != null) { + if (message.contains("party")) { + if (message.contains("disbanded the party")) { + lastPartyDisbandedMessage = message; + partyDisbandedMap.put(getPlayerNameFromMessage(message), Instant.now()); + if (getConfig().isDevModeEnabled()) { + sendPrivateMessageToSelf("Watching next 20 Sec for invite: " + getPlayerNameFromMessage(message)); + } + } + else if (message.contains("invited you to join their party")) { + if (lastPartyDisbandedMessage != null && partyDisbandedMap != null) { + Instant lastDisbandedInstant = partyDisbandedMap.get(getPlayerNameFromMessage(lastPartyDisbandedMessage)); + if (lastDisbandedInstant != null && lastDisbandedInstant.isAfter(Instant.now().minusSeconds(20)) && (getPlayerNameFromMessage(message).equals(getPlayerNameFromMessage(lastPartyDisbandedMessage)))) { + sendCommand("/p accept " + getPlayerNameFromMessage(lastPartyDisbandedMessage)); + } + } + if (!MinecraftClient.getInstance().isWindowFocused()) { + sendNotification("BBsentials Party Notifier", "You got invited too a party by: " + getPlayerNameFromMessage(message)); + } + + } + else if (message.equals("Party > " + BBsentials.getConfig().getUsername() + ": rp")) { + sendCommand("/pl"); + repartyActive = true; + } + else if (message.startsWith("Party Members (")) { + Config.partyMembers = new ArrayList<String>(); + } + else if (message.startsWith("Party Moderators:") && repartyActive) { + message = message.replace("Party Moderators:", "").replace(" ●", "").replaceAll("\\s*\\[[^\\]]+\\]", "").trim(); + if (message.contains(",")) { + for (int i = 0; i < message.split(",").length; i++) { + Config.partyMembers.add(message.split(",")[i - 1]); + } + } + else { + Config.partyMembers.add(message); + } + } + else if (message.startsWith("Party Members:")) { + message = message.replace("Party Members:", "").replace(" ●", "").replaceAll("\\s*\\[[^\\]]+\\]", "").trim(); + if (message.contains(",")) { + for (int i = 0; i < message.split(",").length; i++) { + System.out.println("Added to plist: " + (message.split(",")[i - 1])); + Config.partyMembers.add(message.split(",")[i - 1]); + } + } + else { + Config.partyMembers.add(message); + } + if (repartyActive) { + repartyActive = false; + sendCommand("/p disband"); + for (int i = 0; i < Integer.max(4, getConfig().getPlayersInParty().length); i++) { + if (i < getConfig().getPlayersInParty().length) { + sendCommand("/p invite " + getConfig().getPlayersInParty()[i]); + } + } + } + } + else if (message.endsWith("bb:party me") && message.startsWith("From ")) { + if (BBsentials.getConfig().allowBBinviteMe()) { + sendCommand("/p " + getPlayerNameFromMessage(message.replace("From ", ""))); + } + } + + } //Everything containing party is processed differently (split so fewer checks) + else if (message.contains("unlocked Wither Essence") && message.startsWith(getConfig().getUsername())) { + if (BBsentials.getConfig().isLeader()) { + repartyActive = true; + sendCommand("/pl"); + } + else { + System.out.println("No Reparty because not leader"); + } + if (getConfig().isLeaveDungeonAutomatically()) { + config.sender.addSendTask("/warp dhub", 3); + } + } + else if (message.contains("unlocked Crimson Essence") && message.startsWith(getConfig().getUsername())) { + if (BBsentials.getConfig().isLeader()) { + repartyActive = true; + sendCommand("/pl"); + } + else { + System.out.println("No Reparty because not leader"); + } + try { + Thread.sleep(3000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + if (getConfig().isLeaveKuudraAutomatically()) { + sendCommand("/warp kuudra"); + } + } + else if (message.contains("bb:test")) { + sendPrivateMessageToSelf(test()); + } + else if ((message.endsWith("is visiting Your Garden !") || message.endsWith("is visiting Your Island !")) && !MinecraftClient.getInstance().isWindowFocused()) { + sendNotification("BBsentials Visit-Watcher", message); + } + else if (message.equals("Please type /report confirm to log your report for staff review.")) { + sendCommand("/report confirm"); + } + else if (message.contains(":") && !MinecraftClient.getInstance().isWindowFocused()) { + if (message.startsWith("Party >")) { + String partyMessage = message.replaceFirst("Party >", "").trim(); + messageOriginal = replaceAllForText(messageOriginal, "\"action\":\"run_command\",\"value\":\"/viewprofile", "\"action\":\"run_command\",\"value\":\"/hci menu pcm " + partyMessage); + if (partyMessage.split(":", 2)[1].toLowerCase().contains(getConfig().getUsername().toLowerCase()) || (partyMessage.toLowerCase().contains(getConfig().getNickname().toLowerCase() + " ") && getConfig().getNotifForParty().toLowerCase().equals("nick")) || getConfig().getNotifForParty().toLowerCase().equals("all")) { + sendNotification("BBsentials Party Chat Notification", partyMessage); + } + } + else if (message.startsWith("From ")) { + String sender = getPlayerNameFromMessage(message.replaceFirst("From", "").trim()); + String content = message.split(":", 2)[1]; + sendNotification("BBsentials Message Notifier", sender + " sent you the following message: " + content); + } + else if (message.toLowerCase().contains("party")) { + if ((message.contains("Party Leader:") && !message.contains(getConfig().getUsername())) || message.equals("You are not currently in a party.") || (message.contains("warped the party into a Skyblock Dungeon") && !message.startsWith(getConfig().getUsername()) || (!message.startsWith("The party was transferred to " + getConfig().getUsername()) && message.startsWith("The party was transferred to"))) || message.equals(getConfig().getUsername() + " is now a Party Moderator") || (message.equals("The party was disbanded because all invites expired and the party was empty.")) || (message.contains("You have joined ") && message.contains("'s party!")) || (message.contains("Party Leader, ") && message.contains(" , summoned you to their server.")) || (message.contains("warped to your dungeon"))) { + BBsentials.getConfig().setIsLeader(false); + if (getConfig().isDetailedDevModeEnabled()) { + sendPrivateMessageToSelf("Leader: " + getConfig().isLeader()); + } + } + if ((message.equals("Party Leader: " + getConfig().getUsername() + " ●")) || (message.contains(getConfig().getUsername() + " warped the party to a SkyBlock dungeon!")) || message.startsWith("The party was transferred to " + getConfig().getUsername()) || message.equals("Raul_J has promoted " + getConfig().getUsername() + " to Party Leader") || (message.contains("warped to your dungeon"))) { + BBsentials.getConfig().setIsLeader(true); + if (getConfig().isDetailedDevModeEnabled()) { + sendPrivateMessageToSelf("Leader: " + getConfig().isLeader()); + } + } + else if (repartyActive && !BBsentials.getConfig().isLeader()) { + repartyActive = false; + sendPrivateMessageToSelf("Resetted Reparty is Active since you are not leader "); + } + } + else { + String[] temp = message.split(":", 2); + String content = temp[temp.length - 1]; + if (temp.length == 2 && (content.toLowerCase().contains(getConfig().getUsername().toLowerCase()) || content.toLowerCase().contains(config.getNickname().toLowerCase() + " "))) { + sendNotification("BBsentials Notifier", "You got mentioned in chat! " + content); + } + } + } + else if (message.equals("You laid an Egg!")) { + Thread eggThread = new Thread(() -> { + try { + Thread.sleep(21000); // Wait for 21 seconds (in milliseconds) + sendPrivateMessageToSelf("Chicken Head is ready"); + } catch (InterruptedException e) { + e.printStackTrace(); + } + }); + + eggThread.start(); + } + else if (message.contains("[OPEN MENU]") || message.contains("YES")) { + setChatPromtId(messageOriginal.toString()); + } + + } + return messageOriginal; + } + + //{"strikethrough":false,"extra":[{"strikethrough":false,"clickEvent":{"action":"run_command","value":"/viewprofile 4fa1228c-8dd6-47c4-8fe3-b04b580311b8"},"hoverEvent":{"action":"show_text","contents":{"strikethrough":false,"text":"§eClick here to view §bHype_the_Time§e's profile"}},"text":"§9Party §8> §b[MVP§2+§b] Hype_the_Time§f: "},{"bold":false,"italic":false,"underlined":false,"strikethrough":false,"obfuscated":false,"text":"h:test"}],"text":""}// {"strikethrough":false,"extra":[{"strikethrough":false,"clickEvent":{"action":"run_command","value":"/viewprofile f772b2c7-bd2a-46e1-b1a2-41fa561157d6"},"hoverEvent":{"action":"show_text","contents":{"strikethrough":false,"text":"§eClick here to view §bShourtu§e's profile"}},"text":"§9Party §8> §b[MVP§c+§b] Shourtu§f: "},{"bold":false,"italic":false,"underlined":false,"strikethrough":false,"obfuscated":false,"text":"Hype_the_Time TEST"}],"text":""} + //{"strikethrough":false,"extra":[{"strikethrough":false,"clickEvent":{"action":"run_command","value":"/viewprofile 4fa1228c-8dd6-47c4-8fe3-b04b580311b8"},"hoverEvent":{"action":"show_text","contents":{"strikethrough":false,"text":"§eClick here to view §bHype_the_Time§e's profile"}},"text":"§9Party §8> §b[MVP§2+§b] Hype_the_Time§f: "},{"bold":false,"italic":false,"underlined":false,"strikethrough":false,"obfuscated":false,"text":"h:test"}],"text":""} + private final Map<String, Instant> partyDisbandedMap = new HashMap<>(); + private String lastPartyDisbandedMessage = null; + + public static String getPlayerNameFromMessage(String message) { + message = message.replaceAll("\\[.*?\\]", "").trim(); + message = message.replaceAll("-----------------------------------------------------", "").replaceAll(":", ""); + String[] temp = message.split(" "); + String playerName = ""; + + for (int i = 0; i < temp.length; i++) { + if (!temp[i].equals(" ") && !temp[i].equals("")) { + playerName = temp[i]; + break; // Stop looping after finding the first non-empty value + } + } + + // Remove the rank from the player name, if it exists + Pattern rankPattern = Pattern.compile("\\s*\\[[^\\]]+\\]"); + playerName = rankPattern.matcher(playerName).replaceAll(" "); + + return playerName; + } + + public String extractPlainText(String input) { + String returns = ""; + String[] literals = input.split("literal\\{"); + if (!input.startsWith("literal")) { + literals[0] = ""; + } + for (int i = 0; i < literals.length; i++) { + if (dontExclude(literals, i) && !literals[i].equals("")) { + String literal = literals[i].split("}")[0]; + + if (!literal.isEmpty()) { + returns = returns + literal; + } + } + } + // Remove § formatting + returns = returns.replaceAll("§.", ""); + // Remove brackets that contain only uppercase letters or pluses + returns = returns.replaceAll("\\[[A-Z+]+\\]", ""); + returns = returns.replaceAll("\\[[0-9]+\\]", ""); + returns = returns.trim(); + returns = returns.replaceAll("\\s+", " "); + + return returns; + } + + private boolean dontExclude(String[] s, int i) { + if ((i - 1) < 0) { + return true; + } + else { + if (s[i - 1].endsWith("value='")) { + return false; + } + else { + return true; + } + } + } + + public boolean isSpam(String message) { + if (message.contains("Mana")) return true; + if (message.contains("Achievement Points")) return true; + return false; + } + + private boolean repartyActive = false; + + public String test() { + //put test code here + sendNotification("test", "This is an example which was run of the h:test test"); + return new String(); + } + + private static String removeMultipleSpaces(String input) { + return input.replaceAll("\\s+", " "); + } + + public static void sendPrivateMessageToSelf(String message) { + MinecraftClient client = MinecraftClient.getInstance(); + if (client.player != null) { + client.player.sendMessage(Text.of((Formatting.RED + message.formatted(Formatting.RED)))); + } + } + + public static void sendPrivateMessageToSelfText(Text message) { + MinecraftClient client = MinecraftClient.getInstance(); + if (client.player != null) { + client.player.sendMessage(message); + } + } + + public static void sendCommand(String s) { + getConfig().sender.addSendTask(s); + } + + public void sendNotification(String title, String text) { + Thread soundThread = new Thread(() -> { + try { + InputStream inputStream = getClass().getResourceAsStream("/sounds/mixkit-sci-fi-confirmation-914.wav"); + AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(inputStream); + Clip clip = AudioSystem.getClip(); + clip.open(audioInputStream); + clip.start(); + Thread.sleep(clip.getMicrosecondLength() / 1000); + clip.close(); + audioInputStream.close(); + } catch (Exception e) { + e.printStackTrace(); + } + }); + + soundThread.start(); + + List<String> argsList = new ArrayList<>(); + argsList.add("--title"); + argsList.add(title); + argsList.add("--passivepopup"); + argsList.add(text); + argsList.add("5"); + + try { + ProcessBuilder processBuilder = new ProcessBuilder(); + processBuilder.command("kdialog"); + processBuilder.command().addAll(argsList); + + Process process = processBuilder.start(); + process.waitFor(); + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + } + } + + public static Text createClientSideTellraw(String tellrawInput) { + Text formattedMessage = null; + try { + formattedMessage = Text.Serializer.fromJson(tellrawInput); + } catch (Exception e) { + e.printStackTrace(); + System.out.println("Invalid Json: \n" + tellrawInput); + } + return formattedMessage; + } + + public static void followMenu(String menu, String message) { + // Check the "menu" argument and execute the appropriate logic + String command; + String username = getPlayerNameFromMessage(message); + if (message.contains(":")) { + message = message.split(":", 2)[1].trim(); + if (menu.equalsIgnoreCase("pcm")) { + command = "[\"\",{\"text\":\"@@username\",\"color\":\"gray\",\"clickEvent\":{\"action\":\"suggest_command\",\"value\":\"/pc @username\"}},{\"text\":\" [Copy_Message]\",\"color\":\"blue\",\"clickEvent\":{\"action\":\"copy_to_clipboard\",\"value\":\"Copy-Text-Message\"},\"hoverEvent\":{\"action\":\"show_text\",\"contents\":[\"Copy the message the Player send without their name into the clipboard.\"]}},{\"text\":\" [Kick_Player]\",\"color\":\"dark_red\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/p kick @username\"},\"hoverEvent\":{\"action\":\"show_text\",\"contents\":[\"Kick the player from the party\"]}},{\"text\":\" [Promote_Player]\",\"color\":\"dark_green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/p promote @username\"},\"hoverEvent\":{\"action\":\"show_text\",\"contents\":[\"Promote the player\"]}},{\"text\":\" [Demote_Player]\",\"color\":\"red\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/p demote @username\"},\"hoverEvent\":{\"action\":\"show_text\",\"contents\":[\"Demote the player\"]}},{\"text\":\" [Transfer_to_Player]\",\"color\":\"gold\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/p transfer @username\"},\"hoverEvent\":{\"action\":\"show_text\",\"contents\":[\"Transfer the Party to the player\"]}},{\"text\":\" [Mute/Unmute_Party]\",\"color\":\"dark_aqua\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/p mute @username\"},\"hoverEvent\":{\"action\":\"show_text\",\"contents\":[\"Mutes the ENTIRE party but party moderators or Hypixel Staff can still type.\"]}}]"; + //{"jformat":8,"jobject":[{"bold":false,"italic":false,"underlined":false,"strikethrough":false,"obfuscated":false,"font":null,"color":"gray","insertion":"","click_event_type":"suggest_command","click_event_value":"/pc @username","hover_event_type":"none","hover_event_value":"","hover_event_object":{},"hover_event_children":[],"text":"@username"},{"bold":false,"italic":false,"underlined":false,"strikethrough":false,"obfuscated":false,"font":null,"color":"blue","insertion":"","click_event_type":"copy_to_clipboard","click_event_value":"Copy-Text-Message","hover_event_type":"show_text","hover_event_value":"","hover_event_object":{},"hover_event_children":[{"bold":false,"italic":false,"underlined":false,"strikethrough":false,"obfuscated":false,"font":null,"color":"none","insertion":"","click_event_type":"none","click_event_value":"","hover_event_type":"none","hover_event_value":"","hover_event_object":{},"hover_event_children":[],"text":"Copy the message the Player send without their name into the clipboard."}],"text":" [Copy_Message]"},{"bold":false,"italic":false,"underlined":false,"strikethrough":false,"obfuscated":false,"font":null,"color":"dark_red","insertion":"","click_event_type":"run_command","click_event_value":"/p kick @username","hover_event_type":"show_text","hover_event_value":"","hover_event_object":{},"hover_event_children":[{"bold":false,"italic":false,"underlined":false,"strikethrough":false,"obfuscated":false,"font":null,"color":"none","insertion":"","click_event_type":"none","click_event_value":"","hover_event_type":"none","hover_event_value":"","hover_event_object":{},"hover_event_children":[],"text":"Kick the player from the party"}],"text":" [Kick_Player]"},{"bold":false,"italic":false,"underlined":false,"strikethrough":false,"obfuscated":false,"font":null,"color":"dark_green","insertion":"","click_event_type":"run_command","click_event_value":"/p promote @username","hover_event_type":"show_text","hover_event_value":"","hover_event_object":{},"hover_event_children":[{"bold":false,"italic":false,"underlined":false,"strikethrough":false,"obfuscated":false,"font":null,"color":"none","insertion":"","click_event_type":"none","click_event_value":"","hover_event_type":"none","hover_event_value":"","hover_event_object":{},"hover_event_children":[],"text":"Promote the player"}],"text":" [Promote_Player]"},{"bold":false,"italic":false,"underlined":false,"strikethrough":false,"obfuscated":false,"font":null,"color":"red","insertion":"","click_event_type":"run_command","click_event_value":"/p demote @username","hover_event_type":"show_text","hover_event_value":"","hover_event_object":{},"hover_event_children":[{"bold":false,"italic":false,"underlined":false,"strikethrough":false,"obfuscated":false,"font":null,"color":"none","insertion":"","click_event_type":"none","click_event_value":"","hover_event_type":"none","hover_event_value":"","hover_event_object":{},"hover_event_children":[],"text":"Demote the player"}],"text":" [Demote_Player]"},{"bold":false,"italic":false,"underlined":false,"strikethrough":false,"obfuscated":false,"font":null,"color":"gold","insertion":"","click_event_type":"run_command","click_event_value":"/p transfer @username","hover_event_type":"show_text","hover_event_value":"","hover_event_object":{},"hover_event_children":[{"bold":false,"italic":false,"underlined":false,"strikethrough":false,"obfuscated":false,"font":null,"color":"none","insertion":"","click_event_type":"none","click_event_value":"","hover_event_type":"none","hover_event_value":"","hover_event_object":{},"hover_event_children":[],"text":"Transfer the Party to the player"}],"text":" [Transfer_to_Player]"},{"bold":false,"italic":false,"underlined":false,"strikethrough":false,"obfuscated":false,"font":null,"color":"dark_aqua","insertion":"","click_event_type":"run_command","click_event_value":"/p mute @username","hover_event_type":"show_text","hover_event_value":"","hover_event_object":{},"hover_event_children":[{"bold":false,"italic":false,"underlined":false,"strikethrough":false,"obfuscated":false,"font":null,"color":"none","insertion":"","click_event_type":"none","click_event_value":"","hover_event_type":"none","hover_event_value":"","hover_event_object":{},"hover_event_children":[],"text":"Mutes the ENTIRE party but party moderators or Hypixel Staff can still type."}],"text":" [Mute/Unmute_Party]"}],"command":"%s","jtemplate":"tellraw"} + } + else if (menu.equalsIgnoreCase("sbacm")) { + command = "[\"\",\"\\n\",{\"text\":\"@@username\",\"color\":\"gray\",\"clickEvent\":{\"action\":\"suggest_command\",\"value\":\"@username\"}},{\"text\":\" [Party_Player]\",\"color\":\"gold\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/p invite @username\"},\"hoverEvent\":{\"action\":\"show_text\",\"contents\":[\"Invite the player to the party\"]}},{\"text\":\" [Ignore_Player]\",\"color\":\"yellow\",\"clickEvent\":{\"action\":\"suggest_command\",\"value\":\"/ignore add @username\"},\"hoverEvent\":{\"action\":\"show_text\",\"contents\":[\"Add the player to your ignore list.\"]}},{\"text\":\" [Chat_Report_Player]\",\"color\":\"dark_red\",\"clickEvent\":{\"action\":\"suggest_command\",\"value\":\"/creport @username\"},\"hoverEvent\":{\"action\":\"show_text\",\"contents\":[\"Chat report the user with /creport.\"]}},{\"text\":\" [Visit_Player]\",\"color\":\"dark_green\",\"insertion\":\" \",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/visit @username\"},\"hoverEvent\":{\"action\":\"show_text\",\"contents\":[]}},{\"text\":\" [/Invite_Player]\",\"color\":\"green\",\"insertion\":\" \",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/invite @username\"},\"hoverEvent\":{\"action\":\"show_text\",\"contents\":[\"/invite the player to visit your Island / Garden.\"]}},{\"text\":\" [Copy_Message] \",\"color\":\"blue\",\"clickEvent\":{\"action\":\"copy_to_clipboard\",\"value\":\"Copy-Text-Message\"},\"hoverEvent\":{\"action\":\"show_text\",\"contents\":[\"Copy the message the user send without their prefixes nor their username\"]}},{\"text\":\" [Copy_Username]\",\"color\":\"dark_aqua\",\"insertion\":\" \",\"clickEvent\":{\"action\":\"copy_to_clipboard\",\"value\":\"@username\"},\"hoverEvent\":{\"action\":\"show_text\",\"contents\":[\"Copy the players username into the clipboard.\"]}},{\"text\":\" [/msg_Chat_Player]\",\"color\":\"light_purple\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/msg @username\"},\"hoverEvent\":{\"action\":\"show_text\",\"contents\":[\"/msg the Player. the chat will be set to message them. This means you do not need to type /msg upfront. To return to normal do /chat a\"]}},{\"text\":\" [Sky_shiiyu_player]\",\"color\":\"aqua\",\"clickEvent\":{\"action\":\"open_url\",\"value\":\"https://sky.shiiyu.moe/stats/@username\"},\"hoverEvent\":{\"action\":\"show_text\",\"contents\":[\"Opens the players Skyblock Profile in Sky Crypt (sky.shiiyu.moe)\"]}},\"\\n\"]"; + } + else if (menu.equalsIgnoreCase("acm")) { + command = "[\"\",\"\\n\",{\"text\":\"@@username\",\"color\":\"gray\",\"clickEvent\":{\"action\":\"suggest_command\",\"value\":\"@username\"}},{\"text\":\" [Party_Player]\",\"color\":\"gold\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/p invite @username\"},\"hoverEvent\":{\"action\":\"show_text\",\"contents\":[\"Invite the player to the party\"]}},{\"text\":\" [Ignore_Player]\",\"color\":\"yellow\",\"clickEvent\":{\"action\":\"suggest_command\",\"value\":\"/ignore add @username\"},\"hoverEvent\":{\"action\":\"show_text\",\"contents\":[\"Add the player to your ignore list.\"]}},{\"text\":\" [Chat_Report_Player]\",\"color\":\"dark_red\",\"clickEvent\":{\"action\":\"suggest_command\",\"value\":\"/creport @username\"},\"hoverEvent\":{\"action\":\"show_text\",\"contents\":[\"Chat report the user with /creport.\"]}},{\"text\":\" [Copy_Message] \",\"color\":\"blue\",\"clickEvent\":{\"action\":\"copy_to_clipboard\",\"value\":\"Copy-Text-Message\"},\"hoverEvent\":{\"action\":\"show_text\",\"contents\":[\"Copy the message the user send without their prefixes nor their username\"]}},{\"text\":\" [Copy_Username]\",\"color\":\"dark_aqua\",\"insertion\":\" \",\"clickEvent\":{\"action\":\"copy_to_clipboard\",\"value\":\"@username\"},\"hoverEvent\":{\"action\":\"show_text\",\"contents\":[\"Copy the players username into the clipboard.\"]}},{\"text\":\" [/msg_Chat_Player]\",\"color\":\"light_purple\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"/msg @username\"},\"hoverEvent\":{\"action\":\"show_text\",\"contents\":[\"/msg the Player. the chat will be set to message them. This means you do not need to type /msg upfront. To return to normal do /chat a\"]}}"; + } + else { + // Handle unrecognized menu argument + sendPrivateMessageToSelf(Formatting.RED + "Unrecognized menu argument! Do not use this command unless you know exactly what you are doing aka only use it as a developer!"); + return; + } + command = command.replaceAll("@username", username); + command = command.replaceAll("Copy-Text-Message", message); + sendPrivateMessageToSelfText(createClientSideTellraw(command)); + } + else { + sendPrivateMessageToSelf("Invalid message!: " + message); + } + } + + public static void setChatPromtId(String logMessage) { + String cbUUIDPattern = "/cb ([a-fA-F0-9-]+)"; + Pattern cbPattern = Pattern.compile(cbUUIDPattern); + Matcher cbMatcher = cbPattern.matcher(logMessage); + + String yesClickAction = "/chatprompt ([a-fA-F0-9-]+) YES"; + Pattern yesPattern = Pattern.compile(yesClickAction); + Matcher yesMatcher = yesPattern.matcher(logMessage); + String lastPrompt = null; + if (cbMatcher.find()) { + lastPrompt = cbMatcher.group(1); + String finalLastPrompt1 = lastPrompt; + new Thread(new Runnable() { + @Override + public void run() { + BBsentials.getConfig().setLastChatPromptAnswer("/cb " + finalLastPrompt1); + try { + Thread.sleep(10 * 1000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + BBsentials.getConfig().setLastChatPromptAnswer(null); + return; + } + }).start(); + } + if (yesMatcher.find()) { + lastPrompt = yesMatcher.group(1); + String finalLastPrompt = lastPrompt; + new Thread(new Runnable() { + @Override + public void run() { + BBsentials.getConfig().setLastChatPromptAnswer("/chatprompt " + finalLastPrompt + " YES"); + try { + Thread.sleep(10 * 1000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + BBsentials.getConfig().setLastChatPromptAnswer(null); + return; + } + }).start(); + + } + } + + public static Text replaceAllForText(Text input, String replace, String replaceWith) { + String text = Text.Serializer.toJson(input); + if (text.contains(replace)) { + text = text.replaceAll("\\w{8}-\\w{4}-\\w{4}-\\w{4}-\\w{12}", ""); + } + text = text.replace(replace, replaceWith); + Text output = Text.Serializer.fromJson(text); + return output; + } +}
\ No newline at end of file diff --git a/src/main/java/de/hype/bbsentials/chat/Sender.java b/src/main/java/de/hype/bbsentials/chat/Sender.java new file mode 100644 index 0000000..4590b5f --- /dev/null +++ b/src/main/java/de/hype/bbsentials/chat/Sender.java @@ -0,0 +1,106 @@ +package de.hype.bbsentials.chat; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +import java.util.ArrayList; +import java.util.List; + +import static de.hype.bbsentials.chat.Chat.sendPrivateMessageToSelf; +import static de.hype.bbsentials.chat.Chat.sendPrivateMessageToSelfText; + +public class Sender { + private final List<String> sendQueue; + private final List<Double> sendQueueTiming; + private final List<Boolean> hidden; + + + public Sender() { + this.sendQueue = new ArrayList<>(); + this.sendQueueTiming = new ArrayList<>(); + this.hidden = new ArrayList<>(); + startSendingThread(); + } + + public void addSendTask(String task, double timing) { + synchronized (sendQueue) { + sendPrivateMessageToSelfText(Text.literal(Formatting.GREEN + "Scheduled send-task (as " + sendQueueTiming.size() + " in line): " + task + " | Delay: " + timing)); + sendQueueTiming.add(timing); + sendQueue.add(task); + hidden.add(false); + sendQueue.notify(); // Notify the waiting thread that a new String has been added + } + } + + public void addHiddenSendTask(String task, double timing) { + synchronized (sendQueue) { + sendQueueTiming.add(timing); + sendQueue.add(task); + hidden.add(true); + + sendQueue.notify(); // Notify the waiting thread that a new String has been added + } + } + + public void addImmediateSendTask(String task) { + synchronized (sendQueue) { + sendQueueTiming.add(0, 0.0); + sendQueue.add(0, task); + hidden.add(false); + + sendQueue.notify(); // Notify the waiting thread that a new String has been added + } + } + + public void addSendTask(String task) { + addSendTask(task, 1); + } + + public void startSendingThread() { + Thread sendingThread = new Thread(new SendingRunnable()); + sendingThread.start(); + } + + private class SendingRunnable implements Runnable { + @Override + public void run() { + while (true) { + String task = getNextTask(); + if (task != null) { + send(task, sendQueueTiming.remove(0), hidden.remove(0)); + } + else { + synchronized (sendQueue) { + try { + sendQueue.wait(); // Wait for new Send + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + } + } + + private String getNextTask() { + synchronized (sendQueue) { + if (!sendQueue.isEmpty()) { + return sendQueue.remove(0); + } + return null; + } + } + + private void send(String toSend, double timing, boolean hidden) { + try { + Thread.sleep((long) (timing * 1000)); // Simulate the send operation + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + MinecraftClient.getInstance().getNetworkHandler().sendChatMessage(toSend); + if (!hidden) { + sendPrivateMessageToSelf("Sent Command to Server: " + toSend); + } + } + } +} diff --git a/src/main/java/de/hype/bbsentials/client/BBsentials.java b/src/main/java/de/hype/bbsentials/client/BBsentials.java new file mode 100644 index 0000000..596d194 --- /dev/null +++ b/src/main/java/de/hype/bbsentials/client/BBsentials.java @@ -0,0 +1,174 @@ +package de.hype.bbsentials.client; + +import com.mojang.brigadier.arguments.StringArgumentType; +import de.hype.bbsentials.api.Options; +import de.hype.bbsentials.chat.Chat; +import de.hype.bbsentials.client.Commands.CommandsOLD; +import de.hype.bbsentials.communication.BBsentialConnection; +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; +import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.option.KeyBinding; +import net.minecraft.client.util.InputUtil; +import net.minecraft.command.CommandSource; +import net.minecraft.util.Formatting; +import org.lwjgl.glfw.GLFW; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; + +import static de.hype.bbsentials.chat.Chat.*; + +public class BBsentials implements ClientModInitializer { + private boolean initialised = false; + public static Config config; + public static BBsentialConnection bbserver; + public static CommandsOLD coms; + + /** + * Runs the mod initializer on the client environment. + */ + @Override + public void onInitializeClient() { + ClientPlayConnectionEvents.JOIN.register((a, b, c) -> { + if (!initialised) { + config = Config.load(); + Options.setGamma(10); + Chat chat = new Chat(); + if (Config.isBingoTime() || config.overrideBingoTime()) { + connectToBBserver(); + } + initialised = true; + } + } + ); + KeyBinding promptKeyBind = new KeyBinding("Chat Prompt Yes / Open Menu", InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_R, "BBsentials"); + KeyBindingHelper.registerKeyBinding(promptKeyBind); + ClientTickEvents.END_CLIENT_TICK.register(client -> { + if (promptKeyBind.wasPressed()) { + if (config.getLastChatPromptAnswer() != null) { + if (config.isDetailedDevModeEnabled()){ + Chat.sendPrivateMessageToSelf(config.getLastChatPromptAnswer());} + MinecraftClient.getInstance().getNetworkHandler().sendChatMessage(config.getLastChatPromptAnswer()); + } + config.setLastChatPromptAnswer(null); + } + }); + } + + public static Config getConfig() { + return config; + } + + public static void connectToBBserver() { + if (bbserver != null) { + bbserver.sendHiddenMessage("exit"); + } + bbserver = new BBsentialConnection(); + bbserver.setMessageReceivedCallback(message -> bbserver.onMessageReceived(message)); + bbserver.connect(config.getBBServerURL(), 5000); + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> { + dispatcher.register(ClientCommandManager.literal("bbi") + .then(ClientCommandManager.literal("reconnect") + .executes((context) -> { + connectToBBserver(); + return 1; + })) + .then(ClientCommandManager.literal("config") + .then(ClientCommandManager.argument("category", StringArgumentType.string()) + .suggests((context, builder) -> { + // Provide tab-completion options for config subfolder + return CommandSource.suggestMatching(new String[]{"save", "reset", "load"}, builder); + }).executes((context) -> { + String category = StringArgumentType.getString(context, "category"); + switch (category) { + case "save": + getConfig().save(); + sendPrivateMessageToSelf(Formatting.GREEN + "Saved config successfully"); + break; + case "load": + BBsentials.config = Config.load(); + break; + case "reset": + // Reset logic here + break; + } + return 1; + })) + .then(ClientCommandManager.literal("set-value") + .then(ClientCommandManager.argument("className", StringArgumentType.string()) + .suggests((context, builder) -> { + // Provide tab-completion options for classes + ArrayList<String> classNames = new ArrayList<>(); + classNames.add("Config"); + // Replace with your own logic to retrieve class names + return CommandSource.suggestMatching(classNames, builder); + }) + .then(ClientCommandManager.argument("variableName", StringArgumentType.string()) + .suggests((context, builder) -> { + // Provide tab-completion options for variable names + List<String> variableNames; + variableNames = List.of(getVariableInfo("de.hype.bbsentials.client", "Config")); + return CommandSource.suggestMatching(variableNames, builder); + }) + .then(ClientCommandManager.argument("variableValue", StringArgumentType.string()) + .executes((context) -> { + // Handle "variableName" and "variableValue" logic here + String variableName = StringArgumentType.getString(context, "variableName"); + String variableValue = StringArgumentType.getString(context, "variableValue"); + try { + if (!variableName.contains("dev")||config.bbsentialsRoles.contains("dev")){ + setVariableValue(getConfig(), variableName, variableValue);} + getConfig().save(); + } catch (ClassNotFoundException | NoSuchFieldException | + IllegalAccessException | + InstantiationException | + InvocationTargetException | + NoSuchMethodException e) { + Chat.sendPrivateMessageToSelf("§cInvalid variable or value"); + } + return 1; + }))))) + .then(ClientCommandManager.literal("get-value") + .then(ClientCommandManager.argument("className", StringArgumentType.string()) + .suggests((context, builder) -> { + // Provide tab-completion options for classes + ArrayList<String> classNames = new ArrayList<>(); + classNames.add("Config"); + // Replace with your own logic to retrieve class names + return CommandSource.suggestMatching(classNames, builder); + }) + .then(ClientCommandManager.argument("variableName", StringArgumentType.string()) + .suggests((context, builder) -> { + // Provide tab-completion options for variable names + List<String> variableNames; + variableNames = List.of(getVariableInfo("de.hype.bbsentials.client", "Config")); + return CommandSource.suggestMatching(variableNames, builder); + }).executes((context) -> { + // Handle "variableName" and "variableValue" logic here + String variableName = StringArgumentType.getString(context, "variableName"); + try { + Chat.getVariableValue(getConfig(), variableName); + } catch (Exception e) { + e.printStackTrace(); + } + return 1; + }))).executes((context) -> { + // Handle the case when "config" argument is not provided + // ... + return 1; + }))) + ); + }); //bbi} + } + + public static void refreshCommands() { + coms = new CommandsOLD(); + } + +}
\ No newline at end of file diff --git a/src/main/java/de/hype/bbsentials/client/Commands/CommandsOLD.java b/src/main/java/de/hype/bbsentials/client/Commands/CommandsOLD.java new file mode 100644 index 0000000..bdb7478 --- /dev/null +++ b/src/main/java/de/hype/bbsentials/client/Commands/CommandsOLD.java @@ -0,0 +1,308 @@ +package de.hype.bbsentials.client.Commands; + +import com.mojang.brigadier.arguments.IntegerArgumentType; +import com.mojang.brigadier.arguments.StringArgumentType; +import de.hype.bbsentials.client.BBsentials; +import de.hype.bbsentials.communication.BBsentialConnection; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; +import net.minecraft.command.CommandSource; + +import static de.hype.bbsentials.client.BBsentials.*; + +public class CommandsOLD { + public CommandsOLD() { + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> { + dispatcher.register(ClientCommandManager.literal("warp").then(ClientCommandManager.argument("destination", StringArgumentType.string()).suggests((context, builder) -> { + // Provide tab-completion options for menu subfolder + return CommandSource.suggestMatching(new String[]{"desert", "hub", "dhub", "nether", "isle", "wizard", "portal", "mines", "forge", "ch", "crystals", "nucleus", "end", "drag", "void", "castle", "howl", "park", "jungle", "nest", "arachne", "spider", "deep", "barn", "home", "kuurda", "wasteland", "dragontail", "scarleton", "smold", "garden", "da", "crypt", "museum", "trapper", "dungeon_hub"}, builder); + }).executes((context) -> { + // Handle "variableName" and "variableValue" logic here + String destination = StringArgumentType.getString(context, "destination"); + getConfig().sender.addSendTask("/warp " + destination, 0); + return 1; + }))); + }); //warp test + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> { + dispatcher.register(ClientCommandManager.literal("creport") + .then(ClientCommandManager.argument("Player_Name", StringArgumentType.string()) + .executes((context) -> { + String playerName = StringArgumentType.getString(context, "Player_Name"); + getConfig().sender.addSendTask("/creport " + playerName, 0); + getConfig().addReported(playerName); + return 1; + }))); + });//creport helper → no double report during same launch + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> { + dispatcher.register(ClientCommandManager.literal("hp").then(ClientCommandManager.literal("accept").then(ClientCommandManager.argument("player", StringArgumentType.string()).executes((context) -> { + String player = StringArgumentType.getString(context, "player"); + getConfig().sender.addImmediateSendTask("/party accept " + player); + return 1; + }))).then(ClientCommandManager.literal("chat").executes((context) -> { + getConfig().sender.addImmediateSendTask("/party chat"); + return 1; + })).then(ClientCommandManager.literal("demote").then(ClientCommandManager.argument("player", StringArgumentType.string()).suggests((context, builder) -> { + // Provide tab-completion options for menu subfolder + return CommandSource.suggestMatching(getConfig().getPlayersInParty(), builder); + }).executes((context) -> { + String player = StringArgumentType.getString(context, "player"); + getConfig().sender.addImmediateSendTask("/party demote " + player); + return 1; + }))).then(ClientCommandManager.literal("disband").executes((context) -> { + getConfig().sender.addImmediateSendTask("/party disband"); + return 1; + })).then(ClientCommandManager.literal("kick").then(ClientCommandManager.argument("player", StringArgumentType.string()).suggests((context, builder) -> { + // Provide tab-completion options for menu subfolder + return CommandSource.suggestMatching(getConfig().getPlayersInParty(), builder); + }).executes((context) -> { + String player = StringArgumentType.getString(context, "player"); + getConfig().sender.addImmediateSendTask("/party kick " + player); + return 1; + }))).then(ClientCommandManager.literal("kickoffline").executes((context) -> { + getConfig().sender.addImmediateSendTask("/party kickoffline"); + return 1; + })).then(ClientCommandManager.literal("leave").executes((context) -> { + getConfig().sender.addImmediateSendTask("/party leave"); + return 1; + })).then(ClientCommandManager.literal("list").executes((context) -> { + getConfig().sender.addImmediateSendTask("/party list"); + return 1; + })).then(ClientCommandManager.literal("mute").executes((context) -> { + getConfig().sender.addImmediateSendTask("/party mute"); + return 1; + })).then(ClientCommandManager.literal("poll").then(ClientCommandManager.argument("question/answer/answer/answer", StringArgumentType.greedyString()).executes((context) -> { + String questionAndAnswers = StringArgumentType.getString(context, "question answer answer (answer)"); + getConfig().sender.addImmediateSendTask("/party poll " + questionAndAnswers); + return 1; + }))).then(ClientCommandManager.literal("private").executes((context) -> { + getConfig().sender.addImmediateSendTask("/party private"); + return 1; + })).then(ClientCommandManager.literal("promote").then(ClientCommandManager.argument("player", StringArgumentType.string()).suggests((context, builder) -> { + // Provide tab-completion options for menu subfolder + return CommandSource.suggestMatching(getConfig().getPlayersInParty(), builder); + }).executes((context) -> { + String player = StringArgumentType.getString(context, "player"); + getConfig().sender.addImmediateSendTask("/party promote " + player); + return 1; + }))).then(ClientCommandManager.literal("setting").then(ClientCommandManager.literal("allinvite")).executes((context) -> { + String setting = StringArgumentType.getString(context, "setting"); + getConfig().sender.addImmediateSendTask("/party setting " + setting); + return 1; + }) + + ).then(ClientCommandManager.literal("transfer").then(ClientCommandManager.argument("player", StringArgumentType.string()).suggests((context, builder) -> { + // Provide tab-completion options for menu subfolder + return CommandSource.suggestMatching(getConfig().getPlayersInParty(), builder); + }).executes((context) -> { + String player = StringArgumentType.getString(context, "player"); + getConfig().sender.addImmediateSendTask("/party transfer " + player); + return 1; + }))).then(ClientCommandManager.literal("warp").executes((context) -> { + getConfig().sender.addImmediateSendTask("/party warp"); + return 1; + })).executes(context -> { + getConfig().sender.addImmediateSendTask("/p"); + return 1; + })); + }); //party test + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> { + dispatcher.register( + ClientCommandManager.literal("goblinraid") + .executes((context) -> { + bbserver.sendMessage("?dwevent goblinraid"); + return 1; + }) + ); + });/*goblinraid*/ + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> { + dispatcher.register( + ClientCommandManager.literal("2xpowder") + .executes((context) -> { + bbserver.sendMessage("?dwevent 2xpowder"); + return 1; + }) + ); + });/*2xpowder*/ + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> { + dispatcher.register( + ClientCommandManager.literal("bettertogether") + .executes((context) -> { + bbserver.sendMessage("?dwevent bettertogether"); + return 1; + }) + ); + });/*b2g*/ + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> { + dispatcher.register( + ClientCommandManager.literal("raffle") + .executes((context) -> { + bbserver.sendMessage("?dwevent raffle"); + return 1; + }) + ); + });/*raffle*/ + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> { + dispatcher.register( + ClientCommandManager.literal("gonewiththewind") + .executes((context) -> { + bbserver.sendMessage("?dwevent gonewiththewind"); + return 1; + }) + ); + });/*gonewiththewind*/ + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> { + dispatcher.register(ClientCommandManager.literal("chchest") + .then(ClientCommandManager.argument("Item", StringArgumentType.string()) + .suggests((context, builder) -> { + return CommandSource.suggestMatching(new String[]{"PrehistoricEgg", "Pickonimbus2000", "ElectronTransmitter", "FTX3070", "RobotronReflector", "ControlSwitch", "SyntheticHeart", "SuperliteMotor", "BlueGoblinEgg", "YellowGoblinEgg", "FlawlessAmberGemstone", "FlawlessJadeGemstone", "FlawlessSapphireGemstone", "FlawlessRubyGemstone", "FlawlessAmethystGemstone", "JungleHeart", "FlawlessTopazGemstone", "FlawlessJasperGemstone"}, builder); + }) + .then(ClientCommandManager.argument("X", IntegerArgumentType.integer()) + .then(ClientCommandManager.argument("Y", IntegerArgumentType.integer()) + .then(ClientCommandManager.argument("Z", IntegerArgumentType.integer()) + .then(ClientCommandManager.argument("ContactWay", StringArgumentType.greedyString()) + .suggests(((context, builder) -> { + return CommandSource.suggestMatching(new String[]{"/msg " + getConfig().getUsername() + " bb:party me", "/p join " + config.getUsername()}, builder); + })) + .executes((context) -> { + String destination = StringArgumentType.getString(context, "Item"); + int x = IntegerArgumentType.getInteger(context, "X"); + int y = IntegerArgumentType.getInteger(context, "Y"); + int z = IntegerArgumentType.getInteger(context, "Z"); + String contactWay = StringArgumentType.getString(context, "ContactWay"); + + String combinedString = "?chchest " + destination + " " + x + " " + y + " " + z + " " + contactWay; + bbserver.sendMessage(combinedString); + return 1; + } + ) + ) + ) + ) + ) + ) + ); + }); /*chchest*/ + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> { + dispatcher.register( + ClientCommandManager.literal("bbserver") + .then(ClientCommandManager.argument("Message", StringArgumentType.greedyString()) + .executes((context) -> { + String message = StringArgumentType.getString(context, "Message"); + if (message.equals("bb:reconnect")) { + BBsentials.connectToBBserver(); + } else { + BBsentials.bbserver.sendMessage(message); + } + return 1; + }) + ) + ); + });/*BBserver*/ + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> { + dispatcher.register( + ClientCommandManager.literal("bc") + .then(ClientCommandManager.argument("Message to Bingo Chat", StringArgumentType.greedyString()) + .executes((context) -> { + String message = StringArgumentType.getString(context, "Message to Bingo Chat"); + sendCommand("?bingochat " + message); + return 1; + }) + ) + ); + });/*BincoChatShort*/ + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> { + dispatcher.register( + ClientCommandManager.literal("bingochat") + .then(ClientCommandManager.argument("Message to Bingo Chat", StringArgumentType.greedyString()) + .executes((context) -> { + String message = StringArgumentType.getString(context, "Message to Bingo Chat"); + sendCommand("?bingochat " + message); + return 1; + }) + ) + ); + });/*BingoChatLong*/ + if (getConfig().bbsentialsRoles != null) { + if (getConfig().bbsentialsRoles.contains("mod")) { + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> { + dispatcher.register( + ClientCommandManager.literal("bannounce") + .then(ClientCommandManager.argument("message", StringArgumentType.greedyString()) + .executes((context) -> { + String message = StringArgumentType.getString(context, "message"); + sendCommand("?announce " + message); + return 1; + }) + ) + ); + });/*bAnnounce*/ + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> { + dispatcher.register( + ClientCommandManager.literal("bmute") + .then(ClientCommandManager.argument("message", StringArgumentType.greedyString()) + .executes((context) -> { + String message = StringArgumentType.getString(context, "message"); + sendCommand("?mute " + message); + return 1; + }) + ) + ); + });/*bmute*/ + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> { + dispatcher.register( + ClientCommandManager.literal("bban") + .then(ClientCommandManager.argument("message", StringArgumentType.greedyString()) + .executes((context) -> { + String message = StringArgumentType.getString(context, "message"); + sendCommand("?bban " + message); + return 1; + }) + ) + ); + });/*bmute*/ + } + if (getConfig().bbsentialsRoles.contains("splasher")) { + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> { + dispatcher.register( + ClientCommandManager.literal("splashAnnounce") + .then(ClientCommandManager.argument("Hub", IntegerArgumentType.integer(1, 28)) + .then(ClientCommandManager.argument("location", StringArgumentType.string()) + .suggests((context, builder) -> { + return CommandSource.suggestMatching(new String[]{"kat","bea","guild-house"}, builder); + }) + .then(ClientCommandManager.argument("extramessage", StringArgumentType.greedyString()) + .executes((context) -> { + int hub_Number = IntegerArgumentType.getInteger(context, "Hub"); + String extramessage = StringArgumentType.getString(context, "extramessage"); + String location = StringArgumentType.getString(context, "location"); + sendCommand("?splash " + hub_Number + " " + location+" "+extramessage); + return 1; + }) + ) + .executes((context) -> { + int hub_Number = IntegerArgumentType.getInteger(context, "Hub"); + String message = ""; + String location = "bea"; + sendCommand("?splash " + hub_Number + " "+location+" " + message); + return 1; + }) + ) + .executes((context) -> { + int hub_Number = IntegerArgumentType.getInteger(context, "Hub"); + String message = ""; + String location = StringArgumentType.getString(context, "location"); + sendCommand("?splash " + hub_Number + " "+location+" " + message); + return 1; + }) + ) + ); + });/*SplashAnnounce*/ + } else { + } + } + } + public void sendCommand(String message){ + BBsentials.bbserver.sendCommand(message); + } +} +//TODO
\ No newline at end of file diff --git a/src/main/java/de/hype/bbsentials/client/Config.java b/src/main/java/de/hype/bbsentials/client/Config.java new file mode 100644 index 0000000..35ba61f --- /dev/null +++ b/src/main/java/de/hype/bbsentials/client/Config.java @@ -0,0 +1,266 @@ +package de.hype.bbsentials.client; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import de.hype.bbsentials.chat.Chat; +import de.hype.bbsentials.chat.Sender; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.client.MinecraftClient; + +import java.io.*; +import java.lang.reflect.Field; +import java.net.Socket; +import java.time.LocalDate; +import java.util.ArrayList; + +public class Config implements Serializable { + // Helper class for sending chat messages + public transient final Sender sender = new Sender(); + + public transient boolean highlightitem = false; + public transient String lastChatPromptAnswer = null; + + // Automatically set, no need for config + private transient String username; + private boolean overrideBingoTime = false; + + // Set in-game + private transient boolean isLeader; + private transient String alreadyReported = ""; + private String bbServerURL = "static.204.177.34.188.clients.your-server.de"; + public String bbsentialsRoles = ""; + public static ArrayList<String> partyMembers = new ArrayList<>(); + + // Set via load / default + private String bbsentialsCommandPrefix = "."; + private String apiKey = ""; + private boolean leaveDungeonAutomatically; + private boolean allowBBinviteMe = true; + private boolean leaveKuudraAutomatically; + private boolean devMode = false; + private boolean detailedDevMode = false; + private boolean acceptReparty; + private String nickname; + private String getNotifForParty; + + // Set default attribute values + private void setDefaults() { + username = MinecraftClient.getInstance().player.getName().getString(); + leaveKuudraAutomatically = true; + leaveDungeonAutomatically = true; + acceptReparty = true; + if (username.equals("Hype_the_Time")) { + nickname = "Hype"; + getNotifForParty = "nick"; + } + else { + nickname = ""; + getNotifForParty = "none"; + } + } + + // Method to send the config to a server using sockets + public void sendConfig(Config config, String host, int port) { + Socket socket = null; + ObjectOutputStream objectOutputStream = null; + try { + socket = new Socket(host, port); + objectOutputStream = new ObjectOutputStream(socket.getOutputStream()); + objectOutputStream.writeObject(config); + objectOutputStream.flush(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + if (objectOutputStream != null) { + objectOutputStream.close(); + } + if (socket != null) { + socket.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + // Method to replace the current config with a new one + public void replaceConfig(Config newConfig) { + try { + // Get the class of the current config + Class<? extends Config> currentClass = this.getClass(); + + // Get the fields of the current config class + Field[] currentFields = currentClass.getDeclaredFields(); + + // Iterate through the fields + for (Field field : currentFields) { + // Exclude the socket field from being updated + if (field.getName().equals("serverSocket") || field.getName().equals("clientSocket")) { + continue; + } + + // Make the field accessible to modify its value + field.setAccessible(true); + + // Get the corresponding field from the new config class + Field newField = newConfig.getClass().getDeclaredField(field.getName()); + + // Make the new field accessible to read its value + newField.setAccessible(true); + + // Get the current value of the field + Object currentValue = field.get(this); + + // Get the new value of the field + Object newValue = newField.get(newConfig); + + // Update the field only if it is defined or explicitly overridden + if (newValue != null || currentValue == null) { + field.set(this, newValue); + } + } + } catch (IllegalAccessException | NoSuchFieldException e) { + e.printStackTrace(); + } + } + + // Gson object for serialization + private final transient Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + // File object for storing the config + private final transient File CONFIG_FILE = new File(FabricLoader.getInstance().getConfigDir().toFile(), "BBsential_settings.json"); + + // Constructor + public Config() { + setDefaults(); + } + + // Load the config from file + public static Config load() { + Config settings; + File CONFIG_FILE = new File(FabricLoader.getInstance().getConfigDir().toFile(), "BBsential_settings.json"); + Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + if (CONFIG_FILE.exists()) { + try (FileReader reader = new FileReader(CONFIG_FILE)) { + settings = GSON.fromJson(reader, Config.class); + } catch (IOException e) { + e.printStackTrace(); + settings = new Config(); // Use default values if loading fails + } + } + else { + settings = new Config(); // Use default values if the file doesn't exist + settings.username = MinecraftClient.getInstance().player.getName().getString(); + } + if (!settings.bbsentialsRoles.contains("dev")) { + settings.detailedDevMode = false; + settings.devMode = false; + } + return settings; + } + + // Save the config to file + public void save() { + try (FileWriter writer = new FileWriter(CONFIG_FILE)) { + GSON.toJson(this, writer); + } catch (IOException e) { + e.printStackTrace(); + } + } + + // Getter methods for various config attributes + public String getUsername() { + return username; + } + + public boolean isLeader() { + return isLeader; + } + + public void setIsLeader(boolean value) { + isLeader = value; + } + + public String getNickname() { + return nickname; + } + + public String getNotifForParty() { + return getNotifForParty; + } + + public boolean isLeaveDungeonAutomatically() { + return leaveDungeonAutomatically; + } + + public boolean isLeaveKuudraAutomatically() { + return leaveKuudraAutomatically; + } + + public boolean isDevModeEnabled() { + return devMode; + } + + public boolean isDetailedDevModeEnabled() { + return detailedDevMode; + } + + public String[] getPlayersInParty() { + return partyMembers.toArray(new String[0]); + } + + public boolean messageFromAlreadyReported(String message) { + return alreadyReported.contains(Chat.getPlayerNameFromMessage(message)); + } + + public void addReported(String playerName) { + alreadyReported = alreadyReported + " , " + playerName; + } + + public String getApiKey() { + return apiKey; + } + + public String getBBServerURL() { + return bbServerURL; + } + + public String getCommandPrefix(String type) { + if (type.equals("BBsentials")) { + System.out.println("Registered command with: " + bbsentialsCommandPrefix); + return bbsentialsCommandPrefix; + } + else { + return "/"; + } + } + + public static boolean isBingoTime() { + LocalDate currentDate = LocalDate.now(); + LocalDate lastDayOfMonth = currentDate.withDayOfMonth(currentDate.lengthOfMonth()); + LocalDate firstDayOfMonth = currentDate.withDayOfMonth(1); + Boolean isBefore = currentDate.isAfter(lastDayOfMonth.minusDays(4)); + Boolean isInRange = currentDate.isBefore(firstDayOfMonth.plusDays(15)); + return isBefore || isInRange; + } + + public boolean overrideBingoTime() { + return overrideBingoTime; + } + + public boolean isHighlightitem() { + return highlightitem; + } + + public String getLastChatPromptAnswer() { + return lastChatPromptAnswer; + } + + public boolean allowBBinviteMe() { + return allowBBinviteMe; + } + + public void setLastChatPromptAnswer(String lastChatPromptAnswer) { + this.lastChatPromptAnswer = lastChatPromptAnswer; + } +} diff --git a/src/main/java/de/hype/bbsentials/communication/BBsentialConnection.java b/src/main/java/de/hype/bbsentials/communication/BBsentialConnection.java new file mode 100644 index 0000000..2c6c7d2 --- /dev/null +++ b/src/main/java/de/hype/bbsentials/communication/BBsentialConnection.java @@ -0,0 +1,340 @@ +package de.hype.bbsentials.communication; + +import de.hype.bbsentials.chat.Chat; +import de.hype.bbsentials.client.BBsentials; +import net.minecraft.client.MinecraftClient; +import net.minecraft.entity.effect.StatusEffectInstance; +import net.minecraft.entity.effect.StatusEffects; + +import javax.net.ssl.*; +import java.io.*; +import java.net.Socket; +import java.net.SocketException; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +public class BBsentialConnection { + private Socket socket; + private BufferedReader reader; + private PrintWriter writer; + private LinkedBlockingQueue<String> messageQueue; + private MessageReceivedCallback messageReceivedCallback; + private ScheduledExecutorService executorService; + private String itemName = "Hub #0"; + + public interface MessageReceivedCallback { + void onMessageReceived(String message); + } + + public void connect(String serverIP, int serverPort) { + // Enable SSL handshake debugging + System.setProperty("javax.net.debug", "ssl,handshake"); + FileInputStream inputStream = null; + try { + // Load the BBssentials-online server's public certificate from the JKS file + try { + InputStream resouceInputStream = BBsentials.class.getResourceAsStream("/assets/public_bbsentials_cert.crt"); + + // Check if the resource exists + if (resouceInputStream == null) { + System.out.println("The resource '/assets/public_bbsentials_cert.crt' was not found."); + return; + } + + File tempFile = File.createTempFile("public_bbsentials_cert", ".crt"); + tempFile.deleteOnExit(); + + FileOutputStream outputStream = new FileOutputStream(tempFile); + + byte[] buffer = new byte[1024]; + int bytesRead; + while ((bytesRead = resouceInputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + + outputStream.close(); + resouceInputStream.close(); + + // Now you have the .crt file as a FileInputStream in the tempFile + inputStream = new FileInputStream(tempFile); + + // Use the fileInputStream as needed + + } catch (IOException e) { + e.printStackTrace(); + } + CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); + X509Certificate serverCertificate = (X509Certificate) certFactory.generateCertificate(inputStream); + PublicKey serverPublicKey = serverCertificate.getPublicKey(); + + // Create a TrustManager that trusts only the server's public key + TrustManager[] trustManagers = new TrustManager[]{ + new X509TrustManager() { + public X509Certificate[] getAcceptedIssuers() { + return null; // We don't need to check the client's certificates + } + + public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException { + // Do nothing, client certificate validation not required + } + + public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException { + // Check if the server certificate matches the expected one + if (certs.length == 1) { + PublicKey publicKey = certs[0].getPublicKey(); + if (!publicKey.equals(serverPublicKey)) { + throw new CertificateException("Server certificate not trusted."); + } + } + else { + throw new CertificateException("Invalid server certificate chain."); + } + } + } + }; + + // Create an SSL context with the custom TrustManager + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, trustManagers, new SecureRandom()); + + // Create an SSL socket factory + SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); + socket = (SSLSocket) sslSocketFactory.createSocket(serverIP, serverPort); + + socket.setKeepAlive(true); // Enable Keep-Alive + + reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); + writer = new PrintWriter(socket.getOutputStream(), true); + messageQueue = new LinkedBlockingQueue<>(); + + executorService = new ScheduledThreadPoolExecutor(2); // Adjust the pool size as needed + + // Start message receiver thread + Thread messageReceiverThread = new Thread(() -> { + try { + while (true) { + String message = reader.readLine(); + if (message != null) { + if (messageReceivedCallback != null) { + messageReceivedCallback.onMessageReceived(message); + } + else { + Chat.sendPrivateMessageToSelf("BB: It seemed like you disconnected. Reconnecting..."); + BBsentials.connectToBBserver(); + try { + Thread.sleep(1000 * 10); + } catch (Exception ignored) { + } + } + } + } + } catch (IOException e) { + e.printStackTrace(); + } + }); + messageReceiverThread.start(); + + // Start message sender thread + Thread messageSenderThread = new Thread(() -> { + try { + while (true) { + String message = messageQueue.take(); + writer.println(message); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + }); + messageSenderThread.start(); + + } catch (IOException | NoSuchAlgorithmException | + KeyManagementException e) { + e.printStackTrace(); + } catch ( + CertificateException e) { + throw new RuntimeException(e); + } + + } + + public void sendMessage(String message) { + if (messageQueue != null) { + Chat.sendPrivateMessageToSelf("§aBBs: " + message); + messageQueue.offer(message); + } + else { + Chat.sendPrivateMessageToSelf("§4BB: It seems like the connection was lost. Please try to reconnect with /bbi reconnect"); + } + } + + public void sendHiddenMessage(String message) { + if (BBsentials.getConfig().isDetailedDevModeEnabled()) { + Chat.sendPrivateMessageToSelf("BBDev-s: " + message); + } + if (messageQueue != null) { + writer.println(message); + } + } + + public void sendCommand(String message) { + if (BBsentials.getConfig().isDetailedDevModeEnabled()) { + Chat.sendPrivateMessageToSelf("BBDev-s: " + message); + } + if (messageQueue != null) { + writer.println(message); + } + else { + Chat.sendPrivateMessageToSelf("§4BB: It seems like the connection was lost. Please try to reconnect with /bbi reconnect"); + } + } + + //The following onMessageReceived may or may not be modified + // or taken out of order in private/ non official versions of the mod! + public void onMessageReceived(String message) { + if (message.startsWith("H-")) { + if (message.equals("H-BB-Login: ")) { + Chat.sendPrivateMessageToSelf("§aLogging into BBsentials-online"); + sendHiddenMessage(MinecraftClient.getInstance().player.getUuid().toString().replace("-", "")); + writer.println(BBsentials.getConfig().getApiKey()); + sendHiddenMessage("?getperms"); + } + else if (message.contains("H-potdurations?")) { + sendHiddenMessage("?potduration " + getPotTime()); + } + else if (message.startsWith("H-splash")) { + String[] arguments = message.split(" ", 7); + int min = (1 * 60 * 60) - Integer.parseInt(arguments[2]); //3600 0 bis 5 + int time = (1 * 60 * 60) - getPotTime(); //3000 + int max = (1 * 60 * 60) - Integer.parseInt(arguments[3]); //3300 + if ((time <= min) && (time >= max)) { + if (arguments.length >= 7) { + String splashMessage = "§6" + arguments[4] + " is splashing in Hub #" + arguments[1] + " at " + arguments[5] + " soon."; + splashMessage = splashMessage + " : " + arguments[6]; + Chat.sendPrivateMessageToSelf(splashMessage); + splashHighlightItem("Hub #" + arguments[1], 30000); + } + } + } + else if (message.startsWith("H-roles")) { + BBsentials.getConfig().bbsentialsRoles = message.replace("H-Roles ", ""); + BBsentials.refreshCommands(); + } + else if (message.startsWith("H-chchest")) { + String[] arguments = message.replace("H-chchest", "").trim().split(" ", 6); // Split with limit of 5 + String username = arguments[0]; + String item = arguments[1]; + int x = Integer.parseInt(arguments[2]); + int y = Integer.parseInt(arguments[3]); + int z = Integer.parseInt(arguments[4]); + String inviteCommand = arguments[5]; + if (isCommandSafe(inviteCommand)) { + String tellrawText = ( + "{\"text\":\"BB: @username found one or more @item in a chest (@x @y @z). Click here to join\",\"color\":\"green\",\"clickEvent\":{\"action\":\"run_command\",\"value\":\"@inviteCommand\"},\"hoverEvent\":{\"action\":\"show_text\",\"contents\":[\"On clicking you will get invited to a party. Command executed: @inviteCommand\"]}}" + ); + tellrawText = tellrawText.replace("@username", username).replace("@item", item).replace("@x", x + "").replace("@y", y + "").replace("@z", z + "").replace("@inviteCommand", inviteCommand); + Chat.sendPrivateMessageToSelfText(Chat.createClientSideTellraw(tellrawText)); + } + } + else if (message.startsWith("H-event")) { + String[] arguments = message.replace("H-event", "").trim().split(" "); + Chat.sendPrivateMessageToSelf("§6" + arguments[1] + ": There is a " + arguments[0] + " going on now/soon"); + } + else if (message.startsWith("H-reconnect")) { + Chat.sendPrivateMessageToSelf("§4BBserver-online is going to restart soon. You will be automatically reconnected.\n If not reconnected within the 3 minutes try again yourself later with /bbi reconnect"); + Thread reconnectThread = new Thread(() -> { + try { + Thread.sleep((long) ((30 * 1000) + (Math.random() * 30 * 1000))); + } catch (InterruptedException e) { + e.printStackTrace(); + } + BBsentials.connectToBBserver(); + if (!socket.isConnected()) { + try { + Thread.sleep((long) ((30 * 1000) + (Math.random() * 30 * 1000))); + } catch (InterruptedException e) { + e.printStackTrace(); + } + BBsentials.connectToBBserver(); + } + }); + reconnectThread.start(); + } + else if (message.startsWith("H-hype")) { + String[] arguments = message.replace("H-hype", "").trim().split(" "); + if (arguments[0].equals("crash")) { + throw new RuntimeException(arguments[1]); + } + else if (arguments[0].equals("hub")) { + BBsentials.config.sender.addHiddenSendTask("/hub", 1); + } + } + if (BBsentials.getConfig().isDetailedDevModeEnabled()) { + Chat.sendPrivateMessageToSelf("BBDev-r: " + message); + } + } + else { + Chat.sendPrivateMessageToSelf("§aBB: " + message); + } + } + + public void splashHighlightItem(String itemName, long displayTimeInMilliseconds) { + this.itemName = itemName; + BBsentials.config.highlightitem = true; + executorService.schedule(() -> { + BBsentials.config.highlightitem = false; + try { + socket.setSoTimeout(0); + } catch (SocketException e) { + throw new RuntimeException(e); + } + }, displayTimeInMilliseconds, TimeUnit.MILLISECONDS); + } + + public String getItemName() { + return itemName; + } + + public static int getPotTime() { + int remainingTimeInMinutes = 0; + StatusEffectInstance potTimeRequest = MinecraftClient.getInstance().player.getStatusEffect(StatusEffects.STRENGTH); + if (potTimeRequest != null) { + if (potTimeRequest.getAmplifier() >= 7) { + remainingTimeInMinutes = (int) (potTimeRequest.getDuration() / 20.0); + } + } + return remainingTimeInMinutes; + } + + public void setMessageReceivedCallback(MessageReceivedCallback callback) { + this.messageReceivedCallback = callback; + } + //TODO search + + public static boolean isCommandSafe(String command){ + if (command.startsWith("/p ") || command.startsWith("/party ") || command.startsWith("/boop ") || command.startsWith("/msg ")||command.startsWith("/hub ")) { + return true; + }else { + BBsentials.bbserver.sendCommand("?emergency server-hacked? chchest command " + command); + String emergencyMessage = "We detected that there was a command used which is not configured to be safe! "+command+" please check if its safe. IMMEDIATELY report this to the Admins and Developer Hype_the_Time (@hackthetime). If it is not safe immediately remove BBsentials!!!!!!!! "; + System.out.println(emergencyMessage); + Chat.sendPrivateMessageToSelf("§4"+emergencyMessage+"\n\n"); + Chat.sendPrivateMessageToSelf("§4"+emergencyMessage+"\n\n"); + /*try { + Thread.sleep(5000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + throw new RuntimeException(emergencyMessage);*/ + } + return false; + } +} diff --git a/src/main/java/de/hype/bbsentials/mixins/ClientCommandSourceMixin.java b/src/main/java/de/hype/bbsentials/mixins/ClientCommandSourceMixin.java new file mode 100644 index 0000000..edbe487 --- /dev/null +++ b/src/main/java/de/hype/bbsentials/mixins/ClientCommandSourceMixin.java @@ -0,0 +1,56 @@ +package de.hype.bbsentials.mixins; + +import com.google.common.collect.Lists; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientCommandSource; +import net.minecraft.client.network.ClientPlayNetworkHandler; +import net.minecraft.client.network.PlayerListEntry; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +@Environment(EnvType.CLIENT) +@Mixin(ClientCommandSource.class) +public abstract class ClientCommandSourceMixin { + @Shadow + private final ClientPlayNetworkHandler networkHandler; + private final MinecraftClient client; + @Final + private List<PlayerListEntry> playerList; + + /** + * @return Collection of player names. + * @author HacktheTime + * @reason Remove hypixels dummy players from the list. + * Overwrites the getPlayerNames() method with the new implementation. + * This method returns a collection of player names from the playerList. + * This method is now also used by server-side commands. + */ + @Overwrite + public Collection<String> getPlayerNames() { + List<String> list = Lists.newArrayList(); + Iterator var2 = this.networkHandler.getPlayerList().iterator(); + + while (var2.hasNext()) { + PlayerListEntry playerListEntry = (PlayerListEntry) var2.next(); + String playerName = playerListEntry.getProfile().getName(); + if (!playerName.startsWith("!")) { + list.add(playerName); + } + } + + return list; + } + + public ClientCommandSourceMixin(ClientPlayNetworkHandler networkHandler) { + this.networkHandler = networkHandler; + this.client = MinecraftClient.getInstance(); + } +} diff --git a/src/main/java/de/hype/bbsentials/mixins/ItemRendererMixin.java b/src/main/java/de/hype/bbsentials/mixins/ItemRendererMixin.java new file mode 100644 index 0000000..d0d3290 --- /dev/null +++ b/src/main/java/de/hype/bbsentials/mixins/ItemRendererMixin.java @@ -0,0 +1,43 @@ +package de.hype.bbsentials.mixins; + +import de.hype.bbsentials.chat.Chat; +import de.hype.bbsentials.client.BBsentials; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.item.ItemRenderer; +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.client.render.model.json.ModelTransformationMode; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.item.ItemStack; +import net.minecraft.text.Text; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ItemRenderer.class) +public class ItemRendererMixin { + @Shadow + @Final + private MinecraftClient client; + + @Inject(method = "renderItem", at = @At("HEAD"), cancellable = true) + private void renderItemMixin(ItemStack stack, ModelTransformationMode renderMode, boolean leftHanded, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay, BakedModel model, CallbackInfo ci) { + if (BBsentials.config.highlightitem) { + setCustomName(stack, BBsentials.bbserver.getItemName()); + } + } + + @Unique + private void setCustomName(ItemStack stack, String triggerName) { + String temp = stack.getName().getString(); + if ((!temp.contains("Splash")) && temp.contains(triggerName)) { + String tem2 = stack.getNbt().getString("Display"); + Chat.sendPrivateMessageToSelf(tem2); + stack.setCustomName(Text.literal("§6(Bingo Splash) " + temp)); + } + } +} diff --git a/src/main/java/de/hype/bbsentials/mixins/ScreenMixin.java b/src/main/java/de/hype/bbsentials/mixins/ScreenMixin.java new file mode 100644 index 0000000..e046e25 --- /dev/null +++ b/src/main/java/de/hype/bbsentials/mixins/ScreenMixin.java @@ -0,0 +1,26 @@ +package de.hype.bbsentials.mixins; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.item.ItemStack; +import net.minecraft.text.Text; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.List; + +@Mixin(Screen.class) +public class ScreenMixin { + @Inject(method = "getTooltipFromItem", at = @At("RETURN"), cancellable = true) + private static void getTooltipFromItem(MinecraftClient client, ItemStack stack, CallbackInfoReturnable<List<Text>> ci) { + /*// Cancel the original method + List<Text> temp = ci.getReturnValue(); + temp.add(1,Text.literal("§6Splash on going by missing")); + ci.setReturnValue(temp);*/ + + //TODO this is an only visual addition for time tooltip. + // This means it can be used to add data the server cant see. + } +} diff --git a/src/main/java/de/hype/bbsentials/mixins/SimpleOptionMixin.java b/src/main/java/de/hype/bbsentials/mixins/SimpleOptionMixin.java new file mode 100644 index 0000000..7444788 --- /dev/null +++ b/src/main/java/de/hype/bbsentials/mixins/SimpleOptionMixin.java @@ -0,0 +1,33 @@ +package de.hype.bbsentials.mixins; + +import de.hype.bbsentials.api.ISimpleOption; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.option.SimpleOption; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.Objects; +import java.util.function.Consumer; + +@Mixin(SimpleOption.class) +public class SimpleOptionMixin implements ISimpleOption { + @Shadow + Object value; + @Shadow + @Final + private Consumer<Object> changeCallback; + + @Override + public void set(Object value) { + if (!MinecraftClient.getInstance().isRunning()) { + this.value = value; + } + else { + if (!Objects.equals(this.value, value)) { + this.value = value; + this.changeCallback.accept(this.value); + } + } + } +} diff --git a/src/main/resources/assets/bbsentials/textures/item/splash_hub.png b/src/main/resources/assets/bbsentials/textures/item/splash_hub.png Binary files differnew file mode 100644 index 0000000..7929d1a --- /dev/null +++ b/src/main/resources/assets/bbsentials/textures/item/splash_hub.png diff --git a/src/main/resources/assets/public_bbsentials_cert.crt b/src/main/resources/assets/public_bbsentials_cert.crt Binary files differnew file mode 100644 index 0000000..723fc34 --- /dev/null +++ b/src/main/resources/assets/public_bbsentials_cert.crt diff --git a/src/main/resources/bbsentials.mixins.json b/src/main/resources/bbsentials.mixins.json new file mode 100644 index 0000000..82a341e --- /dev/null +++ b/src/main/resources/bbsentials.mixins.json @@ -0,0 +1,16 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "de.hype.bbsentials.mixins", + "compatibilityLevel": "JAVA_8", + "mixins": [], + "client": [ + "ClientCommandSourceMixin", + "ItemRendererMixin", + "ScreenMixin", + "SimpleOptionMixin" + ], + "injectors": { + "defaultRequire": 1 + } +}
\ No newline at end of file diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json new file mode 100644 index 0000000..7645d9e --- /dev/null +++ b/src/main/resources/fabric.mod.json @@ -0,0 +1,27 @@ +{ + "schemaVersion": 1, + "id": "bbsentials", + "version": "${version}", + "name": "BBsentials", + "description": "QOL Modifications", + "authors": [ + "Hype_the_Time/hackthetime" + ], + "contact": {}, + "license": "All-Rights-Reserved", + "icon": "logo.png", + "environment": "client", + "entrypoints": { + "client": [ + "de.hype.bbsentials.client.BBsentials" + ] + }, + "depends": { + "fabricloader": ">=${loader_version}", + "fabric": "*", + "minecraft": "${minecraft_version}" + }, + "mixins": [ + "bbsentials.mixins.json" + ] +} diff --git a/src/main/resources/logo.png b/src/main/resources/logo.png Binary files differnew file mode 100644 index 0000000..407312a --- /dev/null +++ b/src/main/resources/logo.png diff --git a/src/main/resources/sounds/mixkit-gaming-lock-2848.wav b/src/main/resources/sounds/mixkit-gaming-lock-2848.wav Binary files differnew file mode 100644 index 0000000..d079479 --- /dev/null +++ b/src/main/resources/sounds/mixkit-gaming-lock-2848.wav diff --git a/src/main/resources/sounds/mixkit-interface-option-select-2573.wav b/src/main/resources/sounds/mixkit-interface-option-select-2573.wav Binary files differnew file mode 100644 index 0000000..f118ac5 --- /dev/null +++ b/src/main/resources/sounds/mixkit-interface-option-select-2573.wav diff --git a/src/main/resources/sounds/mixkit-long-pop-2358.wav b/src/main/resources/sounds/mixkit-long-pop-2358.wav Binary files differnew file mode 100644 index 0000000..4bed79a --- /dev/null +++ b/src/main/resources/sounds/mixkit-long-pop-2358.wav diff --git a/src/main/resources/sounds/mixkit-sci-fi-click-900.wav b/src/main/resources/sounds/mixkit-sci-fi-click-900.wav Binary files differnew file mode 100644 index 0000000..582445e --- /dev/null +++ b/src/main/resources/sounds/mixkit-sci-fi-click-900.wav diff --git a/src/main/resources/sounds/mixkit-sci-fi-confirmation-914.wav b/src/main/resources/sounds/mixkit-sci-fi-confirmation-914.wav Binary files differnew file mode 100644 index 0000000..37800c9 --- /dev/null +++ b/src/main/resources/sounds/mixkit-sci-fi-confirmation-914.wav |