mirror of
https://github.com/vortres/ZenMod.git
synced 2025-05-23 02:46:46 -04:00
Initial commit
This commit is contained in:
commit
3e884e6711
99 changed files with 7466 additions and 0 deletions
.gitattributes.gitignoreLICENSEREADME.mdbuild.gradlegradle.properties
gradle/wrapper
gradlewgradlew.batsettings.gradlesrc/main
java/land/chipmunk/chipmunkmod
ChipmunkMod.javaConfiguration.java
command
data
BlockArea.javaChomeNSBotCommand.javaLambdaBotCommand.javaMutablePlayerListEntry.javaQBotCommand.java
listeners
mixin
ChatInputSuggestorMixin.javaChatScreenMixin.javaClientConnectionAccessor.javaClientConnectionInvoker.javaClientConnectionMixin.javaClientPlayNetworkHandlerAccessor.javaClientPlayNetworkHandlerMixin.javaClientPlayerEntityMixin.javaCommandDispatcherMixin.javaElderGuardianAppearanceParticleMixin.javaIdentifierMixin.javaMinecraftClientAccessor.javaPlayerListEntryAccessor.javaSoundSystemMixin.javaStringHelperMixin.javaTextFieldWidgetMixin.javaTextMixin.javaTextSerializerMixin.java
modules
Chat.javaChomeNSAuth.javaChomeNSBotCommandSuggestions.javaCommandCore.javaCommandLoopManager.javaCustomChat.javaKaboomCheck.javaLambdaAuth.javaLambdaCommandSuggestions.javaPlayers.javaQBotAuth.javaQBotCommandSuggestions.javaRainbowName.javaSelfCare.javaSongPlayer.javaTabComplete.javaTransactionManager.java
song
Instrument.javaMidiConverter.javaNBSConverter.javaNote.javaSong.javaSongLoaderException.javaSongLoaderThread.java
util
resources
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
33
.gitignore
vendored
Normal file
33
.gitignore
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
# gradle
|
||||
.gradle/
|
||||
build/
|
||||
out/
|
||||
classes/
|
||||
|
||||
# eclipse
|
||||
*.launch
|
||||
|
||||
# idea
|
||||
.idea/
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
|
||||
# vscode
|
||||
.settings/
|
||||
.vscode/
|
||||
bin/
|
||||
.classpath
|
||||
.project
|
||||
|
||||
# macos
|
||||
*.DS_Store
|
||||
|
||||
# fabric
|
||||
run/
|
||||
|
||||
# java
|
||||
hs_err_*.log
|
||||
replay_*.log
|
||||
*.hprof
|
||||
*.jfr
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2024 ZenZoya
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
17
README.md
Normal file
17
README.md
Normal file
|
@ -0,0 +1,17 @@
|
|||
# ZenMod
|
||||
My fork of [ChipmunkMod (7cc5c4f330d47060's fork)](https://code.chipmunk.land/7cc5c4f330d47060/chipmunkmod)
|
||||
|
||||
If you are not sure if this MOD is safe to Run or Compile, you can read through every line of code, and compile it with the below instructions. You can also see the commit history by clicking on the `<numbers> commits` button, to make sure nobody has added any `exploits or introduced vulnerabilities` to the code.
|
||||
|
||||
If you find any exploits, security issues, etc. in the code, please send me an issue or pull request and I will try to respond to it as soon as possible.
|
||||
|
||||
## How to install?
|
||||
You can use the pre-compiled releases in the [Releases sidebar](https://github.com/vortres/ZenMod/releases), or you can compile it yourself.
|
||||
|
||||
To comple it yourself make sure you have a Java 21 JDK installed (this may not work for newer JDK versions), then run `./gradlew build --parallel` for Unix(-like) OSes or `gradlew.bat build` for Windows. If the build was successful, the compiled JAR file should be in "build/libs" directory.
|
||||
|
||||
Make sure you have the Fabric loader and Fabric API installed for version 1.21.1, and copy the JAR file to your `mods` folder.
|
||||
|
||||
If thats not clear enough, ask your search engine `how to install a fabric mod`.
|
||||
|
||||
If it pops up with errors you dont understand [create an issue](https://github.com/vortres/ZenMod/issues/new) about it
|
78
build.gradle
Normal file
78
build.gradle
Normal file
|
@ -0,0 +1,78 @@
|
|||
plugins {
|
||||
id 'fabric-loom' version '1.7-SNAPSHOT'
|
||||
id 'maven-publish'
|
||||
}
|
||||
|
||||
sourceCompatibility = JavaVersion.VERSION_21
|
||||
targetCompatibility = JavaVersion.VERSION_21
|
||||
|
||||
archivesBaseName = project.archives_base_name
|
||||
version = project.mod_version
|
||||
group = project.maven_group
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
// https://github.com/MeteorDevelopment/meteor-client/blob/master/build.gradle#L46
|
||||
configurations {
|
||||
implementation.extendsFrom(library)
|
||||
shadow.extendsFrom(library)
|
||||
include.extendsFrom(library)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
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}"
|
||||
|
||||
library "net.kyori:adventure-platform-fabric:5.14.1" // for Minecraft 1.21-1.21.1
|
||||
library "net.kyori:adventure-text-serializer-gson:4.17.0"
|
||||
library "net.kyori:adventure-text-serializer-legacy:4.17.0"
|
||||
library "org.luaj:luaj-jse:3.0.1"
|
||||
}
|
||||
|
||||
processResources {
|
||||
inputs.property "version", project.version
|
||||
|
||||
filesMatching("fabric.mod.json") {
|
||||
expand "version": project.version
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
it.options.release = 21
|
||||
}
|
||||
|
||||
java {
|
||||
// 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}"}
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
}
|
||||
}
|
18
gradle.properties
Normal file
18
gradle.properties
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Done to increase the memory available to gradle.
|
||||
org.gradle.jvmargs=-Xmx1G
|
||||
org.gradle.parallel=true
|
||||
|
||||
# Fabric Properties
|
||||
# check these on https://fabricmc.net/develop
|
||||
minecraft_version=1.21.1
|
||||
yarn_mappings=1.21.1+build.3
|
||||
loader_version=0.16.5
|
||||
|
||||
# Mod Properties
|
||||
mod_version = 1.0.0-mc1.21-1
|
||||
maven_group = land.chipmunk.chipmunkmod
|
||||
archives_base_name = zenmod
|
||||
|
||||
# Dependencies
|
||||
fabric_version=0.105.0+1.21.1
|
||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
7
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
7
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
249
gradlew
vendored
Executable file
249
gradlew
vendored
Executable file
|
@ -0,0 +1,249 @@
|
|||
#!/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
|
||||
JAVACMD=/usr/bin/java
|
||||
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
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
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
|
||||
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" "$@"
|
92
gradlew.bat
vendored
Normal file
92
gradlew.bat
vendored
Normal file
|
@ -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
|
10
settings.gradle
Normal file
10
settings.gradle
Normal file
|
@ -0,0 +1,10 @@
|
|||
pluginManagement {
|
||||
repositories {
|
||||
maven {
|
||||
name = 'Fabric'
|
||||
url = 'https://maven.fabricmc.net/'
|
||||
}
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
115
src/main/java/land/chipmunk/chipmunkmod/ChipmunkMod.java
Normal file
115
src/main/java/land/chipmunk/chipmunkmod/ChipmunkMod.java
Normal file
|
@ -0,0 +1,115 @@
|
|||
package land.chipmunk.chipmunkmod;
|
||||
|
||||
import com.google.gson.GsonBuilder;
|
||||
import land.chipmunk.chipmunkmod.util.misc.Version;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.fabricmc.loader.api.metadata.ModMetadata;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import land.chipmunk.chipmunkmod.modules.KaboomCheck;
|
||||
import land.chipmunk.chipmunkmod.modules.Players;
|
||||
import land.chipmunk.chipmunkmod.modules.SelfCare;
|
||||
import land.chipmunk.chipmunkmod.util.gson.BlockPosTypeAdapter;
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import java.io.InputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
public class ChipmunkMod implements ModInitializer {
|
||||
public static final Logger LOGGER;
|
||||
public static final File MOD_DIR = new File("zenmod");
|
||||
private static final File CONFIG_FILE = new File(MOD_DIR, "config.json");
|
||||
|
||||
public static final String MOD_ID = "zenmod";
|
||||
public static final ModMetadata MOD_META;
|
||||
public static final String NAME;
|
||||
public static final Version VERSION;
|
||||
|
||||
public static ChipmunkMod INSTANCE;
|
||||
public static Configuration CONFIG;
|
||||
public static MinecraftClient MCInstance;
|
||||
|
||||
public static ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
|
||||
|
||||
static {
|
||||
MOD_META = FabricLoader.getInstance().getModContainer(MOD_ID).orElseThrow().getMetadata();
|
||||
|
||||
NAME = MOD_META.getName();
|
||||
LOGGER = LoggerFactory.getLogger(NAME);
|
||||
|
||||
String versionString = MOD_META.getVersion().getFriendlyString();
|
||||
if (versionString.contains("-")) versionString = versionString.split("-")[0];
|
||||
// When building and running through IntelliJ and not Gradle it doesn't replace the version so just default to v0.0.0 (IDK why)
|
||||
if (versionString.equals("${version}")) versionString = "0.0.0";
|
||||
|
||||
VERSION = new Version(versionString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
INSTANCE = this;
|
||||
|
||||
try {
|
||||
CONFIG = loadConfig();
|
||||
} catch (IOException exception) {
|
||||
LOGGER.error("Could not load the config", exception);
|
||||
CONFIG = new Configuration(); // Create default config if loading fails
|
||||
}
|
||||
|
||||
MCInstance = MinecraftClient.getInstance();
|
||||
|
||||
if (CONFIG != null) {
|
||||
Players.INSTANCE.init();
|
||||
KaboomCheck.INSTANCE.init();
|
||||
SelfCare.INSTANCE.init();
|
||||
LOGGER.info("Loaded ZenMod (chipmunk mod fork)");
|
||||
} else {
|
||||
LOGGER.error("Failed to initialize ZenMod - CONFIG is null");
|
||||
}
|
||||
}
|
||||
|
||||
public static Configuration loadConfig() throws IOException {
|
||||
MOD_DIR.mkdirs();
|
||||
|
||||
final Gson gson = new GsonBuilder()
|
||||
.registerTypeAdapter(BlockPos.class, new BlockPosTypeAdapter())
|
||||
.create();
|
||||
final File file = CONFIG_FILE;
|
||||
|
||||
if (!file.exists()) {
|
||||
InputStream is = ChipmunkMod.class.getClassLoader().getResourceAsStream("default_config.json");
|
||||
if (is == null) {
|
||||
LOGGER.warn("Default config not found, creating new configuration");
|
||||
return new Configuration();
|
||||
}
|
||||
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
while (reader.ready()) sb.append((char) reader.read());
|
||||
final String defaultConfig = sb.toString();
|
||||
|
||||
// Write the default config
|
||||
BufferedWriter configWriter = new BufferedWriter(new FileWriter(file));
|
||||
configWriter.write(defaultConfig);
|
||||
configWriter.close();
|
||||
|
||||
return gson.fromJson(defaultConfig, Configuration.class);
|
||||
}
|
||||
|
||||
try (InputStream is = new FileInputStream(file);
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
|
||||
return gson.fromJson(reader, Configuration.class);
|
||||
}
|
||||
}
|
||||
}
|
93
src/main/java/land/chipmunk/chipmunkmod/Configuration.java
Normal file
93
src/main/java/land/chipmunk/chipmunkmod/Configuration.java
Normal file
|
@ -0,0 +1,93 @@
|
|||
package land.chipmunk.chipmunkmod;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import land.chipmunk.chipmunkmod.data.BlockArea;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
public class Configuration {
|
||||
public ClientConfig client = new ClientConfig();
|
||||
public Bots bots = new Bots();
|
||||
|
||||
public static class ClientConfig {
|
||||
public String prefix = ";";
|
||||
public String autoSkinUsername = "off";
|
||||
public Colors colors = new Colors();
|
||||
public CustomChat customChat = new CustomChat();
|
||||
public CommandCore core = new CommandCore();
|
||||
}
|
||||
|
||||
public static class Bots {
|
||||
public LambdaBotInfo lambda = new LambdaBotInfo("λ", null);
|
||||
public ChomeNSBotInfo chomens = new ChomeNSBotInfo("*", null, null, null);
|
||||
public BotInfo fnfboyfriend = new BotInfo("~", null);
|
||||
public BotInfo nbot = new BotInfo("?", null);
|
||||
public BotInfo qbot = new BotInfo("}", null);
|
||||
public BotInfo hbot = new BotInfo("#", null);
|
||||
public BotInfo sbot = new BotInfo(":", null);
|
||||
public TestBotInfo testbot = new TestBotInfo("-", null);
|
||||
public BotInfo chipmunk = new BotInfo("'", null);
|
||||
public BotInfo kittycorp = new BotInfo("^", null);
|
||||
}
|
||||
|
||||
public static class Colors {
|
||||
public String PRIMARY = "#3b82f6";
|
||||
public String SECONDARY = "#a855f7";
|
||||
public String DANGER = "#f43f5e";
|
||||
public String SUCCESS = "#10b981";
|
||||
public String GRAY = "#353b40";
|
||||
}
|
||||
|
||||
public static class CommandCore {
|
||||
public BlockArea relativeArea = new BlockArea(new BlockPos(0, 0, 0), new BlockPos(15, 1, 15));
|
||||
}
|
||||
|
||||
public static class CustomChat {
|
||||
public JsonObject format;
|
||||
}
|
||||
|
||||
// Default bot infos
|
||||
public static class BotInfo {
|
||||
public String prefix;
|
||||
public String key;
|
||||
|
||||
public BotInfo (String prefix, String key) {
|
||||
this.prefix = prefix;
|
||||
this.key = key;
|
||||
}
|
||||
}
|
||||
|
||||
// Special bot infos
|
||||
public static class LambdaBotInfo { // rn its just same as BotInfo class, but I will change it soon fr
|
||||
public String prefix;
|
||||
public String key;
|
||||
|
||||
public LambdaBotInfo (String prefix, String key) {
|
||||
this.prefix = prefix;
|
||||
this.key = key;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ChomeNSBotInfo {
|
||||
public String prefix;
|
||||
public String key;
|
||||
public String authKey;
|
||||
public String formatKey;
|
||||
|
||||
public ChomeNSBotInfo(String prefix, String key, String authKey, String formatKey) {
|
||||
this.prefix = prefix;
|
||||
this.key = key;
|
||||
this.authKey = authKey;
|
||||
this.formatKey = formatKey;
|
||||
}
|
||||
}
|
||||
|
||||
public static class TestBotInfo {
|
||||
public String prefix;
|
||||
public String webhookUrl;
|
||||
|
||||
public TestBotInfo (String prefix, String webhookUrl) {
|
||||
this.prefix = prefix;
|
||||
this.webhookUrl = webhookUrl;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
package land.chipmunk.chipmunkmod.command;
|
||||
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import land.chipmunk.chipmunkmod.util.misc.ColorUtils;
|
||||
import land.chipmunk.chipmunkmod.util.player.ChatUtils;
|
||||
import net.minecraft.text.*;
|
||||
import net.minecraft.util.Formatting;
|
||||
import net.minecraft.command.CommandRegistryAccess;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
|
||||
import land.chipmunk.chipmunkmod.command.commands.*;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class CommandManager {
|
||||
public CommandDispatcher<FabricClientCommandSource> dispatcher = new CommandDispatcher<>();
|
||||
public String prefix;
|
||||
|
||||
public static CommandManager INSTANCE;
|
||||
|
||||
public CommandManager (String prefix, CommandRegistryAccess commandRegistryAccess) {
|
||||
this.prefix = prefix;
|
||||
|
||||
TestCommand.register(this.dispatcher);
|
||||
CoreCommand.register(this.dispatcher);
|
||||
UsernameCommand.register(this.dispatcher);
|
||||
ItemCommand.register(this.dispatcher, commandRegistryAccess);
|
||||
EvalCommand.register(this.dispatcher);
|
||||
CustomChatCommand.register(this.dispatcher);
|
||||
MusicCommand.register(this.dispatcher);
|
||||
RainbowNameCommand.register(this.dispatcher);
|
||||
SayCommand.register(this.dispatcher);
|
||||
AutoSkinCommand.register(this.dispatcher);
|
||||
ReloadConfigCommand.register(this.dispatcher);
|
||||
SelfCareCommand.register(this.dispatcher);
|
||||
ValidateCommand.register(this.dispatcher);
|
||||
|
||||
// CloopCommand.register(this.dispatcher);
|
||||
// KickCommand.register(this.dispatcher);
|
||||
}
|
||||
|
||||
public void executeCommand (String command) {
|
||||
final MinecraftClient client = MinecraftClient.getInstance();
|
||||
|
||||
final FabricClientCommandSource commandSource = (FabricClientCommandSource) Objects.requireNonNull(client.getNetworkHandler()).getCommandSource();
|
||||
|
||||
try {
|
||||
dispatcher.execute(command, commandSource);
|
||||
} catch (CommandSyntaxException e) {
|
||||
ChatUtils.warningPrefix("CMD", String.valueOf(e.getRawMessage()));
|
||||
final Text context = getContext(e);
|
||||
if (context != null) commandSource.sendError(context);
|
||||
} catch (Exception e) {
|
||||
ChatUtils.warning(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public Text getContext (CommandSyntaxException exception) {
|
||||
final int _cursor = exception.getCursor();
|
||||
final String input = exception.getInput();
|
||||
|
||||
if (input == null || _cursor < 0) {
|
||||
return null;
|
||||
}
|
||||
final MutableText text = Text.literal("» ")
|
||||
.formatted(Formatting.GRAY);
|
||||
text.setStyle(text.getStyle().withClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, prefix + input)));
|
||||
|
||||
final int cursor = Math.min(input.length(), _cursor);
|
||||
|
||||
if (cursor > CommandSyntaxException.CONTEXT_AMOUNT) {
|
||||
text.append(Text.literal("..."));
|
||||
}
|
||||
|
||||
text
|
||||
.append(Text.literal(input.substring(Math.max(0, cursor - CommandSyntaxException.CONTEXT_AMOUNT), cursor)).setStyle(Style.EMPTY.withColor(ColorUtils.PRIMARY)))
|
||||
.append(Text.literal(input.substring(cursor)).setStyle(Style.EMPTY.withColor(ColorUtils.DANGER).withFormatting(Formatting.UNDERLINE)))
|
||||
.append(Text.translatable("command.context.here").setStyle(Style.EMPTY.withColor(ColorUtils.WARNING).withFormatting(Formatting.ITALIC)));
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
public static LiteralArgumentBuilder<FabricClientCommandSource> literal (String name) { return LiteralArgumentBuilder.literal(name); }
|
||||
public static <T> RequiredArgumentBuilder<FabricClientCommandSource, T> argument (String name, ArgumentType<T> type) { return RequiredArgumentBuilder.argument(name, type); }
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
package land.chipmunk.chipmunkmod.command.arguments;
|
||||
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
public class LocationArgumentType implements ArgumentType<Object> {
|
||||
private static final Collection<String> EXAMPLES = Arrays.<String>asList("songs/amogus.mid", "images/cat.jpg", "videos/badapple.mp4");
|
||||
private static final SimpleCommandExceptionType OOB_FILEPATH = new SimpleCommandExceptionType(Text.literal("The specified file path is outside of the allowed directory"));
|
||||
|
||||
private boolean allowsUrls = false;
|
||||
private boolean allowsPaths = false;
|
||||
private final Path root;
|
||||
|
||||
private LocationArgumentType (boolean allowsUrls, boolean allowsPaths, Path root) {
|
||||
this.allowsUrls = allowsUrls;
|
||||
this.allowsPaths = allowsPaths;
|
||||
this.root = root.toAbsolutePath().normalize();
|
||||
}
|
||||
|
||||
public static LocationArgumentType location (Path rootPath) { return new LocationArgumentType(true, true, rootPath); }
|
||||
public static LocationArgumentType url () { return new LocationArgumentType(true, false, null); }
|
||||
public static LocationArgumentType filepath (Path rootPath) { return new LocationArgumentType(false, true, rootPath); }
|
||||
|
||||
@Override
|
||||
public Object parse (StringReader reader) throws CommandSyntaxException {
|
||||
final String remaining = reader.getString().substring(reader.getCursor());
|
||||
if (allowsUrls && isUrlStart(remaining)) return parseUrl(reader);
|
||||
if (allowsPaths) return parsePath(reader);
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isUrlStart (String string) { return string.startsWith("http://") || string.startsWith("https://") || string.startsWith("ftp://"); }
|
||||
|
||||
public URL parseUrl (StringReader reader) throws CommandSyntaxException {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
while (reader.canRead() && reader.peek() != ' ') {
|
||||
sb.append(reader.read());
|
||||
}
|
||||
|
||||
try {
|
||||
return new URL(sb.toString());
|
||||
} catch (MalformedURLException exception) {
|
||||
throw new SimpleCommandExceptionType(Text.literal(exception.getMessage())).create();
|
||||
}
|
||||
}
|
||||
|
||||
public Path parsePath (StringReader reader) throws CommandSyntaxException {
|
||||
final String pathString = reader.readString();
|
||||
final Path path = Path.of(root.toString(), pathString).toAbsolutePath().normalize();
|
||||
if (!path.startsWith(root)) throw OOB_FILEPATH.create();
|
||||
return path;
|
||||
}
|
||||
|
||||
private static Object getLocation (CommandContext<?> context, String name) {
|
||||
return context.getArgument(name, Object.class);
|
||||
}
|
||||
|
||||
public static URL getUrl (CommandContext<?> context, String name) {
|
||||
final Object location = getLocation(context, name);
|
||||
if (location instanceof URL) return (URL) location;
|
||||
try {
|
||||
if (location instanceof Path) return new URL("file", "", -1, location.toString());
|
||||
} catch (MalformedURLException ignored) {
|
||||
return null; // The real question is whether this will actually ever get called
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Path getPath (CommandContext<?> context, String name) {
|
||||
final Object location = getLocation(context, name);
|
||||
if (location instanceof Path) return (Path) location;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getExamples () { return EXAMPLES; }
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package land.chipmunk.chipmunkmod.command.arguments;
|
||||
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import java.util.Collection;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class TimestampArgumentType implements ArgumentType<Long> {
|
||||
private static final Collection<String> EXAMPLES = Arrays.<String>asList("0:01", "1:23", "6:09");
|
||||
|
||||
private TimestampArgumentType () {
|
||||
}
|
||||
|
||||
public static TimestampArgumentType timestamp () { return new TimestampArgumentType(); }
|
||||
|
||||
@Override
|
||||
public Long parse (StringReader reader) throws CommandSyntaxException {
|
||||
long seconds = 0L;
|
||||
long minutes = 0L;
|
||||
|
||||
seconds = reader.readLong();
|
||||
if (reader.canRead() && reader.peek() == ':') {
|
||||
reader.skip();
|
||||
minutes = seconds;
|
||||
seconds = reader.readLong();
|
||||
}
|
||||
|
||||
return (seconds * 1000) + (minutes * 1000 * 60);
|
||||
}
|
||||
|
||||
// ? Should I create a getter method? Seems like reinventing the wheel since LongArgumentType#getLong is already a thing.
|
||||
|
||||
@Override
|
||||
public Collection<String> getExamples () { return EXAMPLES; }
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package land.chipmunk.chipmunkmod.command.commands;
|
||||
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import land.chipmunk.chipmunkmod.modules.SelfCare;
|
||||
import land.chipmunk.chipmunkmod.util.player.ChatUtils;
|
||||
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
|
||||
|
||||
import static com.mojang.brigadier.arguments.StringArgumentType.getString;
|
||||
import static com.mojang.brigadier.arguments.StringArgumentType.string;
|
||||
import static land.chipmunk.chipmunkmod.command.CommandManager.argument;
|
||||
import static land.chipmunk.chipmunkmod.command.CommandManager.literal;
|
||||
|
||||
public class AutoSkinCommand {
|
||||
public static void register(CommandDispatcher<FabricClientCommandSource> dispatcher) {
|
||||
dispatcher.register(
|
||||
literal("autoskin")
|
||||
.then(argument("username", string())
|
||||
.executes(AutoSkinCommand::execute)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public static int execute(CommandContext<FabricClientCommandSource> context) {
|
||||
String username = getString(context, "username");
|
||||
SelfCare selfCare = SelfCare.INSTANCE;
|
||||
|
||||
selfCare.skin = username;
|
||||
|
||||
if ("off".equals(username)) {
|
||||
ChatUtils.infoPrefix("Auto Skin", "Auto skin is now [hl]off");
|
||||
} else {
|
||||
selfCare.hasSkin = false;
|
||||
ChatUtils.infoPrefix("Auto Skin", "Set your auto skin username to [hl]%s", username);
|
||||
}
|
||||
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
package land.chipmunk.chipmunkmod.command.commands;
|
||||
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import static land.chipmunk.chipmunkmod.command.CommandManager.literal;
|
||||
import static land.chipmunk.chipmunkmod.command.CommandManager.argument;
|
||||
import static com.mojang.brigadier.arguments.LongArgumentType.longArg;
|
||||
import static com.mojang.brigadier.arguments.LongArgumentType.getLong;
|
||||
import static com.mojang.brigadier.arguments.StringArgumentType.greedyString;
|
||||
import static com.mojang.brigadier.arguments.StringArgumentType.getString;
|
||||
import static com.mojang.brigadier.arguments.IntegerArgumentType.integer;
|
||||
import static com.mojang.brigadier.arguments.IntegerArgumentType.getInteger;
|
||||
|
||||
import land.chipmunk.chipmunkmod.util.player.ChatUtils;
|
||||
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
|
||||
import net.minecraft.text.Text;
|
||||
import land.chipmunk.chipmunkmod.modules.CommandLoopManager;
|
||||
import java.util.List;
|
||||
|
||||
public class CloopCommand {
|
||||
public static void register (CommandDispatcher<FabricClientCommandSource> dispatcher) {
|
||||
dispatcher.register(
|
||||
literal("cloop")
|
||||
.then(
|
||||
literal("add")
|
||||
.then(
|
||||
argument("interval", longArg())
|
||||
.then(
|
||||
argument("command", greedyString())
|
||||
.executes(c -> addCloop(c))
|
||||
)
|
||||
)
|
||||
)
|
||||
.then(
|
||||
literal("remove")
|
||||
.then(
|
||||
argument("id", integer())
|
||||
.executes(c -> removeCloop(c))
|
||||
)
|
||||
)
|
||||
.then(
|
||||
literal("clear")
|
||||
.executes(c -> clearCloops(c))
|
||||
)
|
||||
.then(
|
||||
literal("list")
|
||||
.executes(c -> listCloops(c))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public static int addCloop (CommandContext<FabricClientCommandSource> context) {
|
||||
final FabricClientCommandSource source = context.getSource();
|
||||
final long interval = getLong(context, "interval");
|
||||
final String command = getString(context, "command");
|
||||
|
||||
int id = CommandLoopManager.INSTANCE.loopCommand(command, interval);
|
||||
|
||||
ChatUtils.infoPrefix("CLOOP", "Cloop [hl]\"%s\"[df] created with ID [hl]%s", command, id);
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
|
||||
public static int removeCloop (CommandContext<FabricClientCommandSource> context) {
|
||||
final FabricClientCommandSource source = context.getSource();
|
||||
final CommandLoopManager manager = CommandLoopManager.INSTANCE;
|
||||
final int id = getInteger(context, "id");
|
||||
|
||||
if (id < 0 || id >= manager.commandLoops.size()) {
|
||||
ChatUtils.infoPrefix("CLOOP", "Invalid cloop ID [hl]%s", id);
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
|
||||
manager.removeAndStop(id);
|
||||
|
||||
ChatUtils.infoPrefix("CLOOP", "Cloop with ID [hl]%s[def] removed", id);
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
|
||||
public static int clearCloops (CommandContext<FabricClientCommandSource> context) {
|
||||
final FabricClientCommandSource source = context.getSource();
|
||||
final CommandLoopManager manager = CommandLoopManager.INSTANCE;
|
||||
|
||||
manager.clearLoops();
|
||||
|
||||
ChatUtils.infoPrefix("CLOOP", "Cleared all cloops");
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
|
||||
public static int listCloops(CommandContext<FabricClientCommandSource> context) {
|
||||
final FabricClientCommandSource source = context.getSource();
|
||||
final List<CommandLoopManager.CommandLoop> loops = CommandLoopManager.INSTANCE.commandLoops;
|
||||
|
||||
if (loops == null || loops.isEmpty()) {
|
||||
ChatUtils.infoPrefix("CLOOP", "No cloops currently running!");
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
|
||||
ChatUtils.infoPrefix("CLOOP", "Cloops List:");
|
||||
|
||||
int id = 0; // Start indexing at 0
|
||||
for (CommandLoopManager.CommandLoop loop : loops) {
|
||||
ChatUtils.info("[hl]%d [def]› cmd: [hl]'%s'[def], int: [hl]%d[def]ms", id, loop.command, loop.interval);
|
||||
id++;
|
||||
}
|
||||
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
package land.chipmunk.chipmunkmod.command.commands;
|
||||
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
|
||||
import static com.mojang.brigadier.arguments.BoolArgumentType.bool;
|
||||
import static com.mojang.brigadier.arguments.BoolArgumentType.getBool;
|
||||
import static land.chipmunk.chipmunkmod.command.CommandManager.literal;
|
||||
import static land.chipmunk.chipmunkmod.command.CommandManager.argument;
|
||||
import static com.mojang.brigadier.arguments.StringArgumentType.greedyString;
|
||||
import static com.mojang.brigadier.arguments.StringArgumentType.getString;
|
||||
|
||||
import land.chipmunk.chipmunkmod.util.misc.TextUtil;
|
||||
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import land.chipmunk.chipmunkmod.modules.CommandCore;
|
||||
|
||||
public class CoreCommand {
|
||||
public static void register (CommandDispatcher<FabricClientCommandSource> dispatcher) {
|
||||
dispatcher.register(
|
||||
literal("core")
|
||||
.then(
|
||||
literal("run")
|
||||
.then(
|
||||
argument("command", greedyString())
|
||||
.executes(c -> run(c))
|
||||
)
|
||||
)
|
||||
|
||||
.then(
|
||||
literal("runTracked")
|
||||
.then(
|
||||
argument("command", greedyString())
|
||||
.executes(c -> runTracked(c))
|
||||
)
|
||||
)
|
||||
|
||||
.then(literal("refill").executes(c -> refill(c)))
|
||||
.then(literal("move").executes(c -> move(c)))
|
||||
|
||||
.then(
|
||||
literal("runFillCommand")
|
||||
.then(
|
||||
argument("enabled", bool())
|
||||
.executes(c -> runFillCommand(c))
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public static int run (CommandContext<FabricClientCommandSource> context) {
|
||||
CommandCore.INSTANCE.run(getString(context, "command"));
|
||||
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
|
||||
public static int runTracked (CommandContext<FabricClientCommandSource> context) {
|
||||
final FabricClientCommandSource source = context.getSource();
|
||||
|
||||
final String command = getString(context, "command");
|
||||
|
||||
final CompletableFuture<NbtCompound> future = CommandCore.INSTANCE.runTracked(command);
|
||||
future.thenApply(tag -> {
|
||||
try {
|
||||
final String output = tag.getString("LastOutput");
|
||||
if (output != null) source.sendFeedback(TextUtil.fromJson(output));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return tag;
|
||||
});
|
||||
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
|
||||
public static int refill (CommandContext<FabricClientCommandSource> context) {
|
||||
CommandCore.INSTANCE.refill();
|
||||
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
|
||||
public static int move (CommandContext<FabricClientCommandSource> context) {
|
||||
final FabricClientCommandSource source = context.getSource();
|
||||
|
||||
CommandCore.INSTANCE.move(source.getClient().player.getPos());
|
||||
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
|
||||
public static int runFillCommand(CommandContext<FabricClientCommandSource> context) {
|
||||
final FabricClientCommandSource source = context.getSource();
|
||||
|
||||
final boolean bool = getBool(context, "enabled");
|
||||
|
||||
CommandCore.INSTANCE.runFillCommand = bool;
|
||||
|
||||
source.sendFeedback(Text.literal("Running fill commands are now " + (bool ? "on" : "off")));
|
||||
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package land.chipmunk.chipmunkmod.command.commands;
|
||||
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import land.chipmunk.chipmunkmod.modules.CustomChat;
|
||||
import land.chipmunk.chipmunkmod.util.player.ChatUtils;
|
||||
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
import static com.mojang.brigadier.arguments.BoolArgumentType.bool;
|
||||
import static com.mojang.brigadier.arguments.BoolArgumentType.getBool;
|
||||
import static com.mojang.brigadier.arguments.StringArgumentType.getString;
|
||||
import static com.mojang.brigadier.arguments.StringArgumentType.greedyString;
|
||||
import static land.chipmunk.chipmunkmod.command.CommandManager.argument;
|
||||
import static land.chipmunk.chipmunkmod.command.CommandManager.literal;
|
||||
|
||||
public class CustomChatCommand {
|
||||
public static void register (CommandDispatcher<FabricClientCommandSource> dispatcher) {
|
||||
dispatcher.register(
|
||||
literal("customchat")
|
||||
.then(
|
||||
literal("enabled")
|
||||
.then(
|
||||
argument("boolean", bool())
|
||||
.executes(CustomChatCommand::enabled)
|
||||
)
|
||||
)
|
||||
.then(
|
||||
literal("format")
|
||||
.then(
|
||||
argument("format", greedyString())
|
||||
.executes(CustomChatCommand::setFormat)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public static int enabled (CommandContext<FabricClientCommandSource> context) {
|
||||
final FabricClientCommandSource source = context.getSource();
|
||||
final boolean bool = getBool(context, "boolean");
|
||||
CustomChat.INSTANCE.enabled = bool;
|
||||
|
||||
ChatUtils.infoPrefix("CHAT", "Custom chat is now [hl]%s", bool ? "on" : "off");
|
||||
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
|
||||
public static int setFormat (CommandContext<FabricClientCommandSource> context) {
|
||||
final FabricClientCommandSource source = context.getSource();
|
||||
final String format = getString(context, "format");
|
||||
CustomChat.INSTANCE.format = format;
|
||||
|
||||
ChatUtils.infoPrefix("CHAT", "Format for custom chat set to [hl]%s", format);
|
||||
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package land.chipmunk.chipmunkmod.command.commands;
|
||||
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import land.chipmunk.chipmunkmod.util.player.ChatUtils;
|
||||
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Formatting;
|
||||
import org.luaj.vm2.Globals;
|
||||
import org.luaj.vm2.LuaValue;
|
||||
import org.luaj.vm2.lib.jse.CoerceJavaToLua;
|
||||
import org.luaj.vm2.lib.jse.JsePlatform;
|
||||
|
||||
import static com.mojang.brigadier.arguments.StringArgumentType.getString;
|
||||
import static com.mojang.brigadier.arguments.StringArgumentType.greedyString;
|
||||
import static land.chipmunk.chipmunkmod.command.CommandManager.argument;
|
||||
import static land.chipmunk.chipmunkmod.command.CommandManager.literal;
|
||||
|
||||
public class EvalCommand {
|
||||
public static void register (CommandDispatcher<FabricClientCommandSource> dispatcher) {
|
||||
dispatcher.register(
|
||||
literal("eval")
|
||||
.then(
|
||||
argument("code", greedyString())
|
||||
.executes(EvalCommand::eval)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public static int eval (CommandContext<FabricClientCommandSource> context) {
|
||||
final String code = getString(context, "code");
|
||||
|
||||
try {
|
||||
final Globals globals = JsePlatform.standardGlobals();
|
||||
|
||||
globals.set("client", CoerceJavaToLua.coerce(MinecraftClient.getInstance()));
|
||||
globals.set("context", CoerceJavaToLua.coerce(context));
|
||||
globals.set("class", CoerceJavaToLua.coerce(Class.class));
|
||||
|
||||
LuaValue chunk = globals.load(code);
|
||||
|
||||
ChatUtils.infoPrefix("Eval", "%s", chunk.call().toString());
|
||||
} catch (Exception e) {
|
||||
ChatUtils.infoPrefix("Eval", "Error: [hl]%s", e.toString());
|
||||
}
|
||||
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
package land.chipmunk.chipmunkmod.command.commands;
|
||||
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import land.chipmunk.chipmunkmod.util.player.ChatUtils;
|
||||
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.command.CommandRegistryAccess;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.network.packet.c2s.play.CreativeInventoryActionC2SPacket;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import static land.chipmunk.chipmunkmod.command.CommandManager.literal;
|
||||
import static land.chipmunk.chipmunkmod.command.CommandManager.argument;
|
||||
import static com.mojang.brigadier.arguments.IntegerArgumentType.integer;
|
||||
import static com.mojang.brigadier.arguments.IntegerArgumentType.getInteger;
|
||||
import static net.minecraft.command.argument.ItemStackArgumentType.itemStack;
|
||||
import static net.minecraft.command.argument.ItemStackArgumentType.getItemStackArgument;
|
||||
|
||||
public class ItemCommand {
|
||||
|
||||
public static void register(CommandDispatcher<FabricClientCommandSource> dispatcher, CommandRegistryAccess commandRegistryAccess) {
|
||||
dispatcher.register(
|
||||
literal("item")
|
||||
.then(argument("item", itemStack(commandRegistryAccess))
|
||||
.executes(context -> setItem(context, 1)) // Default count to 1 if unspecified
|
||||
.then(argument("count", integer(1, 512))
|
||||
.executes(ItemCommand::setItem)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public static int setItem(CommandContext<FabricClientCommandSource> context) throws CommandSyntaxException {
|
||||
int count = getInteger(context, "count");
|
||||
return setItem(context, count);
|
||||
}
|
||||
|
||||
public static int setItem(CommandContext<FabricClientCommandSource> context, int count) throws CommandSyntaxException {
|
||||
FabricClientCommandSource source = context.getSource();
|
||||
MinecraftClient client = source.getClient();
|
||||
|
||||
ItemStack stack = getItemStackArgument(context, "item").createStack(count, false);
|
||||
assert client.player != null;
|
||||
int slot = 36 + client.player.getInventory().selectedSlot; // Get player's currently selected hotbar slot
|
||||
|
||||
// Send packet to server to update the inventory with the new item stack
|
||||
Objects.requireNonNull(client.getNetworkHandler()).getConnection().send(new CreativeInventoryActionC2SPacket(slot, stack));
|
||||
|
||||
ChatUtils.infoPrefix("Item", "Replaced your held item with [hl]%s %s", count, stack.toHoverableText());
|
||||
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package land.chipmunk.chipmunkmod.command.commands;
|
||||
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
|
||||
import land.chipmunk.chipmunkmod.modules.CommandCore;
|
||||
import land.chipmunk.chipmunkmod.util.player.ChatUtils;
|
||||
|
||||
import static land.chipmunk.chipmunkmod.command.CommandManager.literal;
|
||||
import static land.chipmunk.chipmunkmod.command.CommandManager.argument;
|
||||
import static com.mojang.brigadier.arguments.StringArgumentType.word;
|
||||
import static com.mojang.brigadier.arguments.StringArgumentType.getString;
|
||||
|
||||
public class KickCommand {
|
||||
private static final String PAYLOAD_LONGSTRING = "Hi\u00a7k" + "猫".repeat(31000) + "\u00a7r:>";
|
||||
private static final String PAYLOAD_THREADDESTROYER = "Payload";
|
||||
|
||||
public static void register(CommandDispatcher<FabricClientCommandSource> dispatcher) {
|
||||
dispatcher.register(
|
||||
literal("kick")
|
||||
.then(literal("longstring")
|
||||
.then(argument("player", word())
|
||||
.executes(context -> executeKick(context, "longstring"))
|
||||
)
|
||||
)
|
||||
// .then(literal("thread")
|
||||
// .then(argument("player", word())
|
||||
// .executes(context -> executeKick(context, "thread"))
|
||||
// )
|
||||
// )
|
||||
);
|
||||
}
|
||||
|
||||
public static int executeKick(CommandContext<FabricClientCommandSource> context, String method) {
|
||||
String player = getString(context, "player");
|
||||
|
||||
switch (method) {
|
||||
case "longstring" -> {
|
||||
executeLongString(player);
|
||||
ChatUtils.infoPrefix("Kick", "Executing [hl]Long String[def] kick on [hl]%s", player);
|
||||
}
|
||||
}
|
||||
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
|
||||
private static void executeLongString(String player) {
|
||||
CommandCore.INSTANCE.run("/title " + player + " title \"" + PAYLOAD_LONGSTRING + "\"");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,306 @@
|
|||
package land.chipmunk.chipmunkmod.command.commands;
|
||||
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
|
||||
import land.chipmunk.chipmunkmod.command.CommandManager;
|
||||
import land.chipmunk.chipmunkmod.modules.SongPlayer;
|
||||
import land.chipmunk.chipmunkmod.song.Song;
|
||||
import land.chipmunk.chipmunkmod.util.player.ChatUtils;
|
||||
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.JoinConfiguration;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.event.HoverEvent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Formatting;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.mojang.brigadier.arguments.BoolArgumentType.bool;
|
||||
import static com.mojang.brigadier.arguments.BoolArgumentType.getBool;
|
||||
import static com.mojang.brigadier.arguments.FloatArgumentType.floatArg;
|
||||
import static com.mojang.brigadier.arguments.FloatArgumentType.getFloat;
|
||||
import static com.mojang.brigadier.arguments.IntegerArgumentType.getInteger;
|
||||
import static com.mojang.brigadier.arguments.IntegerArgumentType.integer;
|
||||
import static com.mojang.brigadier.arguments.LongArgumentType.getLong;
|
||||
import static land.chipmunk.chipmunkmod.command.CommandManager.argument;
|
||||
import static land.chipmunk.chipmunkmod.command.CommandManager.literal;
|
||||
import static land.chipmunk.chipmunkmod.command.arguments.LocationArgumentType.*;
|
||||
import static land.chipmunk.chipmunkmod.command.arguments.TimestampArgumentType.timestamp;
|
||||
|
||||
public class MusicCommand {
|
||||
private static final SimpleCommandExceptionType NO_SONG_IS_CURRENTLY_PLAYING = new SimpleCommandExceptionType(Text.translatable("No song is currently playing"));
|
||||
private static final SimpleCommandExceptionType OOB_TIMESTAMP = new SimpleCommandExceptionType(Text.translatable("Invalid timestamp for the current song"));
|
||||
private static final SimpleCommandExceptionType DIRECTORY_DOES_NOT_EXIST = new SimpleCommandExceptionType(Text.translatable("The specified directory does not exist"));
|
||||
|
||||
public static void register (CommandDispatcher<FabricClientCommandSource> dispatcher) {
|
||||
final MusicCommand instance = new MusicCommand();
|
||||
|
||||
Path root = Path.of(SongPlayer.SONG_DIR.getPath());
|
||||
|
||||
dispatcher.register(
|
||||
literal("music")
|
||||
.then(
|
||||
literal("play")
|
||||
.then(
|
||||
argument("location", location(root))
|
||||
.executes(instance::play)
|
||||
)
|
||||
)
|
||||
|
||||
.then(literal("stop").executes(instance::stop))
|
||||
.then(literal("skip").executes(instance::skip))
|
||||
.then(literal("pause").executes(instance::pause))
|
||||
|
||||
.then(
|
||||
literal("list")
|
||||
.executes(c -> instance.list(root))
|
||||
.then(
|
||||
argument("location", filepath(root))
|
||||
.executes(c -> instance.list(getPath(c, "location")))
|
||||
)
|
||||
)
|
||||
|
||||
.then(
|
||||
literal("loop")
|
||||
.executes(instance::toggleLoop)
|
||||
.then(
|
||||
argument("count", integer())
|
||||
.executes(instance::loop)
|
||||
)
|
||||
)
|
||||
|
||||
.then(
|
||||
literal("goto")
|
||||
.then(
|
||||
argument("timestamp", timestamp())
|
||||
.executes(instance::gotoCommand)
|
||||
)
|
||||
)
|
||||
|
||||
.then(
|
||||
literal("useCore")
|
||||
.then(
|
||||
argument("boolean", bool())
|
||||
.executes(instance::useCore)
|
||||
)
|
||||
)
|
||||
|
||||
.then(
|
||||
literal("actionbar")
|
||||
.then(
|
||||
argument("boolean", bool())
|
||||
.executes(instance::actionbar)
|
||||
)
|
||||
)
|
||||
|
||||
.then(
|
||||
literal("pitch")
|
||||
.then(
|
||||
argument("pitch", floatArg())
|
||||
.executes(instance::pitch)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public int play (CommandContext<FabricClientCommandSource> context) {
|
||||
final SongPlayer songPlayer = SongPlayer.INSTANCE;
|
||||
|
||||
final Path path = getPath(context, "location");
|
||||
|
||||
if (path != null) songPlayer.loadSong(path);
|
||||
else songPlayer.loadSong(getUrl(context, "location"));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
public int stop (CommandContext<FabricClientCommandSource> context) throws CommandSyntaxException {
|
||||
final SongPlayer songPlayer = SongPlayer.INSTANCE;
|
||||
|
||||
if (songPlayer.currentSong == null) throw NO_SONG_IS_CURRENTLY_PLAYING.create();
|
||||
|
||||
songPlayer.stopPlaying();
|
||||
songPlayer.songQueue.clear();
|
||||
|
||||
ChatUtils.infoPrefix("Music", "[hl]Stopped[def] music playback");
|
||||
return 1;
|
||||
}
|
||||
|
||||
public int skip (CommandContext<FabricClientCommandSource> context) throws CommandSyntaxException {
|
||||
final FabricClientCommandSource source = context.getSource();
|
||||
final SongPlayer songPlayer = SongPlayer.INSTANCE;
|
||||
|
||||
if (songPlayer.currentSong == null) throw NO_SONG_IS_CURRENTLY_PLAYING.create();
|
||||
|
||||
songPlayer.stopPlaying();
|
||||
|
||||
ChatUtils.infoPrefix("Music", "Skipped current song");
|
||||
return 1;
|
||||
}
|
||||
|
||||
public int pause (CommandContext<FabricClientCommandSource> context) throws CommandSyntaxException {
|
||||
final SongPlayer songPlayer = SongPlayer.INSTANCE;
|
||||
final Song currentSong = songPlayer.currentSong;
|
||||
|
||||
if (currentSong == null) throw NO_SONG_IS_CURRENTLY_PLAYING.create();
|
||||
|
||||
if (!currentSong.paused) {
|
||||
currentSong.pause();
|
||||
ChatUtils.infoPrefix("Music", "[hl]Paused[def] current song");
|
||||
} else {
|
||||
currentSong.play();
|
||||
ChatUtils.infoPrefix("Music", "[hl]Unpaused[def] current song");
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
public int list (Path path) throws CommandSyntaxException {
|
||||
final CommandManager commandManager = CommandManager.INSTANCE;
|
||||
|
||||
final String prefix = commandManager.prefix;
|
||||
|
||||
final File directory = path.toFile();
|
||||
final String[] filenames = directory.list();
|
||||
if (filenames == null) throw DIRECTORY_DOES_NOT_EXIST.create();
|
||||
|
||||
final Path root = Path.of(SongPlayer.SONG_DIR.getAbsoluteFile().getPath()).toAbsolutePath();
|
||||
String relativePath;
|
||||
if (path.getNameCount() - root.getNameCount() > 0) relativePath = path.subpath(root.getNameCount(), path.getNameCount()).toString();
|
||||
else relativePath = "";
|
||||
|
||||
final List<Component> directories = new ArrayList<>();
|
||||
final List<Component> files = new ArrayList<>();
|
||||
int i = 0;
|
||||
|
||||
for (String filename : filenames) {
|
||||
final File file = new File(directory, filename);
|
||||
if (!file.isDirectory()) continue;
|
||||
|
||||
final NamedTextColor color = (i++ & 1) == 0 ? NamedTextColor.DARK_GREEN : NamedTextColor.GREEN;
|
||||
|
||||
final Path relativeFilepath = Path.of(relativePath, filename);
|
||||
final String escapedPath = escapePath(relativeFilepath.toString());
|
||||
|
||||
directories.add(
|
||||
Component.text(filename + "/", color)
|
||||
.clickEvent(ClickEvent.suggestCommand(prefix + "music list " + escapedPath))
|
||||
.hoverEvent(HoverEvent.showText(Component.translatable("Click to list %s", Component.text(filename))))
|
||||
);
|
||||
}
|
||||
|
||||
for (String filename : filenames) {
|
||||
final File file = new File(directory, filename);
|
||||
if (file.isDirectory()) continue;
|
||||
|
||||
final NamedTextColor color = (i++ & 1) == 0 ? NamedTextColor.DARK_GREEN : NamedTextColor.GREEN;
|
||||
|
||||
final Path relativeFilepath = Path.of(relativePath, filename);
|
||||
final String escapedPath = escapePath(relativeFilepath.toString());
|
||||
|
||||
files.add(
|
||||
Component.text(filename, color)
|
||||
.clickEvent(ClickEvent.suggestCommand(prefix + "music play " + escapedPath))
|
||||
.hoverEvent(HoverEvent.showText(Component.translatable("Click to play %s", Component.text(filename))))
|
||||
);
|
||||
}
|
||||
|
||||
final ArrayList<Component> mergedList = new ArrayList<>();
|
||||
mergedList.addAll(directories);
|
||||
mergedList.addAll(files);
|
||||
final Component component = Component.translatable("Songs - %s", Component.join(JoinConfiguration.separator(Component.space()), mergedList)).color(NamedTextColor.GREEN);
|
||||
|
||||
((Audience) MinecraftClient.getInstance().player).sendMessage(component);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// TODO: Move this into some utility class, as it is more related to brigadier strings in general than to the list command in specific
|
||||
private String escapePath (String path) {
|
||||
final StringBuilder sb = new StringBuilder("'");
|
||||
|
||||
for (char character : path.toCharArray()) {
|
||||
if (character == '\'' || character == '\\') sb.append('\\');
|
||||
sb.append(character);
|
||||
}
|
||||
|
||||
sb.append("'");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public int toggleLoop (CommandContext<FabricClientCommandSource> context) throws CommandSyntaxException {
|
||||
final SongPlayer songPlayer = SongPlayer.INSTANCE;
|
||||
final Song currentSong = songPlayer.currentSong;
|
||||
|
||||
if (currentSong == null) throw NO_SONG_IS_CURRENTLY_PLAYING.create();
|
||||
|
||||
currentSong.looping = !currentSong.looping;
|
||||
|
||||
ChatUtils.infoPrefix("Music", "Looping is now [hl]" + (currentSong.looping ? "on" : "off"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
public int loop (CommandContext<FabricClientCommandSource> context) throws CommandSyntaxException {
|
||||
final SongPlayer songPlayer = SongPlayer.INSTANCE;
|
||||
final Song currentSong = songPlayer.currentSong;
|
||||
final int count = getInteger(context, "count");
|
||||
|
||||
if (currentSong == null) throw NO_SONG_IS_CURRENTLY_PLAYING.create();
|
||||
|
||||
currentSong.looping = true;
|
||||
currentSong.loopCount = count;
|
||||
|
||||
ChatUtils.infoPrefix("Music", "Enabled loop for [hl]%s[def] times", String.valueOf(count));
|
||||
return 1;
|
||||
}
|
||||
|
||||
public int gotoCommand (CommandContext<FabricClientCommandSource> context) throws CommandSyntaxException {
|
||||
final SongPlayer songPlayer = SongPlayer.INSTANCE;
|
||||
final Song currentSong = songPlayer.currentSong;
|
||||
final long millis = getLong(context, "timestamp");
|
||||
|
||||
if (currentSong == null) throw NO_SONG_IS_CURRENTLY_PLAYING.create();
|
||||
if (millis < 0 || millis > currentSong.length) throw OOB_TIMESTAMP.create();
|
||||
|
||||
currentSong.setTime(millis);
|
||||
|
||||
ChatUtils.infoPrefix("Music", "Song time stamp now set to [hl]%s[def].", songPlayer.formatTime(millis));
|
||||
return 1;
|
||||
}
|
||||
|
||||
public int useCore (CommandContext<FabricClientCommandSource> context) {
|
||||
final boolean enabled = getBool(context, "boolean");
|
||||
|
||||
SongPlayer.INSTANCE.useCore = enabled;
|
||||
|
||||
ChatUtils.infoPrefix("Music", "Playing music with [hl]core[def] is now " + (enabled ? "on" : "off"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
public int actionbar (CommandContext<FabricClientCommandSource> context) {
|
||||
final boolean enabled = getBool(context, "boolean");
|
||||
|
||||
SongPlayer.INSTANCE.actionbar = enabled;
|
||||
|
||||
ChatUtils.infoPrefix("Music", "Actionbar is now [hl]" + (enabled ? "on" : "off"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
public int pitch (CommandContext<FabricClientCommandSource> context) {
|
||||
final float pitch = getFloat(context, "pitch");
|
||||
|
||||
SongPlayer.INSTANCE.pitch = pitch;
|
||||
|
||||
ChatUtils.infoPrefix("Music", "Set the pitch to [hl]%s[def].", pitch);
|
||||
return 1;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package land.chipmunk.chipmunkmod.command.commands;
|
||||
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import land.chipmunk.chipmunkmod.modules.RainbowName;
|
||||
import land.chipmunk.chipmunkmod.util.player.ChatUtils;
|
||||
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
import static com.mojang.brigadier.arguments.BoolArgumentType.bool;
|
||||
import static com.mojang.brigadier.arguments.BoolArgumentType.getBool;
|
||||
import static com.mojang.brigadier.arguments.StringArgumentType.getString;
|
||||
import static com.mojang.brigadier.arguments.StringArgumentType.greedyString;
|
||||
import static land.chipmunk.chipmunkmod.command.CommandManager.argument;
|
||||
import static land.chipmunk.chipmunkmod.command.CommandManager.literal;
|
||||
|
||||
public class RainbowNameCommand {
|
||||
public static void register(CommandDispatcher<FabricClientCommandSource> dispatcher) {
|
||||
dispatcher.register(
|
||||
literal("rainbowname")
|
||||
.then(literal("enabled")
|
||||
.then(argument("boolean", bool())
|
||||
.executes(RainbowNameCommand::toggleRainbowName)
|
||||
)
|
||||
)
|
||||
.then(literal("setName")
|
||||
.then(argument("name", greedyString())
|
||||
.executes(RainbowNameCommand::setDisplayName)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public static int toggleRainbowName(CommandContext<FabricClientCommandSource> context) {
|
||||
boolean isEnabled = getBool(context, "boolean");
|
||||
|
||||
if (isEnabled) {
|
||||
RainbowName.INSTANCE.enable();
|
||||
ChatUtils.infoPrefix("Rainbow Name", "Rainbow name is now [hl]on");
|
||||
} else {
|
||||
RainbowName.INSTANCE.disable();
|
||||
ChatUtils.infoPrefix("Rainbow Name", "Rainbow name is now [hl]off");
|
||||
}
|
||||
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
|
||||
public static int setDisplayName(CommandContext<FabricClientCommandSource> context) {
|
||||
FabricClientCommandSource source = context.getSource();
|
||||
String name = getString(context, "name");
|
||||
|
||||
RainbowName.INSTANCE.displayName = name;
|
||||
ChatUtils.infoPrefix("Rainbow Name", "Name set to [hl]%s", name);
|
||||
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package land.chipmunk.chipmunkmod.command.commands;
|
||||
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import land.chipmunk.chipmunkmod.ChipmunkMod;
|
||||
import land.chipmunk.chipmunkmod.modules.CommandCore;
|
||||
import land.chipmunk.chipmunkmod.modules.CustomChat;
|
||||
import land.chipmunk.chipmunkmod.util.player.ChatUtils;
|
||||
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static land.chipmunk.chipmunkmod.command.CommandManager.literal;
|
||||
|
||||
public class ReloadConfigCommand {
|
||||
public static void register (CommandDispatcher<FabricClientCommandSource> dispatcher) {
|
||||
dispatcher.register(
|
||||
literal("reloadconfig")
|
||||
.executes(ReloadConfigCommand::reload)
|
||||
);
|
||||
}
|
||||
|
||||
public static int reload(CommandContext<FabricClientCommandSource> context) {
|
||||
final FabricClientCommandSource source = context.getSource();
|
||||
|
||||
try {
|
||||
ChipmunkMod.CONFIG = ChipmunkMod.loadConfig();
|
||||
|
||||
CustomChat.INSTANCE.reloadFormat();
|
||||
CommandCore.INSTANCE.reloadRelativeArea();
|
||||
|
||||
ChatUtils.info("Successfully reloaded the config");
|
||||
} catch (IOException e) {
|
||||
ChatUtils.error("Could not load the config, see logs for stacktrace");
|
||||
ChatUtils.warning("Error: [hl]%s", e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package land.chipmunk.chipmunkmod.command.commands;
|
||||
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import land.chipmunk.chipmunkmod.modules.Chat;
|
||||
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
|
||||
|
||||
import static com.mojang.brigadier.arguments.StringArgumentType.getString;
|
||||
import static com.mojang.brigadier.arguments.StringArgumentType.greedyString;
|
||||
import static land.chipmunk.chipmunkmod.command.CommandManager.argument;
|
||||
import static land.chipmunk.chipmunkmod.command.CommandManager.literal;
|
||||
|
||||
public class SayCommand {
|
||||
public static void register(CommandDispatcher<FabricClientCommandSource> dispatcher) {
|
||||
dispatcher.register(
|
||||
literal("say")
|
||||
.then(argument("message", greedyString())
|
||||
.executes(SayCommand::say)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public static int say(CommandContext<FabricClientCommandSource> context) {
|
||||
String message = getString(context, "message");
|
||||
Chat.sendChatMessage(message, true);
|
||||
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
package land.chipmunk.chipmunkmod.command.commands;
|
||||
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import land.chipmunk.chipmunkmod.modules.SelfCare;
|
||||
import land.chipmunk.chipmunkmod.util.player.ChatUtils;
|
||||
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
import static com.mojang.brigadier.arguments.BoolArgumentType.bool;
|
||||
import static com.mojang.brigadier.arguments.BoolArgumentType.getBool;
|
||||
import static land.chipmunk.chipmunkmod.command.CommandManager.argument;
|
||||
import static land.chipmunk.chipmunkmod.command.CommandManager.literal;
|
||||
|
||||
public class SelfCareCommand {
|
||||
public static void register(CommandDispatcher<FabricClientCommandSource> dispatcher) {
|
||||
dispatcher.register(
|
||||
literal("selfcare")
|
||||
.then(createCommand("op"))
|
||||
.then(createCommand("gamemode"))
|
||||
.then(createCommand("cspy"))
|
||||
.then(createCommand("icu"))
|
||||
);
|
||||
}
|
||||
|
||||
private static LiteralArgumentBuilder<FabricClientCommandSource> createCommand(String settingType) {
|
||||
return literal(settingType)
|
||||
.then(argument("enabled", bool())
|
||||
.executes(context -> updateSelfCareSetting(context, settingType))
|
||||
);
|
||||
}
|
||||
|
||||
public static int updateSelfCareSetting(CommandContext<FabricClientCommandSource> context, String settingType) {
|
||||
boolean isEnabled = getBool(context, "enabled");
|
||||
|
||||
switch (settingType) {
|
||||
case "op" -> {
|
||||
SelfCare.INSTANCE.opEnabled = isEnabled;
|
||||
sendFeedback("op", isEnabled);
|
||||
}
|
||||
case "gamemode" -> {
|
||||
SelfCare.INSTANCE.gamemodeEnabled = isEnabled;
|
||||
sendFeedback("gamemode", isEnabled);
|
||||
}
|
||||
case "cspy" -> {
|
||||
SelfCare.INSTANCE.cspyEnabled = isEnabled;
|
||||
sendFeedback("CommandSpy", isEnabled);
|
||||
}
|
||||
case "icu" -> {
|
||||
SelfCare.INSTANCE.icuEnabled = isEnabled;
|
||||
sendFeedback("iControlU", isEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
|
||||
private static void sendFeedback(String feature, boolean isEnabled) {
|
||||
ChatUtils.infoPrefix("Self Care", "The [hl]%s[def] self-care is now [hl]%s[def]", feature, isEnabled ? "on" : "off");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package land.chipmunk.chipmunkmod.command.commands;
|
||||
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import static land.chipmunk.chipmunkmod.command.CommandManager.literal;
|
||||
|
||||
import land.chipmunk.chipmunkmod.util.player.ChatUtils;
|
||||
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
|
||||
|
||||
public class TestCommand {
|
||||
public static void register(CommandDispatcher<FabricClientCommandSource> dispatcher) {
|
||||
dispatcher.register(
|
||||
literal("test")
|
||||
.executes(TestCommand::helloWorld)
|
||||
);
|
||||
}
|
||||
|
||||
public static int helloWorld(CommandContext<FabricClientCommandSource> context) {
|
||||
displayMessages();
|
||||
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
|
||||
private static void displayMessages() {
|
||||
ChatUtils.infoPrefix("TEST", "Hello world!");
|
||||
ChatUtils.info("Hello world!");
|
||||
ChatUtils.warningPrefix("TEST", "Hello world!");
|
||||
ChatUtils.warning("Hello world!");
|
||||
ChatUtils.errorPrefix("TEST", "Hello world!");
|
||||
ChatUtils.error("Hello world!");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
package land.chipmunk.chipmunkmod.command.commands;
|
||||
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import static com.mojang.brigadier.arguments.StringArgumentType.greedyString;
|
||||
import static com.mojang.brigadier.arguments.StringArgumentType.getString;
|
||||
import static land.chipmunk.chipmunkmod.command.CommandManager.literal;
|
||||
import static land.chipmunk.chipmunkmod.command.CommandManager.argument;
|
||||
|
||||
import land.chipmunk.chipmunkmod.util.player.ChatUtils;
|
||||
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.screen.TitleScreen;
|
||||
import net.minecraft.client.gui.screen.multiplayer.ConnectScreen;
|
||||
import net.minecraft.client.network.ServerInfo;
|
||||
import net.minecraft.client.network.ServerAddress;
|
||||
import net.minecraft.client.session.Session;
|
||||
import net.minecraft.text.Text;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import land.chipmunk.chipmunkmod.mixin.MinecraftClientAccessor;
|
||||
|
||||
public class UsernameCommand {
|
||||
private static final Session ORIGINAL_SESSION = ((MinecraftClientAccessor) MinecraftClient.getInstance()).session();
|
||||
|
||||
public static void register(CommandDispatcher<FabricClientCommandSource> dispatcher) {
|
||||
dispatcher.register(
|
||||
literal("username")
|
||||
.then(literal("set")
|
||||
.then(argument("username", greedyString())
|
||||
.executes(UsernameCommand::updateUsername)))
|
||||
.then(literal("revert")
|
||||
.executes(context -> updateSession(context, ORIGINAL_SESSION)))
|
||||
);
|
||||
}
|
||||
|
||||
public static int updateUsername(CommandContext<FabricClientCommandSource> context) {
|
||||
String username = getString(context, "username");
|
||||
|
||||
if (username.length() > 16) {
|
||||
ChatUtils.infoPrefix("Username", "Invalid username length!");
|
||||
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
|
||||
Session session = new Session(username, new UUID(0L, 0L), "", Optional.empty(), Optional.empty(), Session.AccountType.MOJANG);
|
||||
return updateSession(context, session);
|
||||
}
|
||||
|
||||
public static int updateSession(CommandContext<FabricClientCommandSource> context, Session session) {
|
||||
FabricClientCommandSource source = context.getSource();
|
||||
MinecraftClient client = source.getClient();
|
||||
|
||||
((MinecraftClientAccessor) client).session(session);
|
||||
reconnectToCurrentServer(client);
|
||||
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
|
||||
private static void reconnectToCurrentServer(MinecraftClient client) {
|
||||
ServerInfo serverInfo = client.getCurrentServerEntry();
|
||||
|
||||
if (client.world != null) {
|
||||
client.world.disconnect();
|
||||
}
|
||||
client.disconnect();
|
||||
|
||||
ConnectScreen.connect(new TitleScreen(), client, ServerAddress.parse(serverInfo.address), serverInfo, false, null);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package land.chipmunk.chipmunkmod.command.commands;
|
||||
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import land.chipmunk.chipmunkmod.util.player.ChatUtils;
|
||||
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
|
||||
|
||||
import static com.mojang.brigadier.arguments.StringArgumentType.getString;
|
||||
import static com.mojang.brigadier.arguments.StringArgumentType.greedyString;
|
||||
import static land.chipmunk.chipmunkmod.command.CommandManager.argument;
|
||||
import static land.chipmunk.chipmunkmod.command.CommandManager.literal;
|
||||
import static land.chipmunk.chipmunkmod.util.misc.BotValidationUtils.*;
|
||||
|
||||
public class ValidateCommand {
|
||||
public static void register (CommandDispatcher<FabricClientCommandSource> dispatcher) {
|
||||
dispatcher.register(
|
||||
literal("validate")
|
||||
.then(literal("lambda").then(argument("command", greedyString())
|
||||
.executes(c -> lambda(getString(c, "command")))))
|
||||
.then(literal("chomens").then(argument("command", greedyString())
|
||||
.executes(c -> {
|
||||
ChatUtils.warningPrefix("Validation", "Manual ChomeNS Bot validation is deprecated. Please use the completions from typing the bot's prefix.");
|
||||
|
||||
return chomens(getString(c, "command"));
|
||||
})))
|
||||
.then(literal("fnfboyfriend").then(argument("command", greedyString())
|
||||
.executes(c -> fnfboyfriend(getString(c, "command")))))
|
||||
.then(literal("nbot").then(argument("command", greedyString())
|
||||
.executes(c -> nbot(getString(c, "command")))))
|
||||
.then(literal("qbot").then(argument("command", greedyString())
|
||||
.executes(c -> qbot(getString(c, "command")))))
|
||||
.then(literal("hbot").then(argument("command", greedyString())
|
||||
.executes(c -> hbot(getString(c, "command")))))
|
||||
.then(literal("sbot").then(argument("command", greedyString())
|
||||
.executes(c -> sbot(getString(c, "command")))))
|
||||
.then(literal("kittycorp").then(argument("command", greedyString())
|
||||
.executes(c -> kittycorp(getString(c, "command")))))
|
||||
);
|
||||
}
|
||||
}
|
14
src/main/java/land/chipmunk/chipmunkmod/data/BlockArea.java
Normal file
14
src/main/java/land/chipmunk/chipmunkmod/data/BlockArea.java
Normal file
|
@ -0,0 +1,14 @@
|
|||
package land.chipmunk.chipmunkmod.data;
|
||||
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
// ? Am I reinventing the wheel here?
|
||||
public class BlockArea {
|
||||
public BlockPos start;
|
||||
public BlockPos end;
|
||||
|
||||
public BlockArea (BlockPos start, BlockPos end) {
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package land.chipmunk.chipmunkmod.data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ChomeNSBotCommand {
|
||||
public final String name;
|
||||
public final TrustLevel trustLevel;
|
||||
public final List<String> aliases = new ArrayList<>();
|
||||
|
||||
public ChomeNSBotCommand (
|
||||
String name,
|
||||
TrustLevel trustLevel
|
||||
) {
|
||||
this.name = name;
|
||||
this.trustLevel = trustLevel;
|
||||
}
|
||||
|
||||
public enum TrustLevel {
|
||||
PUBLIC,
|
||||
TRUSTED,
|
||||
ADMIN,
|
||||
OWNER
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package land.chipmunk.chipmunkmod.data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class LambdaBotCommand {
|
||||
public final String name;
|
||||
public final AccessLevel accessLevel;
|
||||
public final List<String> aliases = new ArrayList<>();
|
||||
|
||||
public LambdaBotCommand (
|
||||
String name,
|
||||
AccessLevel accessLevel
|
||||
) {
|
||||
this.name = name;
|
||||
this.accessLevel = accessLevel;
|
||||
}
|
||||
|
||||
public enum AccessLevel {
|
||||
PUBLIC,
|
||||
TRUSTED,
|
||||
FULL,
|
||||
OWNER
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package land.chipmunk.chipmunkmod.data;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.minecraft.network.packet.s2c.play.PlayerListS2CPacket;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.world.GameMode;
|
||||
|
||||
public class MutablePlayerListEntry {
|
||||
public GameProfile profile;
|
||||
public GameMode gamemode;
|
||||
public int latency;
|
||||
public Text displayName;
|
||||
|
||||
public MutablePlayerListEntry(GameProfile profile, GameMode gamemode, int latency, Text displayName) {
|
||||
this.profile = profile;
|
||||
this.gamemode = gamemode;
|
||||
this.latency = latency;
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public MutablePlayerListEntry (PlayerListS2CPacket.Entry entry) {
|
||||
this(entry.profile(), entry.gameMode(), entry.latency(), entry.displayName());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package land.chipmunk.chipmunkmod.data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class QBotCommand {
|
||||
public final String name;
|
||||
public final TrustLevel trustLevel;
|
||||
public final List<String> aliases = new ArrayList<>();
|
||||
|
||||
public QBotCommand (
|
||||
String name,
|
||||
TrustLevel trustLevel
|
||||
) {
|
||||
this.name = name;
|
||||
this.trustLevel = trustLevel;
|
||||
}
|
||||
|
||||
public enum TrustLevel {
|
||||
PUBLIC,
|
||||
TRUSTED,
|
||||
OWNER
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package land.chipmunk.chipmunkmod.listeners;
|
||||
|
||||
import net.minecraft.network.packet.Packet;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
public class Listener {
|
||||
public void chatMessageReceived (Text message) {}
|
||||
|
||||
public void packetReceived (Packet<?> packet) {}
|
||||
|
||||
public void packetSent (Packet<?> packet) {}
|
||||
|
||||
public void coreReady () {}
|
||||
|
||||
public void coreMoved () {}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package land.chipmunk.chipmunkmod.listeners;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ListenerManager {
|
||||
public static List<Listener> listeners = new ArrayList<>();
|
||||
|
||||
public static void addListener (Listener listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
package land.chipmunk.chipmunkmod.mixin;
|
||||
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
import land.chipmunk.chipmunkmod.ChipmunkMod;
|
||||
import land.chipmunk.chipmunkmod.Configuration;
|
||||
import land.chipmunk.chipmunkmod.command.CommandManager;
|
||||
import land.chipmunk.chipmunkmod.modules.ChomeNSBotCommandSuggestions;
|
||||
import land.chipmunk.chipmunkmod.modules.LambdaCommandSuggestions;
|
||||
import land.chipmunk.chipmunkmod.modules.QBotCommandSuggestions;
|
||||
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.widget.TextFieldWidget;
|
||||
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
||||
import net.minecraft.client.network.ClientPlayerEntity;
|
||||
import net.minecraft.command.CommandSource;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Mutable;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import static land.chipmunk.chipmunkmod.ChipmunkMod.MCInstance;
|
||||
|
||||
@Mixin(net.minecraft.client.gui.screen.ChatInputSuggestor.class)
|
||||
public class ChatInputSuggestorMixin {
|
||||
@Shadow
|
||||
private CompletableFuture<Suggestions> pendingSuggestions;
|
||||
|
||||
@Shadow
|
||||
public void show(boolean narrateFirstSuggestion) {
|
||||
}
|
||||
|
||||
@Shadow
|
||||
private static int getStartOfCurrentWord(String input) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Mutable
|
||||
@Final
|
||||
@Shadow
|
||||
final TextFieldWidget textField;
|
||||
|
||||
public ChatInputSuggestorMixin() {
|
||||
textField = null;
|
||||
}
|
||||
|
||||
@Inject(at = @At("TAIL"), method = "refresh()V")
|
||||
public void refresh(CallbackInfo ci) {
|
||||
final CommandManager commandManager = CommandManager.INSTANCE;
|
||||
|
||||
final String text = this.textField.getText();
|
||||
final int cursor = this.textField.getCursor();
|
||||
|
||||
final ClientPlayerEntity player = MCInstance.player;
|
||||
|
||||
final Configuration.Bots botinfo = ChipmunkMod.CONFIG.bots;
|
||||
final String lambdaPrefix = botinfo.lambda.prefix;
|
||||
final String chomeNSPrefix = botinfo.chomens.prefix;
|
||||
final String qbotPrefix = botinfo.qbot.prefix;
|
||||
|
||||
if (!text.contains(" ") && text.startsWith(lambdaPrefix) && player != null) {
|
||||
final String textUpToCursor = text.substring(0, cursor);
|
||||
|
||||
final List<String> commands = LambdaCommandSuggestions.INSTANCE.commands
|
||||
.stream()
|
||||
.map((command) -> command.name).toList();
|
||||
|
||||
pendingSuggestions = CommandSource.suggestMatching(
|
||||
commands,
|
||||
new SuggestionsBuilder(
|
||||
textUpToCursor,
|
||||
getStartOfCurrentWord(textUpToCursor)
|
||||
)
|
||||
);
|
||||
|
||||
pendingSuggestions.thenRun(() -> {
|
||||
if (!pendingSuggestions.isDone()) return;
|
||||
|
||||
show(true);
|
||||
});
|
||||
} else if (!text.contains(" ") && text.startsWith(chomeNSPrefix) && player != null) {
|
||||
final String textUpToCursor = text.substring(0, cursor);
|
||||
|
||||
final List<String> commands = ChomeNSBotCommandSuggestions.INSTANCE.commands
|
||||
.stream()
|
||||
.map((command) -> command.name)
|
||||
.toList();
|
||||
|
||||
pendingSuggestions = CommandSource.suggestMatching(
|
||||
commands,
|
||||
new SuggestionsBuilder(
|
||||
textUpToCursor,
|
||||
getStartOfCurrentWord(textUpToCursor)
|
||||
)
|
||||
);
|
||||
|
||||
pendingSuggestions.thenRun(() -> {
|
||||
if (!pendingSuggestions.isDone()) return;
|
||||
|
||||
show(true);
|
||||
});
|
||||
} else if (!text.contains(" ") && text.startsWith(qbotPrefix) && player != null) {
|
||||
final String textUpToCursor = text.substring(0, cursor);
|
||||
|
||||
final List<String> commands = QBotCommandSuggestions.INSTANCE.commands
|
||||
.stream()
|
||||
.map((command) -> command.name).toList();
|
||||
|
||||
pendingSuggestions = CommandSource.suggestMatching(
|
||||
commands,
|
||||
new SuggestionsBuilder(
|
||||
textUpToCursor,
|
||||
getStartOfCurrentWord(textUpToCursor)
|
||||
)
|
||||
);
|
||||
|
||||
pendingSuggestions.thenRun(() -> {
|
||||
if (!pendingSuggestions.isDone()) return;
|
||||
|
||||
show(true);
|
||||
});
|
||||
} else if (cursor > commandManager.prefix.length() && text.startsWith(commandManager.prefix)) {
|
||||
final StringReader reader = new StringReader(text);
|
||||
reader.setCursor(commandManager.prefix.length()); // Skip the prefix
|
||||
|
||||
final MinecraftClient client = MCInstance;
|
||||
final ClientPlayNetworkHandler networkHandler = client.getNetworkHandler();
|
||||
|
||||
if (networkHandler == null) return;
|
||||
|
||||
final CommandDispatcher<FabricClientCommandSource> dispatcher = commandManager.dispatcher;
|
||||
final FabricClientCommandSource commandSource = (FabricClientCommandSource) networkHandler.getCommandSource();
|
||||
|
||||
pendingSuggestions = dispatcher.getCompletionSuggestions(dispatcher.parse(reader, commandSource), cursor);
|
||||
show(true);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,187 @@
|
|||
package land.chipmunk.chipmunkmod.mixin;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import land.chipmunk.chipmunkmod.ChipmunkMod;
|
||||
|
||||
import land.chipmunk.chipmunkmod.Configuration;
|
||||
import land.chipmunk.chipmunkmod.data.LambdaBotCommand;
|
||||
import land.chipmunk.chipmunkmod.modules.LambdaCommandSuggestions;
|
||||
|
||||
import land.chipmunk.chipmunkmod.data.ChomeNSBotCommand;
|
||||
import land.chipmunk.chipmunkmod.modules.ChomeNSBotCommandSuggestions;
|
||||
|
||||
import land.chipmunk.chipmunkmod.data.QBotCommand;
|
||||
import land.chipmunk.chipmunkmod.modules.QBotCommandSuggestions;
|
||||
|
||||
import land.chipmunk.chipmunkmod.util.misc.BotValidationUtils;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.text.Text;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static land.chipmunk.chipmunkmod.ChipmunkMod.MCInstance;
|
||||
|
||||
@Mixin(value = net.minecraft.client.gui.screen.ChatScreen.class)
|
||||
public class ChatScreenMixin extends Screen {
|
||||
@Shadow private String originalChatText;
|
||||
|
||||
public ChatScreenMixin(String originalChatText) {
|
||||
super(Text.translatable("chat_screen.title"));
|
||||
this.originalChatText = originalChatText;
|
||||
}
|
||||
|
||||
@Inject(method = "sendMessage", at = @At("HEAD"), cancellable = true)
|
||||
private void sendMessage (String chatText, boolean addToHistory, CallbackInfo cir) {
|
||||
final MinecraftClient client = MCInstance;
|
||||
|
||||
if (addToHistory) {
|
||||
client.inGameHud.getChatHud().addToMessageHistory(chatText);
|
||||
}
|
||||
|
||||
final Configuration.Bots botinfo = ChipmunkMod.CONFIG.bots;
|
||||
final String lambdaPrefix = botinfo.lambda.prefix;
|
||||
final String chomeNSPrefix = botinfo.chomens.prefix;
|
||||
final String qbotPrefix = botinfo.qbot.prefix;
|
||||
final String testbotPrefix = botinfo.testbot.prefix;
|
||||
|
||||
if (chatText.startsWith(lambdaPrefix)) {
|
||||
final List<LambdaBotCommand> commands = LambdaCommandSuggestions.INSTANCE.commands;
|
||||
|
||||
final List<String> moreOrTrustedCommands = commands.stream()
|
||||
.filter((command) -> command.accessLevel != LambdaBotCommand.AccessLevel.PUBLIC)
|
||||
.map((command) -> command.name.toLowerCase())
|
||||
.toList();
|
||||
|
||||
final List<String> aliases = new ArrayList<>();
|
||||
for (LambdaBotCommand command : commands) {
|
||||
if (command.accessLevel == LambdaBotCommand.AccessLevel.PUBLIC) continue;
|
||||
|
||||
aliases.addAll(command.aliases);
|
||||
}
|
||||
|
||||
final String chatCommand = chatText.toLowerCase().split("\\s")[0];
|
||||
final int prefixLength = lambdaPrefix.length();
|
||||
|
||||
if (
|
||||
moreOrTrustedCommands.contains(chatCommand) ||
|
||||
aliases.contains(chatCommand.substring(prefixLength))
|
||||
) {
|
||||
try {
|
||||
BotValidationUtils.lambda(chatText.substring(prefixLength));
|
||||
|
||||
cir.cancel();
|
||||
|
||||
return;
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
} else if (chatText.startsWith(chomeNSPrefix)) {
|
||||
final List<ChomeNSBotCommand> commands = ChomeNSBotCommandSuggestions.INSTANCE.commands;
|
||||
|
||||
final List<String> moreOrTrustedCommands = commands.stream()
|
||||
.filter((command) -> command.trustLevel != ChomeNSBotCommand.TrustLevel.PUBLIC)
|
||||
.map((command) -> command.name.toLowerCase())
|
||||
.toList();
|
||||
|
||||
final List<String> aliases = new ArrayList<>();
|
||||
for (ChomeNSBotCommand command : commands) {
|
||||
if (command.trustLevel == ChomeNSBotCommand.TrustLevel.PUBLIC) continue;
|
||||
|
||||
aliases.addAll(command.aliases);
|
||||
}
|
||||
|
||||
final String chatCommand = chatText.toLowerCase().split("\\s")[0];
|
||||
final int prefixLength = chomeNSPrefix.length();
|
||||
|
||||
if (
|
||||
moreOrTrustedCommands.contains(chatCommand) ||
|
||||
aliases.contains(chatCommand.substring(prefixLength))
|
||||
) {
|
||||
try {
|
||||
BotValidationUtils.chomens(chatText.substring(prefixLength));
|
||||
|
||||
cir.cancel();
|
||||
|
||||
return;
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
} else if (chatText.startsWith(qbotPrefix)) {
|
||||
final List<QBotCommand> commands = QBotCommandSuggestions.INSTANCE.commands;
|
||||
|
||||
final List<String> moreOrTrustedCommands = commands.stream()
|
||||
.filter((command) -> command.trustLevel != QBotCommand.TrustLevel.PUBLIC)
|
||||
.map((command) -> command.name.toLowerCase())
|
||||
.toList();
|
||||
|
||||
final List<String> aliases = new ArrayList<>();
|
||||
for (QBotCommand command : commands) {
|
||||
if (command.trustLevel == QBotCommand.TrustLevel.PUBLIC) continue;
|
||||
|
||||
aliases.addAll(command.aliases);
|
||||
}
|
||||
|
||||
final String chatCommand = chatText.toLowerCase().split("\\s")[0];
|
||||
final int prefixLength = qbotPrefix.length();
|
||||
|
||||
if (
|
||||
moreOrTrustedCommands.contains(chatCommand) ||
|
||||
aliases.contains(chatCommand.substring(prefixLength))
|
||||
) {
|
||||
try {
|
||||
BotValidationUtils.qbot(chatText.substring(prefixLength));
|
||||
|
||||
cir.cancel();
|
||||
|
||||
return;
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
} else if (botinfo.testbot.webhookUrl != null && chatText.startsWith(testbotPrefix)) {
|
||||
ChipmunkMod.executorService.submit(() -> {
|
||||
try {
|
||||
final URL url = new URL(botinfo.testbot.webhookUrl);
|
||||
|
||||
final HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
|
||||
connection.addRequestProperty("Content-Type", "application/json");
|
||||
connection.addRequestProperty("User-Agent", "ChipmunkMod");
|
||||
connection.setDoOutput(true);
|
||||
connection.setRequestMethod("POST");
|
||||
|
||||
final JsonObject jsonObject = new JsonObject();
|
||||
|
||||
jsonObject.addProperty("username", "ChipmunkMod UwU");
|
||||
jsonObject.addProperty("content", MCInstance.getSession().getUsername());
|
||||
|
||||
final OutputStream stream = connection.getOutputStream();
|
||||
stream.write(jsonObject.toString().getBytes());
|
||||
stream.flush();
|
||||
stream.close();
|
||||
|
||||
connection.getInputStream().close();
|
||||
connection.disconnect();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (client == null) return;
|
||||
|
||||
if (chatText.startsWith("/")) {
|
||||
client.player.networkHandler.sendChatCommand(chatText.substring(1));
|
||||
} else {
|
||||
client.player.networkHandler.sendChatMessage(chatText);
|
||||
}
|
||||
|
||||
cir.cancel();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package land.chipmunk.chipmunkmod.mixin;
|
||||
|
||||
import net.minecraft.network.ClientConnection;
|
||||
import net.minecraft.network.listener.PacketListener;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(ClientConnection.class)
|
||||
public interface ClientConnectionAccessor {
|
||||
@Accessor("packetListener")
|
||||
PacketListener packetListener ();
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package land.chipmunk.chipmunkmod.mixin;
|
||||
|
||||
import net.minecraft.network.ClientConnection;
|
||||
import net.minecraft.network.listener.PacketListener;
|
||||
import net.minecraft.network.packet.Packet;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
|
||||
@Mixin(ClientConnection.class)
|
||||
public interface ClientConnectionInvoker {
|
||||
@Invoker("handlePacket")
|
||||
static <T extends PacketListener> void handlePacket (Packet<T> packet, PacketListener listener) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
package land.chipmunk.chipmunkmod.mixin;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.DecoderException;
|
||||
import land.chipmunk.chipmunkmod.listeners.Listener;
|
||||
import land.chipmunk.chipmunkmod.listeners.ListenerManager;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.world.ClientWorld;
|
||||
import net.minecraft.network.listener.PacketListener;
|
||||
import net.minecraft.network.packet.Packet;
|
||||
import net.minecraft.network.packet.c2s.play.RequestCommandCompletionsC2SPacket;
|
||||
import net.minecraft.network.packet.s2c.play.ParticleS2CPacket;
|
||||
import net.minecraft.network.packet.s2c.play.PlaySoundS2CPacket;
|
||||
import net.minecraft.sound.SoundEvent;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Identifier;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
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;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@Mixin(net.minecraft.network.ClientConnection.class)
|
||||
public class ClientConnectionMixin {
|
||||
@Unique
|
||||
private static final Pattern CUSTOM_PITCH_PATTERN = Pattern.compile(".*\\.pitch\\.(.*)");
|
||||
|
||||
@Inject(at = @At("HEAD"), method = "disconnect", cancellable = true)
|
||||
public void disconnect (Text disconnectReason, CallbackInfo ci) {
|
||||
if (disconnectReason == ClientPlayNetworkHandlerAccessor.chatValidationFailedText()) {
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "exceptionCaught", at = @At("HEAD"), cancellable = true)
|
||||
private void exceptionCaught (ChannelHandlerContext context, Throwable ex, CallbackInfo ci) {
|
||||
ci.cancel();
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
@Inject(method = "handlePacket", at = @At("HEAD"), cancellable = true)
|
||||
private static void handlePacket (Packet<?> packet, PacketListener _listener, CallbackInfo ci) {
|
||||
for (Listener listener : ListenerManager.listeners) {
|
||||
listener.packetReceived(packet);
|
||||
}
|
||||
|
||||
final MinecraftClient client = MinecraftClient.getInstance();
|
||||
|
||||
// please don't skid this.,.
|
||||
// mabe mabe mabe
|
||||
if (packet instanceof ParticleS2CPacket t_packet) {
|
||||
final double max = 1000;
|
||||
|
||||
if (t_packet.getCount() > max) {
|
||||
ci.cancel();
|
||||
}
|
||||
} else if (packet instanceof PlaySoundS2CPacket t_packet) {
|
||||
final SoundEvent soundEvent = t_packet.getSound().value();
|
||||
|
||||
final Identifier sound = soundEvent.getId();
|
||||
|
||||
final Matcher matcher = CUSTOM_PITCH_PATTERN.matcher(sound.getPath());
|
||||
|
||||
if (!matcher.find()) return;
|
||||
|
||||
try {
|
||||
final String stringPitch = matcher.group(1);
|
||||
|
||||
final float pitch = Float.parseFloat(stringPitch);
|
||||
|
||||
final ClientWorld world = client.world;
|
||||
|
||||
if (world == null) return;
|
||||
|
||||
// huge mess
|
||||
final SoundEvent newSound = SoundEvent.of(Identifier.of(sound.getNamespace(), sound.getPath().substring(0, sound.getPath().length() - (".pitch." + stringPitch).length())));
|
||||
|
||||
client.executeSync(() -> world.playSound(client.player, t_packet.getX(), t_packet.getY(), t_packet.getZ(), newSound, t_packet.getCategory(), t_packet.getVolume(), pitch, t_packet.getSeed()));
|
||||
|
||||
ci.cancel();
|
||||
} catch (NumberFormatException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (t_packet.getVolume() == 1 && sound.getPath().equals("entity.enderman.scream")) ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(at = @At("HEAD"), method = "send(Lnet/minecraft/network/packet/Packet;)V", cancellable = true)
|
||||
private void sendPacket (Packet<?> packet, CallbackInfo ci) {
|
||||
if (packet instanceof RequestCommandCompletionsC2SPacket t_packet) {
|
||||
if (t_packet.getPartialCommand().length() > 2048) {
|
||||
ci.cancel();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (Listener listener : ListenerManager.listeners) {
|
||||
listener.packetSent(packet);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package land.chipmunk.chipmunkmod.mixin;
|
||||
|
||||
import net.minecraft.client.network.PlayerListEntry;
|
||||
import net.minecraft.network.ClientConnection;
|
||||
import net.minecraft.network.packet.s2c.play.PlayerListS2CPacket;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
@Mixin(net.minecraft.client.network.ClientPlayNetworkHandler.class)
|
||||
public interface ClientPlayNetworkHandlerAccessor {
|
||||
@Accessor("CHAT_VALIDATION_FAILED_TEXT")
|
||||
static Text chatValidationFailedText () { throw new AssertionError(); }
|
||||
|
||||
@Accessor("playerListEntries")
|
||||
Map<UUID, PlayerListEntry> playerListEntries();
|
||||
|
||||
@Accessor("listedPlayerListEntries")
|
||||
Set<PlayerListEntry> listedPlayerListEntries();
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
package land.chipmunk.chipmunkmod.mixin;
|
||||
|
||||
import land.chipmunk.chipmunkmod.ChipmunkMod;
|
||||
import land.chipmunk.chipmunkmod.command.CommandManager;
|
||||
import land.chipmunk.chipmunkmod.listeners.Listener;
|
||||
import land.chipmunk.chipmunkmod.listeners.ListenerManager;
|
||||
import land.chipmunk.chipmunkmod.modules.*;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.PlayerListEntry;
|
||||
import net.minecraft.command.CommandRegistryAccess;
|
||||
import net.minecraft.network.encryption.NetworkEncryptionUtils;
|
||||
import net.minecraft.network.message.LastSeenMessagesCollector;
|
||||
import net.minecraft.network.message.MessageBody;
|
||||
import net.minecraft.network.message.MessageChain;
|
||||
import net.minecraft.network.message.MessageSignatureData;
|
||||
import net.minecraft.network.packet.c2s.play.ChatMessageC2SPacket;
|
||||
import net.minecraft.network.packet.s2c.play.GameJoinS2CPacket;
|
||||
import net.minecraft.network.packet.s2c.play.GameMessageS2CPacket;
|
||||
import net.minecraft.network.packet.s2c.play.PlayerRemoveS2CPacket;
|
||||
import net.minecraft.registry.DynamicRegistryManager;
|
||||
import net.minecraft.resource.featuretoggle.FeatureSet;
|
||||
import net.minecraft.text.PlainTextContent;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.text.TranslatableTextContent;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import static land.chipmunk.chipmunkmod.ChipmunkMod.MCInstance;
|
||||
|
||||
@Mixin(value = net.minecraft.client.network.ClientPlayNetworkHandler.class, priority = 1002)
|
||||
public class ClientPlayNetworkHandlerMixin {
|
||||
@Final
|
||||
@Shadow private FeatureSet enabledFeatures;
|
||||
@Final
|
||||
@Shadow private DynamicRegistryManager.Immutable combinedDynamicRegistries;
|
||||
@Shadow private LastSeenMessagesCollector lastSeenMessagesCollector;
|
||||
@Shadow private MessageChain.Packer messagePacker;
|
||||
|
||||
@Inject(method = "onGameJoin", at = @At("TAIL"))
|
||||
private void onGameJoin (GameJoinS2CPacket packet, CallbackInfo ci) {
|
||||
final CommandRegistryAccess commandRegistryAccess = CommandRegistryAccess.of(this.combinedDynamicRegistries, this.enabledFeatures);
|
||||
|
||||
KaboomCheck.INSTANCE.onJoin();
|
||||
CommandManager.INSTANCE = new CommandManager(ChipmunkMod.CONFIG.client.prefix, commandRegistryAccess);
|
||||
SelfCare.INSTANCE.onJoin();
|
||||
CommandCore.INSTANCE.init();
|
||||
CustomChat.INSTANCE.init();
|
||||
SongPlayer.INSTANCE.coreReady();
|
||||
RainbowName.INSTANCE.init();
|
||||
|
||||
// Bot command suggestions stuff
|
||||
LambdaCommandSuggestions.INSTANCE.init();
|
||||
LambdaAuth.INSTANCE.init();
|
||||
|
||||
ChomeNSBotCommandSuggestions.INSTANCE.init();
|
||||
ChomeNSAuth.INSTANCE.init();
|
||||
|
||||
QBotCommandSuggestions.INSTANCE.init();
|
||||
QBotAuth.INSTANCE.init(); // Fix incorrect uppercase on QBotAuth
|
||||
}
|
||||
|
||||
@Inject(method = "onPlayerRemove", at = @At("HEAD"), cancellable = true)
|
||||
private void onPlayerRemove (PlayerRemoveS2CPacket packet, CallbackInfo ci) { ci.cancel(); }
|
||||
|
||||
@Inject(method = "onGameMessage", at = @At("HEAD"), cancellable = true)
|
||||
private void onGameMessage (GameMessageS2CPacket packet, CallbackInfo ci) {
|
||||
final Text message = packet.content();
|
||||
|
||||
// Ignore annoying messages
|
||||
try {
|
||||
if (RainbowName.INSTANCE.enabled) {
|
||||
if (message.getString().contains("Your nickname is now ") || message.getString().contains("Nickname changed.")) {
|
||||
ci.cancel();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (((TranslatableTextContent) message.getContent()).getKey().equals("advMode.setCommand.success")) {
|
||||
ci.cancel();
|
||||
return;
|
||||
}
|
||||
} catch (ClassCastException ignored) {}
|
||||
|
||||
for (Listener listener : ListenerManager.listeners) {
|
||||
listener.chatMessageReceived(message);
|
||||
}
|
||||
|
||||
// Bot command suggestion requests messages
|
||||
final String suggestionId = message.getSiblings().getFirst().getString();
|
||||
final String authId = ((PlainTextContent) message.getContent()).string();
|
||||
|
||||
try {
|
||||
if (suggestionId.equals(LambdaCommandSuggestions.ID) || authId.equals(LambdaAuth.INSTANCE.ID)) {
|
||||
ci.cancel();
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
|
||||
try {
|
||||
if (suggestionId.equals(ChomeNSBotCommandSuggestions.ID) || authId.equals(ChomeNSAuth.INSTANCE.ID)) {
|
||||
ci.cancel();
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
|
||||
try {
|
||||
if (suggestionId.equals(QBotCommandSuggestions.ID) || authId.equals(QBotAuth.INSTANCE.ID)) { // Fix incorrect uppercase on QBotAuth
|
||||
ci.cancel();
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
|
||||
@Inject(method = "sendChatMessage", at = @At("HEAD"), cancellable = true)
|
||||
private void sendChatMessage (String chatText, CallbackInfo ci) {
|
||||
final CommandManager commandManager = CommandManager.INSTANCE;
|
||||
|
||||
final String secret = String.valueOf(Chat.secret);
|
||||
|
||||
if (chatText.startsWith(commandManager.prefix)) {
|
||||
commandManager.executeCommand(chatText.substring(commandManager.prefix.length()));
|
||||
|
||||
ci.cancel();
|
||||
} else if (!chatText.startsWith("/") && !chatText.startsWith(secret)) {
|
||||
CustomChat.INSTANCE.chat(chatText);
|
||||
|
||||
ci.cancel();
|
||||
}
|
||||
|
||||
if (chatText.startsWith(secret)) {
|
||||
final String content = chatText.substring(secret.length());
|
||||
|
||||
Instant instant = Instant.now();
|
||||
long l = NetworkEncryptionUtils.SecureRandomUtil.nextLong();
|
||||
|
||||
LastSeenMessagesCollector.LastSeenMessages lastSeenMessages = this.lastSeenMessagesCollector.collect();
|
||||
MessageSignatureData messageSignatureData = this.messagePacker.pack(new MessageBody(content, instant, l, lastSeenMessages.lastSeen()));
|
||||
MCInstance.getNetworkHandler().sendPacket(new ChatMessageC2SPacket(content, instant, l, messageSignatureData, lastSeenMessages.update()));
|
||||
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package land.chipmunk.chipmunkmod.mixin;
|
||||
|
||||
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;
|
||||
import net.minecraft.client.network.ClientPlayerEntity;
|
||||
import net.minecraft.entity.MovementType;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.world.ClientWorld;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec2f;
|
||||
import land.chipmunk.chipmunkmod.modules.CommandCore;
|
||||
|
||||
@Mixin(ClientPlayerEntity.class)
|
||||
public class ClientPlayerEntityMixin {
|
||||
@Unique private static MinecraftClient CLIENT = MinecraftClient.getInstance();
|
||||
|
||||
@Inject(at = @At("HEAD"), method = "move")
|
||||
public void move (MovementType type, Vec3d relPos, CallbackInfo ci) {
|
||||
if ((ClientPlayerEntity) (Object) this != CLIENT.player) return;
|
||||
|
||||
final Vec3d position = ((ClientPlayerEntity) (Object) this).getPos().add(relPos);
|
||||
|
||||
final ClientWorld world = CLIENT.getNetworkHandler().getWorld();
|
||||
|
||||
final BlockPos origin = CommandCore.INSTANCE.origin;
|
||||
if (origin == null) { CommandCore.INSTANCE.move(position); return; }
|
||||
final int distance = (int) Math.sqrt(new Vec2f(origin.getX() / 16, origin.getZ() / 16).distanceSquared(new Vec2f((int) position.getX() / 16, (int) position.getZ() / 16)));
|
||||
if (distance > world.getSimulationDistance()) {
|
||||
CommandCore.INSTANCE.clientPlayerEntityFilled = true;
|
||||
CommandCore.INSTANCE.move(position);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package land.chipmunk.chipmunkmod.mixin;
|
||||
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.ParseResults;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.context.CommandContextBuilder;
|
||||
import com.mojang.brigadier.tree.CommandNode;
|
||||
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;
|
||||
|
||||
@Mixin(CommandDispatcher.class)
|
||||
public class CommandDispatcherMixin<S> {
|
||||
@Inject(method = "parseNodes", at = @At("HEAD"), cancellable = true, /* important --> */ remap = false)
|
||||
private void parseNodes (CommandNode<S> node, StringReader originalReader, CommandContextBuilder<S> contextSoFar, CallbackInfoReturnable<ParseResults<S>> cir) {
|
||||
// correct way to patch this?
|
||||
// if (node.getRelevantNodes(originalReader).size() > 127) {
|
||||
// cir.setReturnValue(new ParseResults<>(contextSoFar));
|
||||
//
|
||||
// cir.cancel();
|
||||
// }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package land.chipmunk.chipmunkmod.mixin;
|
||||
|
||||
import net.minecraft.client.particle.ElderGuardianAppearanceParticle;
|
||||
import net.minecraft.client.particle.Particle;
|
||||
import net.minecraft.client.world.ClientWorld;
|
||||
import net.minecraft.particle.SimpleParticleType;
|
||||
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;
|
||||
|
||||
@Mixin(ElderGuardianAppearanceParticle.Factory.class)
|
||||
public class ElderGuardianAppearanceParticleMixin {
|
||||
@Inject(method = "createParticle(Lnet/minecraft/particle/SimpleParticleType;Lnet/minecraft/client/world/ClientWorld;DDDDDD)Lnet/minecraft/client/particle/Particle;", at = @At("HEAD"))
|
||||
private void createParticle (SimpleParticleType simpleParticleType, ClientWorld clientWorld, double d, double e, double f, double g, double h, double i, CallbackInfoReturnable<Particle> cir) {
|
||||
if (cir.isCancelled() || !cir.isCancellable()) return;
|
||||
|
||||
cir.cancel();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package land.chipmunk.chipmunkmod.mixin;
|
||||
|
||||
import net.minecraft.util.Identifier;
|
||||
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;
|
||||
|
||||
@Mixin(Identifier.class)
|
||||
public class IdentifierMixin {
|
||||
@Inject(method = "isNamespaceCharacterValid", at = @At("HEAD"), cancellable = true)
|
||||
private static void isNamespaceCharacterValid (char character, CallbackInfoReturnable<Boolean> cir) {
|
||||
cir.setReturnValue(true);
|
||||
|
||||
cir.cancel();
|
||||
}
|
||||
|
||||
@Inject(method = "isNamespaceValid", at = @At("HEAD"), cancellable = true)
|
||||
private static void isNamespaceValid (String namespace, CallbackInfoReturnable<Boolean> cir) {
|
||||
cir.setReturnValue(true);
|
||||
|
||||
cir.cancel();
|
||||
}
|
||||
|
||||
@Inject(method = "validateNamespace", at = @At("HEAD"), cancellable = true)
|
||||
private static void validateNamespace(String namespace, String path, CallbackInfoReturnable<String> cir) {
|
||||
cir.setReturnValue(namespace);
|
||||
|
||||
cir.cancel();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package land.chipmunk.chipmunkmod.mixin;
|
||||
|
||||
import net.minecraft.client.session.Session;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Mutable;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(net.minecraft.client.MinecraftClient.class)
|
||||
public interface MinecraftClientAccessor {
|
||||
@Accessor("session")
|
||||
Session session ();
|
||||
|
||||
@Mutable
|
||||
@Accessor("session")
|
||||
void session (Session session);
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package land.chipmunk.chipmunkmod.mixin;
|
||||
|
||||
import net.minecraft.client.network.PlayerListEntry;
|
||||
import net.minecraft.world.GameMode;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
|
||||
@Mixin(PlayerListEntry.class)
|
||||
public interface PlayerListEntryAccessor {
|
||||
@Accessor("gameMode")
|
||||
void setGameMode (GameMode gameMode);
|
||||
|
||||
@Accessor("latency")
|
||||
void setLatency (int latency);
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package land.chipmunk.chipmunkmod.mixin;
|
||||
|
||||
import net.minecraft.client.sound.SoundInstance;
|
||||
import net.minecraft.client.sound.SoundSystem;
|
||||
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;
|
||||
|
||||
@Mixin(SoundSystem.class)
|
||||
public class SoundSystemMixin {
|
||||
@Inject(method = "getAdjustedPitch", at = @At("HEAD"), cancellable = true)
|
||||
private void getAdjustedPitch (SoundInstance sound, CallbackInfoReturnable<Float> cir) {
|
||||
cir.setReturnValue(sound.getPitch());
|
||||
cir.cancel();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package land.chipmunk.chipmunkmod.mixin;
|
||||
|
||||
import net.minecraft.util.StringHelper;
|
||||
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;
|
||||
|
||||
@Mixin(StringHelper.class)
|
||||
public class StringHelperMixin {
|
||||
@Inject(method = "truncateChat", at = @At("HEAD"), cancellable = true)
|
||||
private static void truncateChat (String text, CallbackInfoReturnable<String> cir) {
|
||||
cir.setReturnValue(text);
|
||||
cir.cancel();
|
||||
}
|
||||
|
||||
@Inject(method = "stripTextFormat", at = @At("HEAD"), cancellable = true)
|
||||
private static void stripTextFormat(String text, CallbackInfoReturnable<String> cir) {
|
||||
cir.setReturnValue(text);
|
||||
cir.cancel();
|
||||
}
|
||||
|
||||
@Inject(method = "isValidChar", at = @At("HEAD"), cancellable = true)
|
||||
private static void isValidChar (char chr, CallbackInfoReturnable<Boolean> cir) {
|
||||
cir.setReturnValue(chr >= ' ' && chr != '\u007f');
|
||||
cir.cancel();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package land.chipmunk.chipmunkmod.mixin;
|
||||
|
||||
import net.minecraft.client.gui.widget.TextFieldWidget;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(TextFieldWidget.class)
|
||||
public class TextFieldWidgetMixin {
|
||||
@Shadow private int maxLength;
|
||||
|
||||
@Inject(method = "setMaxLength", at = @At("HEAD"), cancellable = true)
|
||||
private void setMaxLength (int length, CallbackInfo ci) {
|
||||
this.maxLength = Integer.MAX_VALUE;
|
||||
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
26
src/main/java/land/chipmunk/chipmunkmod/mixin/TextMixin.java
Normal file
26
src/main/java/land/chipmunk/chipmunkmod/mixin/TextMixin.java
Normal file
|
@ -0,0 +1,26 @@
|
|||
package land.chipmunk.chipmunkmod.mixin;
|
||||
|
||||
import net.minecraft.text.StringVisitable;
|
||||
import net.minecraft.text.Style;
|
||||
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 org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Optional;
|
||||
|
||||
@Mixin(Text.class)
|
||||
public interface TextMixin {
|
||||
@Inject(method = "visit(Lnet/minecraft/text/StringVisitable$StyledVisitor;Lnet/minecraft/text/Style;)Ljava/util/Optional;", at = @At(value = "INVOKE", target = "Lnet/minecraft/text/Text;visit(Lnet/minecraft/text/StringVisitable$StyledVisitor;Lnet/minecraft/text/Style;)Ljava/util/Optional;"), cancellable = true, locals = LocalCapture.CAPTURE_FAILHARD)
|
||||
private <T> void visit (StringVisitable.StyledVisitor<T> styledVisitor, Style style, CallbackInfoReturnable<Optional<T>> cir, Style style2, Optional optional, Iterator var5, Text text) {
|
||||
if (text == null) cir.setReturnValue(Optional.empty());
|
||||
}
|
||||
|
||||
@Inject(method = "visit(Lnet/minecraft/text/StringVisitable$Visitor;)Ljava/util/Optional;", at = @At(value = "INVOKE", target = "Lnet/minecraft/text/Text;visit(Lnet/minecraft/text/StringVisitable$Visitor;)Ljava/util/Optional;"), cancellable = true, locals = LocalCapture.CAPTURE_FAILHARD)
|
||||
private <T> void visit (StringVisitable.Visitor<T> visitor, CallbackInfoReturnable<Optional<T>> cir, Optional optional, Iterator var3, Text text) {
|
||||
if (text == null) cir.setReturnValue(Optional.empty());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package land.chipmunk.chipmunkmod.mixin;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import net.minecraft.text.MutableText;
|
||||
import net.minecraft.text.Text;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
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.CallbackInfoReturnable;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
@Mixin(Text.Serializer.class)
|
||||
public class TextSerializerMixin {
|
||||
@Unique private static final int LIMIT = 128;
|
||||
@Unique private int i;
|
||||
|
||||
@Unique
|
||||
private boolean checkDepth (JsonElement element) {
|
||||
if (element.isJsonPrimitive()) return false;
|
||||
else if (i >= LIMIT) return true;
|
||||
|
||||
if (element.isJsonArray()) {
|
||||
i++;
|
||||
|
||||
for (JsonElement item : element.getAsJsonArray()) if (checkDepth(item)) return true;
|
||||
} else if (element.isJsonObject()) {
|
||||
final JsonObject object = element.getAsJsonObject();
|
||||
|
||||
JsonArray array;
|
||||
|
||||
if (object.has("extra")) array = object.get("extra").getAsJsonArray();
|
||||
else if (object.has("with")) array = object.get("with").getAsJsonArray();
|
||||
else return false;
|
||||
|
||||
i++;
|
||||
|
||||
for (JsonElement member : array) if (checkDepth(member)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Inject(method = "deserialize(Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Lnet/minecraft/text/MutableText;", at = @At("HEAD"), cancellable = true)
|
||||
private void deserialize (JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext, CallbackInfoReturnable<MutableText> cir) {
|
||||
i = 0; // better way to do this?
|
||||
|
||||
final boolean overLimit = checkDepth(jsonElement);
|
||||
if (!overLimit) return;
|
||||
|
||||
cir.setReturnValue(Text.empty()); // just ignores it
|
||||
cir.cancel();
|
||||
}
|
||||
}
|
18
src/main/java/land/chipmunk/chipmunkmod/modules/Chat.java
Normal file
18
src/main/java/land/chipmunk/chipmunkmod/modules/Chat.java
Normal file
|
@ -0,0 +1,18 @@
|
|||
package land.chipmunk.chipmunkmod.modules;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
||||
|
||||
public class Chat {
|
||||
public static double secret = Math.random();
|
||||
|
||||
public static void sendChatMessage (String message) { sendChatMessage(message, false); }
|
||||
public static void sendChatMessage (String message, boolean usePlayerChat) {
|
||||
if (message == null) return;
|
||||
|
||||
final ClientPlayNetworkHandler networkHandler = MinecraftClient.getInstance().getNetworkHandler();
|
||||
|
||||
if (usePlayerChat) networkHandler.sendChatMessage(secret + message);
|
||||
else networkHandler.sendChatMessage(message);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
package land.chipmunk.chipmunkmod.modules;
|
||||
|
||||
import com.google.common.hash.Hashing;
|
||||
import land.chipmunk.chipmunkmod.ChipmunkMod;
|
||||
import land.chipmunk.chipmunkmod.listeners.Listener;
|
||||
import land.chipmunk.chipmunkmod.listeners.ListenerManager;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.minecraft.text.PlainTextContent;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.text.TextContent;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
public class ChomeNSAuth extends Listener {
|
||||
public final String ID = "chomens_bot_verify";
|
||||
|
||||
public static final ChomeNSAuth INSTANCE = new ChomeNSAuth();
|
||||
|
||||
public ChomeNSAuth () {
|
||||
ListenerManager.addListener(this);
|
||||
}
|
||||
|
||||
public void init () {}
|
||||
|
||||
@Override
|
||||
public void chatMessageReceived(Text text) {
|
||||
final String authKey = ChipmunkMod.CONFIG.bots.chomens.authKey;
|
||||
if (authKey == null) return;
|
||||
|
||||
final TextContent message = text.getContent();
|
||||
if (!(message instanceof PlainTextContent)) return;
|
||||
|
||||
final String ID = ((PlainTextContent) message).string();
|
||||
if (!ID.equals(this.ID)) return;
|
||||
|
||||
final List<Text> children = text.getSiblings();
|
||||
|
||||
if (children.size() != 2) return;
|
||||
if (!(children.getFirst().getContent() instanceof PlainTextContent)) return;
|
||||
|
||||
final String hash = ((PlainTextContent) children.getFirst().getContent()).string();
|
||||
final long time = System.currentTimeMillis() / 10_000;
|
||||
|
||||
final String actual = Hashing.sha256()
|
||||
// very pro hash input
|
||||
.hashString(authKey + time, StandardCharsets.UTF_8)
|
||||
.toString()
|
||||
.substring(0, 8);
|
||||
|
||||
if (!hash.equals(actual)) return;
|
||||
if (!(children.get(1).getContent() instanceof PlainTextContent)) return;
|
||||
|
||||
final String selector = ((PlainTextContent) children.get(1).getContent()).string();
|
||||
|
||||
final String toSendHash = Hashing.sha256()
|
||||
// very pro hash input
|
||||
.hashString(authKey + authKey + time + time, StandardCharsets.UTF_8)
|
||||
.toString()
|
||||
.substring(0, 8);
|
||||
|
||||
final Component toSend = Component.text(ID)
|
||||
.append(Component.text(toSendHash));
|
||||
|
||||
final String toSendString = GsonComponentSerializer.gson().serialize(toSend);
|
||||
|
||||
CommandCore.INSTANCE.run("minecraft:tellraw " + selector + " " + toSendString);
|
||||
CustomChat.INSTANCE.resetTotal();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
package land.chipmunk.chipmunkmod.modules;
|
||||
|
||||
import land.chipmunk.chipmunkmod.ChipmunkMod;
|
||||
import land.chipmunk.chipmunkmod.data.ChomeNSBotCommand;
|
||||
import land.chipmunk.chipmunkmod.listeners.Listener;
|
||||
import land.chipmunk.chipmunkmod.listeners.ListenerManager;
|
||||
import land.chipmunk.chipmunkmod.util.player.UUIDUtil;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientPlayerEntity;
|
||||
import net.minecraft.text.PlainTextContent;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static land.chipmunk.chipmunkmod.ChipmunkMod.MCInstance;
|
||||
|
||||
public class ChomeNSBotCommandSuggestions extends Listener {
|
||||
public static final String ID = "chomens_bot_request_command_suggestion";
|
||||
|
||||
public static ChomeNSBotCommandSuggestions INSTANCE = new ChomeNSBotCommandSuggestions(MCInstance);
|
||||
private final MinecraftClient client;
|
||||
|
||||
public List<ChomeNSBotCommand> commands = new ArrayList<>();
|
||||
|
||||
public ChomeNSBotCommandSuggestions (MinecraftClient client) {
|
||||
this.client = client;
|
||||
|
||||
ListenerManager.addListener(this);
|
||||
}
|
||||
|
||||
public void init () {}
|
||||
|
||||
@Override
|
||||
public void coreMoved () { forceRequest(); }
|
||||
|
||||
public void forceRequest () {
|
||||
final ClientPlayerEntity player = client.player;
|
||||
if (player == null) return;
|
||||
|
||||
final String selector = UUIDUtil.selector(player.getUuid());
|
||||
|
||||
final Component component = Component
|
||||
.text(ID)
|
||||
.append(Component.text(selector));
|
||||
|
||||
final String serialized = GsonComponentSerializer.gson().serialize(component);
|
||||
|
||||
CommandCore.INSTANCE.run("tellraw @a[tag=chomens_bot] " + serialized);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void chatMessageReceived(Text message) {
|
||||
try {
|
||||
final List<Text> children = message.getSiblings();
|
||||
if (children.isEmpty()) return;
|
||||
|
||||
final Text textComponent = children.getFirst();
|
||||
if (!textComponent.getString().equals(ID)) return;
|
||||
|
||||
commands = children.subList(1, children.size())
|
||||
.stream()
|
||||
.map(
|
||||
(eachComponent) -> {
|
||||
final ChomeNSBotCommand command = new ChomeNSBotCommand(
|
||||
ChipmunkMod.CONFIG.bots.chomens.prefix + ((PlainTextContent) eachComponent.getContent()).string(),
|
||||
ChomeNSBotCommand.TrustLevel.valueOf(eachComponent.getSiblings().getFirst().getString())
|
||||
);
|
||||
|
||||
if (!Boolean.parseBoolean(eachComponent.getSiblings().get(1).getString())) return command;
|
||||
|
||||
final List<Text> subList = eachComponent.getSiblings().subList(2, eachComponent.getSiblings().size());
|
||||
|
||||
for (Text aliasComponent : subList) {
|
||||
final String alias = aliasComponent.getString();
|
||||
|
||||
command.aliases.add(alias);
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
)
|
||||
.toList();
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
}
|
334
src/main/java/land/chipmunk/chipmunkmod/modules/CommandCore.java
Normal file
334
src/main/java/land/chipmunk/chipmunkmod/modules/CommandCore.java
Normal file
|
@ -0,0 +1,334 @@
|
|||
package land.chipmunk.chipmunkmod.modules;
|
||||
|
||||
import land.chipmunk.chipmunkmod.ChipmunkMod;
|
||||
import land.chipmunk.chipmunkmod.data.BlockArea;
|
||||
import land.chipmunk.chipmunkmod.listeners.Listener;
|
||||
import land.chipmunk.chipmunkmod.listeners.ListenerManager;
|
||||
import land.chipmunk.chipmunkmod.util.misc.MathUtils;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.CommandBlock;
|
||||
import net.minecraft.block.entity.CommandBlockBlockEntity;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
||||
import net.minecraft.client.world.ClientWorld;
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
import net.minecraft.network.ClientConnection;
|
||||
import net.minecraft.network.packet.c2s.play.UpdateCommandBlockC2SPacket;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.dimension.DimensionType;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class CommandCore {
|
||||
private final MinecraftClient client;
|
||||
public boolean ready = false;
|
||||
public BlockPos origin;
|
||||
public BlockArea noPos;
|
||||
public BlockPos block;
|
||||
public BlockArea withPos;
|
||||
|
||||
private Timer timer;
|
||||
|
||||
private boolean shouldRefill = false;
|
||||
|
||||
public boolean runFillCommand = true;
|
||||
|
||||
public boolean clientPlayerEntityFilled = false;
|
||||
|
||||
public static CommandCore INSTANCE = new CommandCore(MinecraftClient.getInstance());
|
||||
|
||||
public CommandCore (MinecraftClient client) {
|
||||
this.client = client;
|
||||
reloadRelativeArea();
|
||||
}
|
||||
|
||||
public void init () {
|
||||
if (timer != null) cleanup();
|
||||
|
||||
final TimerTask task = new TimerTask() {
|
||||
public void run () {
|
||||
tick();
|
||||
}
|
||||
};
|
||||
|
||||
final TimerTask refillTask = new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (clientPlayerEntityFilled) {
|
||||
clientPlayerEntityFilled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
check();
|
||||
|
||||
if (!shouldRefill) return;
|
||||
|
||||
refill();
|
||||
|
||||
shouldRefill = false;
|
||||
}
|
||||
};
|
||||
|
||||
timer = new Timer();
|
||||
|
||||
timer.schedule(task, 50, 50);
|
||||
|
||||
timer.schedule(refillTask, 50, 1000);
|
||||
|
||||
move(client.player.getPos());
|
||||
}
|
||||
|
||||
private void tick () {
|
||||
final ClientPlayNetworkHandler networkHandler = client.getNetworkHandler();
|
||||
|
||||
if (networkHandler == null) {
|
||||
cleanup();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
reloadRelativeArea();
|
||||
}
|
||||
|
||||
public void reloadRelativeArea () {
|
||||
noPos = ChipmunkMod.CONFIG.client.core.relativeArea;
|
||||
}
|
||||
|
||||
public void check () {
|
||||
final ClientPlayNetworkHandler networkHandler = client.getNetworkHandler();
|
||||
|
||||
if (networkHandler == null || withPos == null || !ready) return;
|
||||
|
||||
try {
|
||||
for (int x = withPos.start.getX(); x <= withPos.end.getX(); x++) {
|
||||
for (int y = withPos.start.getY(); y <= withPos.end.getY(); y++) {
|
||||
for (int z = withPos.start.getZ(); z <= withPos.end.getZ(); z++) {
|
||||
final BlockPos pos = new BlockPos(x, y, z);
|
||||
|
||||
final ClientWorld world = client.world;
|
||||
|
||||
if (world == null) return;
|
||||
|
||||
final Block block = world.getBlockState(pos).getBlock();
|
||||
|
||||
if (block instanceof CommandBlock) continue;
|
||||
|
||||
shouldRefill = true;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void move (Vec3d position) {
|
||||
final ClientWorld world = client.world;
|
||||
|
||||
if (world == null || noPos == null) return;
|
||||
|
||||
final DimensionType dimension = world.getDimension();
|
||||
|
||||
origin = new BlockPos(
|
||||
((int) position.getX() / 16) * 16,
|
||||
(int) MathUtils.clamp(noPos.start.getY(), dimension.minY(), dimension.height()),
|
||||
((int) position.getZ() / 16) * 16
|
||||
);
|
||||
|
||||
withPos = new BlockArea(
|
||||
new BlockPos(
|
||||
noPos.start.getX() + origin.getX(),
|
||||
(int) MathUtils.clamp(noPos.start.getY(), dimension.minY(), dimension.height()),
|
||||
noPos.start.getZ() + origin.getZ()
|
||||
),
|
||||
new BlockPos(
|
||||
noPos.end.getX() + origin.getX(),
|
||||
(int) MathUtils.clamp(noPos.end.getY(), dimension.minY(), dimension.height()),
|
||||
noPos.end.getZ() + origin.getZ()
|
||||
)
|
||||
);
|
||||
|
||||
block = new BlockPos(withPos.start);
|
||||
refill();
|
||||
|
||||
for (Listener listener : ListenerManager.listeners) listener.coreMoved();
|
||||
if (!ready) {
|
||||
ready = true;
|
||||
|
||||
for (Listener listener : ListenerManager.listeners) listener.coreReady();
|
||||
}
|
||||
}
|
||||
|
||||
public void refill () {
|
||||
if (!runFillCommand || withPos == null) return;
|
||||
|
||||
final String command = String.format(
|
||||
KaboomCheck.INSTANCE.isKaboom ?
|
||||
"fill %s %s %s %s %s %s repeating_command_block replace" :
|
||||
"fill %s %s %s %s %s %s command_block",
|
||||
withPos.start.getX(),
|
||||
withPos.start.getY(),
|
||||
withPos.start.getZ(),
|
||||
|
||||
withPos.end.getX(),
|
||||
withPos.end.getY(),
|
||||
withPos.end.getZ()
|
||||
);
|
||||
|
||||
client.getNetworkHandler().sendChatCommand(command);
|
||||
}
|
||||
|
||||
public void incrementCurrentBlock () {
|
||||
if (withPos == null) return;
|
||||
|
||||
final BlockPos start = withPos.start;
|
||||
final BlockPos end = withPos.end;
|
||||
|
||||
if (start == null || end == null) return;
|
||||
|
||||
int x = block.getX();
|
||||
int y = block.getY();
|
||||
int z = block.getZ();
|
||||
|
||||
x++;
|
||||
|
||||
if (x > end.getX()) {
|
||||
x = start.getX();
|
||||
z++;
|
||||
}
|
||||
|
||||
if (z > end.getZ()) {
|
||||
z = start.getZ();
|
||||
y++;
|
||||
}
|
||||
|
||||
if (y > end.getY()) {
|
||||
x = start.getX();
|
||||
y = start.getY();
|
||||
z = start.getZ();
|
||||
}
|
||||
|
||||
block = new BlockPos(x, y, z);
|
||||
}
|
||||
|
||||
public void run (String command) {
|
||||
final ClientConnection connection = client.getNetworkHandler().getConnection();
|
||||
|
||||
if (block == null) return;
|
||||
|
||||
System.out.println(command);
|
||||
|
||||
if (KaboomCheck.INSTANCE.isKaboom) {
|
||||
connection.send(
|
||||
new UpdateCommandBlockC2SPacket(
|
||||
block,
|
||||
command,
|
||||
CommandBlockBlockEntity.Type.AUTO,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
)
|
||||
);
|
||||
} else {
|
||||
connection.send(
|
||||
new UpdateCommandBlockC2SPacket(
|
||||
block,
|
||||
"",
|
||||
CommandBlockBlockEntity.Type.REDSTONE,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
)
|
||||
);
|
||||
|
||||
connection.send(
|
||||
new UpdateCommandBlockC2SPacket(
|
||||
block,
|
||||
command,
|
||||
CommandBlockBlockEntity.Type.REDSTONE,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
incrementCurrentBlock();
|
||||
}
|
||||
|
||||
public CompletableFuture<NbtCompound> runTracked (String command) {
|
||||
final ClientConnection connection = client.getNetworkHandler().getConnection();
|
||||
|
||||
if (block == null) return new CompletableFuture<>();
|
||||
|
||||
if (KaboomCheck.INSTANCE.isKaboom) {
|
||||
connection.send(
|
||||
new UpdateCommandBlockC2SPacket(
|
||||
block,
|
||||
command,
|
||||
CommandBlockBlockEntity.Type.AUTO,
|
||||
true,
|
||||
false,
|
||||
true
|
||||
)
|
||||
);
|
||||
} else {
|
||||
connection.send(
|
||||
new UpdateCommandBlockC2SPacket(
|
||||
block,
|
||||
"",
|
||||
CommandBlockBlockEntity.Type.REDSTONE,
|
||||
true,
|
||||
false,
|
||||
false
|
||||
)
|
||||
);
|
||||
|
||||
connection.send(
|
||||
new UpdateCommandBlockC2SPacket(
|
||||
block,
|
||||
command,
|
||||
CommandBlockBlockEntity.Type.REDSTONE,
|
||||
true,
|
||||
false,
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
incrementCurrentBlock();
|
||||
|
||||
CompletableFuture<NbtCompound> future = new CompletableFuture<>();
|
||||
|
||||
final Timer timer = new Timer();
|
||||
|
||||
final TimerTask queryTask = new TimerTask() {
|
||||
public void run () {
|
||||
client.getNetworkHandler().getDataQueryHandler().queryBlockNbt(block, future::complete);
|
||||
|
||||
timer.cancel(); // ? Is this necesary?
|
||||
timer.purge();
|
||||
}
|
||||
};
|
||||
|
||||
timer.schedule(queryTask, 50);
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
public void cleanup () {
|
||||
if (timer == null) return;
|
||||
|
||||
timer.cancel();
|
||||
timer.purge();
|
||||
|
||||
withPos = null;
|
||||
block = null;
|
||||
ready = false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
package land.chipmunk.chipmunkmod.modules;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
public class CommandLoopManager {
|
||||
private final CommandCore core;
|
||||
public List<CommandLoop> commandLoops = new ArrayList<>();
|
||||
|
||||
public CommandLoopManager (CommandCore core) {
|
||||
this.core = core;
|
||||
}
|
||||
|
||||
public static final CommandLoopManager INSTANCE = new CommandLoopManager(CommandCore.INSTANCE);
|
||||
|
||||
public int loopCommand (String command, long interval) {
|
||||
final CommandLoop loop = new CommandLoop(this.core, command, interval);
|
||||
if (!commandLoops.add(loop)) return -1;
|
||||
return commandLoops.size() - 1;
|
||||
}
|
||||
|
||||
public boolean removeAndStop (CommandLoop loop) {
|
||||
loop.stop();
|
||||
return commandLoops.remove(loop);
|
||||
}
|
||||
|
||||
public boolean removeAndStop (int id) {
|
||||
return removeAndStop(commandLoops.get(id));
|
||||
}
|
||||
|
||||
public void clearLoops () {
|
||||
for (CommandLoop loop : this.commandLoops) loop.stop();
|
||||
commandLoops.clear();
|
||||
}
|
||||
|
||||
public void cleanup () { this.clearLoops(); }
|
||||
|
||||
public static class CommandLoop {
|
||||
public CommandCore core;
|
||||
public String command;
|
||||
public long interval;
|
||||
private Timer timer;
|
||||
|
||||
public CommandLoop (CommandCore core, String command, long interval) {
|
||||
this.core = core;
|
||||
this.command = command;
|
||||
this.interval = interval;
|
||||
this.timer = new Timer();
|
||||
timer.schedule(this.createTimerTask(), interval, interval);
|
||||
}
|
||||
|
||||
private long interval (long interval) {
|
||||
if (timer == null) throw new IllegalStateException("Attempted to set the interval of a stopped command loop");
|
||||
|
||||
timer.cancel();
|
||||
timer.purge();
|
||||
|
||||
this.interval = interval;
|
||||
this.timer = new Timer();
|
||||
timer.schedule(this.createTimerTask(), interval, interval);
|
||||
|
||||
return interval;
|
||||
}
|
||||
|
||||
private void stop () {
|
||||
timer.cancel();
|
||||
timer.purge();
|
||||
}
|
||||
|
||||
public TimerTask createTimerTask () {
|
||||
return new TimerTask() {
|
||||
@Override
|
||||
public void run () {
|
||||
core.run(command);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
126
src/main/java/land/chipmunk/chipmunkmod/modules/CustomChat.java
Normal file
126
src/main/java/land/chipmunk/chipmunkmod/modules/CustomChat.java
Normal file
|
@ -0,0 +1,126 @@
|
|||
package land.chipmunk.chipmunkmod.modules;
|
||||
|
||||
import com.google.common.hash.Hashing;
|
||||
import com.google.gson.JsonElement;
|
||||
import land.chipmunk.chipmunkmod.ChipmunkMod;
|
||||
import land.chipmunk.chipmunkmod.util.player.ChatUtils;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
||||
import net.minecraft.client.network.ClientPlayerEntity;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import static land.chipmunk.chipmunkmod.ChipmunkMod.MCInstance;
|
||||
|
||||
public class CustomChat {
|
||||
public static final CustomChat INSTANCE = new CustomChat(MCInstance);
|
||||
private final MinecraftClient client;
|
||||
|
||||
public boolean enabled = true;
|
||||
public String format;
|
||||
private Timer timer;
|
||||
private int total = 0;
|
||||
|
||||
public CustomChat(MinecraftClient client) {
|
||||
this.client = client;
|
||||
reloadFormat();
|
||||
}
|
||||
|
||||
public void init() {
|
||||
final TimerTask task = new TimerTask() {
|
||||
public void run () {
|
||||
tick();
|
||||
}
|
||||
};
|
||||
|
||||
resetTotal();
|
||||
|
||||
timer = new Timer();
|
||||
timer.schedule(task, 0, 50);
|
||||
}
|
||||
|
||||
private void tick() {
|
||||
final ClientPlayNetworkHandler networkHandler = client.getNetworkHandler();
|
||||
|
||||
if (networkHandler != null) return;
|
||||
|
||||
resetTotal();
|
||||
cleanup();
|
||||
}
|
||||
|
||||
public void resetTotal() {
|
||||
total = 0;
|
||||
}
|
||||
|
||||
private void cleanup() {
|
||||
if (timer == null) return;
|
||||
|
||||
timer.cancel();
|
||||
timer.purge();
|
||||
}
|
||||
|
||||
public void reloadFormat() {
|
||||
final JsonElement formatElement = ChipmunkMod.CONFIG.client.customChat.format;
|
||||
|
||||
if (formatElement == null) {
|
||||
format = "{\"translate\":\"chat.type.text\",\"with\":[\"USERNAME\",\"MESSAGE\"]}";
|
||||
} else {
|
||||
// First deserialize the JsonElement to a Component
|
||||
Component formatComponent = GsonComponentSerializer.gson().deserializeFromTree(formatElement);
|
||||
// Then serialize it back to a string format
|
||||
format = GsonComponentSerializer.gson().serialize(formatComponent);
|
||||
}
|
||||
}
|
||||
|
||||
public void chat(String message) {
|
||||
final ClientPlayerEntity player = client.player;
|
||||
|
||||
if (!enabled || !player.hasPermissionLevel(2) || !player.isCreative()) {
|
||||
Chat.sendChatMessage(message, true);
|
||||
return;
|
||||
}
|
||||
|
||||
final String username = MCInstance.getSession().getUsername();
|
||||
final String sanitizedMessage = message
|
||||
.replace("\\", "\\\\")
|
||||
.replace("\"", "\\\"");
|
||||
|
||||
final LegacyComponentSerializer serializer = LegacyComponentSerializer.legacyAmpersand();
|
||||
final String randomized = String.valueOf(Math.random());
|
||||
|
||||
final Component deserialized = serializer.deserialize(message);
|
||||
final String messageWithColor = GsonComponentSerializer.gson().serialize(deserialized).replace("MESSAGE", randomized);
|
||||
|
||||
final String key = ChipmunkMod.CONFIG.bots.chomens.formatKey;
|
||||
final String chomeNSKey = key != null ?
|
||||
Hashing.sha256()
|
||||
.hashString(key + total, StandardCharsets.UTF_8)
|
||||
.toString()
|
||||
.substring(0, 8) :
|
||||
"";
|
||||
total++;
|
||||
|
||||
try {
|
||||
// Process the format string
|
||||
String sanitizedFormat = format
|
||||
.replace("USERNAME", username)
|
||||
.replace("HASH", chomeNSKey)
|
||||
.replace("{\"text\":\"MESSAGE\"}", messageWithColor)
|
||||
.replace("\"extra\":[\"MESSAGE\"]", "\"extra\":[" + messageWithColor + "]")
|
||||
.replace("MESSAGE", sanitizedMessage.replaceAll("&.", ""));
|
||||
|
||||
// Remove any remaining randomized placeholders
|
||||
sanitizedFormat = sanitizedFormat.replace(randomized, "");
|
||||
|
||||
// Send the formatted message
|
||||
String command = (KaboomCheck.INSTANCE.isKaboom ? "minecraft:tellraw @a " : "tellraw @a ") + sanitizedFormat;
|
||||
CommandCore.INSTANCE.run(command);
|
||||
} catch (Exception err) {
|
||||
ChatUtils.errorPrefix("CustomChat", "Something went wrong: [hl]%s", err.toString() );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
package land.chipmunk.chipmunkmod.modules;
|
||||
|
||||
import com.mojang.brigadier.suggestion.Suggestion;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import land.chipmunk.chipmunkmod.listeners.Listener;
|
||||
import land.chipmunk.chipmunkmod.listeners.ListenerManager;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
||||
import net.minecraft.network.packet.s2c.play.CommandSuggestionsS2CPacket;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class KaboomCheck extends Listener {
|
||||
public boolean isKaboom = false;
|
||||
|
||||
private Timer timer = null;
|
||||
|
||||
private final MinecraftClient client;
|
||||
|
||||
public static final KaboomCheck INSTANCE = new KaboomCheck(MinecraftClient.getInstance());
|
||||
|
||||
public KaboomCheck (MinecraftClient client) {
|
||||
this.client = client;
|
||||
|
||||
ListenerManager.addListener(this);
|
||||
}
|
||||
|
||||
public void init () {}
|
||||
|
||||
public void onJoin () {
|
||||
final TimerTask task = new TimerTask() {
|
||||
public void run () {
|
||||
tick();
|
||||
}
|
||||
};
|
||||
|
||||
if (timer != null) cleanup();
|
||||
|
||||
timer = new Timer();
|
||||
|
||||
timer.schedule(task, 50, 50);
|
||||
|
||||
check();
|
||||
}
|
||||
|
||||
private void tick () {
|
||||
final ClientPlayNetworkHandler networkHandler = client.getNetworkHandler();
|
||||
|
||||
if (networkHandler == null) cleanup();
|
||||
}
|
||||
|
||||
private void check () {
|
||||
final CompletableFuture<CommandSuggestionsS2CPacket> future = TabComplete.INSTANCE.complete("/ver ");
|
||||
|
||||
future.thenApply((packet) -> {
|
||||
final Suggestions suggestions = packet.getSuggestions();
|
||||
|
||||
for (int i = 0; i < suggestions.getList().size(); i++) {
|
||||
final Suggestion suggestion = suggestions.getList().get(i);
|
||||
|
||||
if (suggestion.getText().equals("Extras")) {
|
||||
isKaboom = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void cleanup () {
|
||||
if (timer == null) return;
|
||||
|
||||
isKaboom = false;
|
||||
|
||||
timer.purge();
|
||||
timer.cancel();
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
package land.chipmunk.chipmunkmod.modules;
|
||||
|
||||
import com.google.common.hash.Hashing;
|
||||
import land.chipmunk.chipmunkmod.ChipmunkMod;
|
||||
import land.chipmunk.chipmunkmod.listeners.Listener;
|
||||
import land.chipmunk.chipmunkmod.listeners.ListenerManager;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.minecraft.text.PlainTextContent;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.text.TextContent;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
public class LambdaAuth extends Listener {
|
||||
public final String ID = "lambda_verify";
|
||||
|
||||
public static final LambdaAuth INSTANCE = new LambdaAuth();
|
||||
|
||||
public LambdaAuth() {
|
||||
ListenerManager.addListener(this);
|
||||
}
|
||||
|
||||
public void init () {}
|
||||
|
||||
@Override
|
||||
public void chatMessageReceived(Text text) {
|
||||
final String authKey = ChipmunkMod.CONFIG.bots.chomens.authKey;
|
||||
if (authKey == null) return;
|
||||
|
||||
final TextContent message = text.getContent();
|
||||
if (!(message instanceof PlainTextContent)) return;
|
||||
|
||||
final String ID = ((PlainTextContent) message).string();
|
||||
if (!ID.equals(this.ID)) return;
|
||||
|
||||
final List<Text> children = text.getSiblings();
|
||||
|
||||
if (children.size() != 2) return;
|
||||
if (!(children.getFirst().getContent() instanceof PlainTextContent)) return;
|
||||
|
||||
final String hash = ((PlainTextContent) children.getFirst().getContent()).string();
|
||||
final long time = System.currentTimeMillis() / 10_000;
|
||||
|
||||
final String actual = Hashing.sha256()
|
||||
// very pro hash input
|
||||
.hashString(authKey + time, StandardCharsets.UTF_8)
|
||||
.toString()
|
||||
.substring(0, 8);
|
||||
|
||||
if (!hash.equals(actual)) return;
|
||||
if (!(children.get(1).getContent() instanceof PlainTextContent)) return;
|
||||
|
||||
final String selector = ((PlainTextContent) children.get(1).getContent()).string();
|
||||
|
||||
final String toSendHash = Hashing.sha256()
|
||||
// very pro hash input
|
||||
.hashString(authKey + authKey + time + time, StandardCharsets.UTF_8)
|
||||
.toString()
|
||||
.substring(0, 8);
|
||||
|
||||
final Component toSend = Component.text(ID)
|
||||
.append(Component.text(toSendHash));
|
||||
|
||||
final String toSendString = GsonComponentSerializer.gson().serialize(toSend);
|
||||
|
||||
CommandCore.INSTANCE.run("minecraft:tellraw " + selector + " " + toSendString);
|
||||
CustomChat.INSTANCE.resetTotal();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
package land.chipmunk.chipmunkmod.modules;
|
||||
|
||||
import land.chipmunk.chipmunkmod.ChipmunkMod;
|
||||
import land.chipmunk.chipmunkmod.data.LambdaBotCommand;
|
||||
import land.chipmunk.chipmunkmod.listeners.Listener;
|
||||
import land.chipmunk.chipmunkmod.listeners.ListenerManager;
|
||||
import land.chipmunk.chipmunkmod.util.player.UUIDUtil;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientPlayerEntity;
|
||||
import net.minecraft.text.PlainTextContent;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static land.chipmunk.chipmunkmod.ChipmunkMod.MCInstance;
|
||||
|
||||
public class LambdaCommandSuggestions extends Listener {
|
||||
public static final String ID = "lambda_request_command_suggestion";
|
||||
|
||||
public static LambdaCommandSuggestions INSTANCE = new LambdaCommandSuggestions(MCInstance);
|
||||
private final MinecraftClient client;
|
||||
|
||||
public List<LambdaBotCommand> commands = new ArrayList<>();
|
||||
|
||||
public LambdaCommandSuggestions(MinecraftClient client) {
|
||||
this.client = client;
|
||||
|
||||
ListenerManager.addListener(this);
|
||||
}
|
||||
|
||||
public void init () {}
|
||||
|
||||
@Override
|
||||
public void coreMoved () { forceRequest(); }
|
||||
|
||||
public void forceRequest () {
|
||||
final ClientPlayerEntity player = client.player;
|
||||
if (player == null) return;
|
||||
|
||||
final String selector = UUIDUtil.selector(player.getUuid());
|
||||
|
||||
final Component component = Component
|
||||
.text(ID)
|
||||
.append(Component.text(selector));
|
||||
|
||||
final String serialized = GsonComponentSerializer.gson().serialize(component);
|
||||
|
||||
CommandCore.INSTANCE.run("tellraw @a[team=LambdaBot] " + serialized); // I use team cuz i can have selfcare
|
||||
}
|
||||
|
||||
@Override
|
||||
public void chatMessageReceived(Text message) {
|
||||
try {
|
||||
final List<Text> children = message.getSiblings();
|
||||
if (children.isEmpty()) return;
|
||||
|
||||
final Text textComponent = children.getFirst();
|
||||
if (!textComponent.getString().equals(ID)) return;
|
||||
|
||||
commands = children.subList(1, children.size())
|
||||
.stream()
|
||||
.map(
|
||||
(eachComponent) -> {
|
||||
final LambdaBotCommand command = new LambdaBotCommand(
|
||||
ChipmunkMod.CONFIG.bots.lambda.prefix + ((PlainTextContent) eachComponent.getContent()).string(),
|
||||
LambdaBotCommand.AccessLevel.valueOf(eachComponent.getSiblings().getFirst().getString())
|
||||
);
|
||||
|
||||
if (!Boolean.parseBoolean(eachComponent.getSiblings().get(1).getString())) return command;
|
||||
|
||||
final List<Text> subList = eachComponent.getSiblings().subList(2, eachComponent.getSiblings().size());
|
||||
|
||||
for (Text aliasComponent : subList) {
|
||||
final String alias = aliasComponent.getString();
|
||||
|
||||
command.aliases.add(alias);
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
)
|
||||
.toList();
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
}
|
245
src/main/java/land/chipmunk/chipmunkmod/modules/Players.java
Normal file
245
src/main/java/land/chipmunk/chipmunkmod/modules/Players.java
Normal file
|
@ -0,0 +1,245 @@
|
|||
package land.chipmunk.chipmunkmod.modules;
|
||||
|
||||
import com.mojang.brigadier.Message;
|
||||
import com.mojang.brigadier.suggestion.Suggestion;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import land.chipmunk.chipmunkmod.data.MutablePlayerListEntry;
|
||||
import land.chipmunk.chipmunkmod.listeners.Listener;
|
||||
import land.chipmunk.chipmunkmod.listeners.ListenerManager;
|
||||
import land.chipmunk.chipmunkmod.mixin.ClientPlayNetworkHandlerAccessor;
|
||||
import land.chipmunk.chipmunkmod.mixin.PlayerListEntryAccessor;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.PlayerListEntry;
|
||||
import net.minecraft.network.packet.Packet;
|
||||
import net.minecraft.network.packet.s2c.play.CommandSuggestionsS2CPacket;
|
||||
import net.minecraft.network.packet.s2c.play.PlayerListS2CPacket;
|
||||
import net.minecraft.network.packet.s2c.play.PlayerRemoveS2CPacket;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import static land.chipmunk.chipmunkmod.util.network.ServerUtil.serverHasCommand;
|
||||
|
||||
public class Players extends Listener {
|
||||
public List<MutablePlayerListEntry> list = new ArrayList<>();
|
||||
|
||||
public static Players INSTANCE = new Players(MinecraftClient.getInstance());
|
||||
|
||||
private final MinecraftClient client;
|
||||
|
||||
public Players (MinecraftClient client) {
|
||||
this.client = client;
|
||||
ListenerManager.addListener(this);
|
||||
|
||||
TabComplete.INSTANCE.init();
|
||||
}
|
||||
|
||||
public void init () {}
|
||||
|
||||
@Override
|
||||
public void packetReceived (Packet<?> packet) {
|
||||
if (packet instanceof PlayerListS2CPacket) packetReceived((PlayerListS2CPacket) packet);
|
||||
else if (packet instanceof PlayerRemoveS2CPacket) packetReceived((PlayerRemoveS2CPacket) packet);
|
||||
}
|
||||
|
||||
public void packetReceived (PlayerListS2CPacket packet) {
|
||||
try {
|
||||
for (PlayerListS2CPacket.Action action : packet.getActions()) {
|
||||
for (PlayerListS2CPacket.Entry entry : packet.getEntries()) {
|
||||
if (action == PlayerListS2CPacket.Action.ADD_PLAYER) addPlayer(entry);
|
||||
// else if (action == PlayerListS2CPacket.Action.INITIALIZE_CHAT) initializeChat(entry);
|
||||
else if (action == PlayerListS2CPacket.Action.UPDATE_GAME_MODE) updateGamemode(entry);
|
||||
// else if (action == PlayerListS2CPacket.Action.UPDATE_LISTED) updateListed(entry);
|
||||
else if (action == PlayerListS2CPacket.Action.UPDATE_LATENCY) updateLatency(entry);
|
||||
else if (action == PlayerListS2CPacket.Action.UPDATE_DISPLAY_NAME) updateDisplayName(entry);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
//e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void packetReceived (PlayerRemoveS2CPacket packet) {
|
||||
try {
|
||||
for (UUID uuid : packet.profileIds()) {
|
||||
removePlayer(uuid);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
//e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public final MutablePlayerListEntry getEntry (UUID uuid) {
|
||||
try {
|
||||
for (MutablePlayerListEntry candidate : list) {
|
||||
if (candidate.profile.getId().equals(uuid)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
//e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public final MutablePlayerListEntry getEntry (String username) {
|
||||
for (MutablePlayerListEntry candidate : list) {
|
||||
if (candidate.profile.getName().equals(username)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public final MutablePlayerListEntry getEntry (Text displayName) {
|
||||
for (MutablePlayerListEntry candidate : list) {
|
||||
if (candidate.displayName != null && candidate.displayName.equals(displayName)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private MutablePlayerListEntry getEntry (PlayerListS2CPacket.Entry other) {
|
||||
return getEntry(other.profileId());
|
||||
}
|
||||
|
||||
private void addPlayer (PlayerListS2CPacket.Entry newEntry) {
|
||||
try {
|
||||
final MutablePlayerListEntry duplicate = getEntry(newEntry);
|
||||
if (duplicate != null) {
|
||||
removeFromPlayerList(duplicate.profile.getId());
|
||||
list.remove(duplicate);
|
||||
}
|
||||
|
||||
final MutablePlayerListEntry entry = new MutablePlayerListEntry(newEntry);
|
||||
|
||||
list.add(entry);
|
||||
} catch (Exception e) {
|
||||
//e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateGamemode (PlayerListS2CPacket.Entry newEntry) {
|
||||
try {
|
||||
final MutablePlayerListEntry target = getEntry(newEntry);
|
||||
if (target == null) return;
|
||||
|
||||
target.gamemode = newEntry.gameMode();
|
||||
|
||||
final ClientPlayNetworkHandlerAccessor accessor = ((ClientPlayNetworkHandlerAccessor) MinecraftClient.getInstance().getNetworkHandler());
|
||||
|
||||
if (accessor == null) return;
|
||||
|
||||
final PlayerListEntryAccessor entryAccessor = (PlayerListEntryAccessor) accessor.playerListEntries().get(newEntry.profileId());
|
||||
|
||||
entryAccessor.setGameMode(newEntry.gameMode());
|
||||
} catch (Exception e) {
|
||||
//e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateLatency (PlayerListS2CPacket.Entry newEntry) {
|
||||
final MutablePlayerListEntry target = getEntry(newEntry);
|
||||
if (target == null) return;
|
||||
|
||||
target.latency = newEntry.latency();
|
||||
|
||||
final ClientPlayNetworkHandlerAccessor accessor = ((ClientPlayNetworkHandlerAccessor) MinecraftClient.getInstance().getNetworkHandler());
|
||||
|
||||
if (accessor == null) return;
|
||||
|
||||
final PlayerListEntryAccessor entryAccessor = (PlayerListEntryAccessor) accessor.playerListEntries().get(newEntry.profileId());
|
||||
|
||||
entryAccessor.setLatency(newEntry.latency());
|
||||
}
|
||||
|
||||
private void updateDisplayName (PlayerListS2CPacket.Entry newEntry) {
|
||||
final MutablePlayerListEntry target = getEntry(newEntry);
|
||||
if (target == null) return;
|
||||
|
||||
target.displayName = newEntry.displayName();
|
||||
|
||||
final ClientPlayNetworkHandlerAccessor accessor = ((ClientPlayNetworkHandlerAccessor) MinecraftClient.getInstance().getNetworkHandler());
|
||||
|
||||
if (accessor == null) return;
|
||||
|
||||
accessor.playerListEntries().get(newEntry.profileId()).setDisplayName(newEntry.displayName());
|
||||
}
|
||||
|
||||
private void removePlayer (UUID uuid) {
|
||||
try {
|
||||
final MutablePlayerListEntry target = getEntry(uuid);
|
||||
if (target == null) return;
|
||||
|
||||
if (!serverHasCommand("scoreboard")) {
|
||||
removeFromPlayerList(uuid);
|
||||
return;
|
||||
}
|
||||
|
||||
final CompletableFuture<CommandSuggestionsS2CPacket> future = TabComplete.INSTANCE.complete("/scoreboard players add ");
|
||||
|
||||
if (future == null) return;
|
||||
|
||||
future.thenApply(packet -> {
|
||||
final Suggestions matches = packet.getSuggestions();
|
||||
final String username = target.profile.getName();
|
||||
|
||||
for (int i = 0; i < matches.getList().size(); i++) {
|
||||
final Suggestion suggestion = matches.getList().get(i);
|
||||
|
||||
final Message tooltip = suggestion.getTooltip();
|
||||
if (tooltip != null || !suggestion.getText().equals(username)) continue;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
list.remove(target);
|
||||
|
||||
removeFromPlayerList(uuid);
|
||||
|
||||
for (MutablePlayerListEntry entry : list) {
|
||||
if (!entry.profile.getId().equals(uuid)) continue;
|
||||
|
||||
addToPlayerList(new PlayerListEntry(entry.profile, false));
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
} catch (Exception e) {
|
||||
//e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void addToPlayerList (PlayerListEntry entry) {
|
||||
client.getSocialInteractionsManager().setPlayerOnline(entry);
|
||||
|
||||
final ClientPlayNetworkHandlerAccessor accessor = ((ClientPlayNetworkHandlerAccessor) MinecraftClient.getInstance().getNetworkHandler());
|
||||
|
||||
if (accessor == null) return;
|
||||
|
||||
accessor.playerListEntries().put(entry.getProfile().getId(), entry);
|
||||
|
||||
accessor.listedPlayerListEntries().add(entry);
|
||||
}
|
||||
|
||||
private void removeFromPlayerList (UUID uuid) {
|
||||
client.getSocialInteractionsManager().setPlayerOffline(uuid);
|
||||
|
||||
final ClientPlayNetworkHandlerAccessor accessor = ((ClientPlayNetworkHandlerAccessor) MinecraftClient.getInstance().getNetworkHandler());
|
||||
|
||||
if (accessor == null) return;
|
||||
|
||||
final PlayerListEntry playerListEntry = accessor.playerListEntries().remove(uuid);
|
||||
|
||||
if (playerListEntry != null) {
|
||||
accessor.listedPlayerListEntries().remove(playerListEntry);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
package land.chipmunk.chipmunkmod.modules;
|
||||
|
||||
import com.google.common.hash.Hashing;
|
||||
import land.chipmunk.chipmunkmod.ChipmunkMod;
|
||||
import land.chipmunk.chipmunkmod.listeners.Listener;
|
||||
import land.chipmunk.chipmunkmod.listeners.ListenerManager;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.minecraft.text.PlainTextContent;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.text.TextContent;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
public class QBotAuth extends Listener {
|
||||
public final String ID = "qbot_verify";
|
||||
|
||||
public static final QBotAuth INSTANCE = new QBotAuth();
|
||||
|
||||
public QBotAuth () {
|
||||
ListenerManager.addListener(this);
|
||||
}
|
||||
|
||||
public void init () {}
|
||||
|
||||
@Override
|
||||
public void chatMessageReceived(Text text) {
|
||||
final String authKey = ChipmunkMod.CONFIG.bots.chomens.authKey;
|
||||
if (authKey == null) return;
|
||||
|
||||
final TextContent message = text.getContent();
|
||||
if (!(message instanceof PlainTextContent)) return;
|
||||
|
||||
final String ID = ((PlainTextContent) message).string();
|
||||
if (!ID.equals(this.ID)) return;
|
||||
|
||||
final List<Text> children = text.getSiblings();
|
||||
|
||||
if (children.size() != 2) return;
|
||||
if (!(children.getFirst().getContent() instanceof PlainTextContent)) return;
|
||||
|
||||
final String hash = ((PlainTextContent) children.getFirst().getContent()).string();
|
||||
final long time = System.currentTimeMillis() / 10_000;
|
||||
|
||||
final String actual = Hashing.sha256()
|
||||
// very pro hash input
|
||||
.hashString(authKey + time, StandardCharsets.UTF_8)
|
||||
.toString()
|
||||
.substring(0, 8);
|
||||
|
||||
if (!hash.equals(actual)) return;
|
||||
if (!(children.get(1).getContent() instanceof PlainTextContent)) return;
|
||||
|
||||
final String selector = ((PlainTextContent) children.get(1).getContent()).string();
|
||||
|
||||
final String toSendHash = Hashing.sha256()
|
||||
// very pro hash input
|
||||
.hashString(authKey + authKey + time + time, StandardCharsets.UTF_8)
|
||||
.toString()
|
||||
.substring(0, 8);
|
||||
|
||||
final Component toSend = Component.text(ID)
|
||||
.append(Component.text(toSendHash));
|
||||
|
||||
final String toSendString = GsonComponentSerializer.gson().serialize(toSend);
|
||||
|
||||
CommandCore.INSTANCE.run("minecraft:tellraw " + selector + " " + toSendString);
|
||||
CustomChat.INSTANCE.resetTotal();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
package land.chipmunk.chipmunkmod.modules;
|
||||
|
||||
import land.chipmunk.chipmunkmod.ChipmunkMod;
|
||||
import land.chipmunk.chipmunkmod.data.QBotCommand;
|
||||
import land.chipmunk.chipmunkmod.listeners.Listener;
|
||||
import land.chipmunk.chipmunkmod.listeners.ListenerManager;
|
||||
import land.chipmunk.chipmunkmod.util.player.UUIDUtil;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientPlayerEntity;
|
||||
import net.minecraft.text.PlainTextContent;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static land.chipmunk.chipmunkmod.ChipmunkMod.MCInstance;
|
||||
|
||||
public class QBotCommandSuggestions extends Listener {
|
||||
public static final String ID = "qbot_request_command_suggestion";
|
||||
|
||||
public static QBotCommandSuggestions INSTANCE = new QBotCommandSuggestions(MCInstance);
|
||||
private final MinecraftClient client;
|
||||
|
||||
public List<QBotCommand> commands = new ArrayList<>();
|
||||
|
||||
public QBotCommandSuggestions (MinecraftClient client) {
|
||||
this.client = client;
|
||||
|
||||
ListenerManager.addListener(this);
|
||||
}
|
||||
|
||||
public void init () {}
|
||||
|
||||
@Override
|
||||
public void coreMoved () { forceRequest(); }
|
||||
|
||||
public void forceRequest () {
|
||||
final ClientPlayerEntity player = client.player;
|
||||
if (player == null) return;
|
||||
|
||||
final String selector = UUIDUtil.selector(player.getUuid());
|
||||
|
||||
final Component component = Component
|
||||
.text(ID)
|
||||
.append(Component.text(selector));
|
||||
|
||||
final String serialized = GsonComponentSerializer.gson().serialize(component);
|
||||
|
||||
CommandCore.INSTANCE.run("tellraw @a[tag=qbot] " + serialized);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void chatMessageReceived(Text message) {
|
||||
try {
|
||||
final List<Text> children = message.getSiblings();
|
||||
if (children.isEmpty()) return;
|
||||
|
||||
final Text textComponent = children.getFirst();
|
||||
if (!textComponent.getString().equals(ID)) return;
|
||||
|
||||
commands = children.subList(1, children.size())
|
||||
.stream()
|
||||
.map(
|
||||
(eachComponent) -> {
|
||||
final QBotCommand command = new QBotCommand(
|
||||
ChipmunkMod.CONFIG.bots.qbot.prefix + ((PlainTextContent) eachComponent.getContent()).string(),
|
||||
QBotCommand.TrustLevel.valueOf(eachComponent.getSiblings().getFirst().getString())
|
||||
);
|
||||
|
||||
if (!Boolean.parseBoolean(eachComponent.getSiblings().get(1).getString())) return command;
|
||||
|
||||
final List<Text> subList = eachComponent.getSiblings().subList(2, eachComponent.getSiblings().size());
|
||||
|
||||
for (Text aliasComponent : subList) {
|
||||
final String alias = aliasComponent.getString();
|
||||
|
||||
command.aliases.add(alias);
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
)
|
||||
.toList();
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
}
|
159
src/main/java/land/chipmunk/chipmunkmod/modules/RainbowName.java
Normal file
159
src/main/java/land/chipmunk/chipmunkmod/modules/RainbowName.java
Normal file
|
@ -0,0 +1,159 @@
|
|||
package land.chipmunk.chipmunkmod.modules;
|
||||
|
||||
import land.chipmunk.chipmunkmod.util.misc.ColorUtils;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
public class RainbowName {
|
||||
private final MinecraftClient client;
|
||||
|
||||
public static final RainbowName INSTANCE = new RainbowName(MinecraftClient.getInstance());
|
||||
|
||||
private static final String BUKKIT_COLOR_CODES = "123456789abcdefklmorx";
|
||||
private static final String TEAM_NAME_CHARACTERS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-.+";
|
||||
|
||||
private Timer timer = null;
|
||||
|
||||
public boolean enabled = false;
|
||||
|
||||
private String[] team;
|
||||
|
||||
public String displayName;
|
||||
|
||||
private int startHue = 0;
|
||||
|
||||
public void init () {
|
||||
final TimerTask task = new TimerTask() {
|
||||
public void run () {
|
||||
tick();
|
||||
}
|
||||
};
|
||||
|
||||
if (timer != null) cleanup();
|
||||
|
||||
timer = new Timer();
|
||||
timer.schedule(task, 0, 50);
|
||||
}
|
||||
|
||||
private String[] generateColorCodes(int length) {
|
||||
String SALTCHARS = BUKKIT_COLOR_CODES;
|
||||
StringBuilder salt = new StringBuilder();
|
||||
Random rnd = new Random();
|
||||
while (salt.length() < length) { // length of the random string.
|
||||
int index = (int) (rnd.nextFloat() * SALTCHARS.length());
|
||||
salt.append(SALTCHARS.charAt(index));
|
||||
}
|
||||
String saltStr = salt.toString();
|
||||
return saltStr.split("");
|
||||
}
|
||||
|
||||
private String generateUsername (String[] codes) {
|
||||
StringBuilder string = new StringBuilder();
|
||||
for (String code : codes) string.append("&").append(code);
|
||||
return string.toString();
|
||||
}
|
||||
|
||||
private String generateUsername (int _codes) {
|
||||
StringBuilder string = new StringBuilder();
|
||||
|
||||
final String[] codes = generateColorCodes(_codes);
|
||||
|
||||
for (String code : codes) string.append("&").append(code);
|
||||
return string.toString();
|
||||
}
|
||||
|
||||
private String generateUsername (char[] codes, char character) {
|
||||
StringBuilder string = new StringBuilder();
|
||||
for (char code : codes) string.append(character + code);
|
||||
return string.toString();
|
||||
}
|
||||
|
||||
private String[] generateTeamName () {
|
||||
String SALTCHARS = TEAM_NAME_CHARACTERS;
|
||||
StringBuilder salt = new StringBuilder();
|
||||
Random rnd = new Random();
|
||||
while (salt.length() < TEAM_NAME_CHARACTERS.length()) { // length of the random string.
|
||||
int index = (int) (rnd.nextFloat() * SALTCHARS.length());
|
||||
salt.append(SALTCHARS.charAt(index));
|
||||
}
|
||||
String saltStr = salt.toString();
|
||||
return saltStr.split("");
|
||||
}
|
||||
|
||||
public void enable () {
|
||||
final String[] colorCodes = generateColorCodes(8);
|
||||
client.getNetworkHandler().sendChatCommand("extras:username " + generateUsername(colorCodes));
|
||||
|
||||
team = generateTeamName();
|
||||
|
||||
CommandCore.INSTANCE.run("minecraft:team add " + String.join("", team));
|
||||
|
||||
CommandCore.INSTANCE.run("minecraft:execute as " + client.getNetworkHandler().getProfile().getId() + " run team join " + String.join("", team));
|
||||
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
public void disable () {
|
||||
client.getNetworkHandler().sendChatCommand("extras:username " + client.getSession().getUsername());
|
||||
|
||||
CommandCore.INSTANCE.run("minecraft:team remove " + String.join("", team));
|
||||
team = null;
|
||||
|
||||
CommandCore.INSTANCE.run("essentials:nick " + client.getSession().getUsername() + " off");
|
||||
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
public RainbowName (MinecraftClient client) {
|
||||
this.client = client;
|
||||
this.displayName = client.getSession().getUsername();
|
||||
}
|
||||
|
||||
private void tick () {
|
||||
try {
|
||||
final ClientPlayNetworkHandler networkHandler = client.getNetworkHandler();
|
||||
|
||||
if (networkHandler == null) {
|
||||
cleanup();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!enabled) return;
|
||||
|
||||
int hue = startHue;
|
||||
int increment = (int) (360.0 / Math.max(displayName.length(), 20));
|
||||
|
||||
Component component = Component.empty();
|
||||
StringBuilder essentialsNickname = new StringBuilder();
|
||||
|
||||
for (char character : displayName.toCharArray()) {
|
||||
String color = String.format("%06x", ColorUtils.hsvToRgb(hue, 100, 100));
|
||||
component = component.append(Component.text(character).color(TextColor.fromHexString("#" + color)));
|
||||
essentialsNickname.append("\u00a7#").append(color).append(character != ' ' ? character : '_');
|
||||
hue = (hue + increment) % 360;
|
||||
}
|
||||
|
||||
CommandCore.INSTANCE.run("minecraft:team modify " + String.join("", team) + " prefix " + GsonComponentSerializer.gson().serialize(component));
|
||||
CommandCore.INSTANCE.run("essentials:nick " + client.getSession().getUsername() + " " + essentialsNickname);
|
||||
|
||||
startHue = (startHue + increment) % 360;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void cleanup () {
|
||||
if (timer == null) return;
|
||||
|
||||
timer.cancel();
|
||||
timer.purge();
|
||||
timer = null;
|
||||
}
|
||||
}
|
159
src/main/java/land/chipmunk/chipmunkmod/modules/SelfCare.java
Normal file
159
src/main/java/land/chipmunk/chipmunkmod/modules/SelfCare.java
Normal file
|
@ -0,0 +1,159 @@
|
|||
package land.chipmunk.chipmunkmod.modules;
|
||||
|
||||
import land.chipmunk.chipmunkmod.ChipmunkMod;
|
||||
import land.chipmunk.chipmunkmod.listeners.Listener;
|
||||
import land.chipmunk.chipmunkmod.listeners.ListenerManager;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
||||
import net.minecraft.client.network.ClientPlayerEntity;
|
||||
import net.minecraft.network.packet.Packet;
|
||||
import net.minecraft.network.packet.s2c.play.GameJoinS2CPacket;
|
||||
import net.minecraft.network.packet.s2c.play.GameStateChangeS2CPacket;
|
||||
import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import static land.chipmunk.chipmunkmod.util.network.ServerUtil.serverHasCommand;
|
||||
|
||||
public class SelfCare extends Listener {
|
||||
private final MinecraftClient client;
|
||||
public final long interval;
|
||||
public final long chatInterval;
|
||||
|
||||
public boolean opEnabled = true;
|
||||
public boolean gamemodeEnabled = true;
|
||||
public boolean cspyEnabled = true;
|
||||
public boolean icuEnabled = false;
|
||||
|
||||
private int gameMode;
|
||||
|
||||
public String skin;
|
||||
|
||||
private Timer timer;
|
||||
private Timer chatTimer;
|
||||
|
||||
private boolean cspy = false;
|
||||
public boolean hasSkin = false;
|
||||
|
||||
private int positionPacketsPerSecond = 0;
|
||||
|
||||
public static final SelfCare INSTANCE = new SelfCare(MinecraftClient.getInstance(), 70L, 500L); // make the intervals in config?
|
||||
|
||||
public SelfCare (MinecraftClient client, long interval, long chatInterval) {
|
||||
this.client = client;
|
||||
this.interval = interval;
|
||||
this.chatInterval = chatInterval;
|
||||
|
||||
this.skin = ChipmunkMod.CONFIG.client.autoSkinUsername == null ? "off" : ChipmunkMod.CONFIG.client.autoSkinUsername; // can this be null?
|
||||
|
||||
ListenerManager.addListener(this);
|
||||
}
|
||||
|
||||
public void init () {}
|
||||
|
||||
public void onJoin () {
|
||||
final TimerTask task = new TimerTask() {
|
||||
public void run () {
|
||||
tick();
|
||||
}
|
||||
};
|
||||
|
||||
final TimerTask chatTask = new TimerTask() {
|
||||
public void run () {
|
||||
chatTick();
|
||||
}
|
||||
};
|
||||
|
||||
timer = new Timer();
|
||||
chatTimer = new Timer();
|
||||
|
||||
timer.schedule(task, interval, interval);
|
||||
chatTimer.schedule(chatTask, chatInterval, chatInterval);
|
||||
}
|
||||
|
||||
public void cleanup () {
|
||||
if (timer == null || chatTimer == null) return;
|
||||
|
||||
timer.cancel();
|
||||
timer.purge();
|
||||
|
||||
chatTimer.cancel();
|
||||
chatTimer.purge();
|
||||
|
||||
gameMode = -1;
|
||||
|
||||
hasSkin = false;
|
||||
cspy = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void chatMessageReceived (Text message) {
|
||||
final String stringMessage = message.getString();
|
||||
|
||||
if (stringMessage.equals("Successfully enabled CommandSpy")) cspy = true;
|
||||
else if (stringMessage.equals("Successfully disabled CommandSpy")) cspy = false;
|
||||
|
||||
else if (stringMessage.equals("Successfully set your skin to " + skin + "'s")) hasSkin = true;
|
||||
else if (
|
||||
stringMessage.equals("Successfully removed your skin") ||
|
||||
stringMessage.startsWith("Successfully set your skin to ")
|
||||
) hasSkin = false;
|
||||
}
|
||||
|
||||
public void tick () {
|
||||
final ClientPlayerEntity player = client.player;
|
||||
final ClientPlayNetworkHandler networkHandler = client.getNetworkHandler();
|
||||
|
||||
if (networkHandler == null) {
|
||||
cleanup();
|
||||
return;
|
||||
}
|
||||
|
||||
if (player != null && !player.hasPermissionLevel(2) && opEnabled) { if (serverHasCommand("op")) networkHandler.sendChatCommand("op @s[type=player]"); }
|
||||
else if (gameMode != 1 && gamemodeEnabled) networkHandler.sendChatCommand("gamemode creative");
|
||||
else if (positionPacketsPerSecond >= 10 && icuEnabled) CommandCore.INSTANCE.run("sudo * icu stop");
|
||||
}
|
||||
|
||||
public void chatTick () {
|
||||
final ClientPlayNetworkHandler networkHandler = client.getNetworkHandler();
|
||||
|
||||
if (!cspy && cspyEnabled) { if (serverHasCommand("c")) networkHandler.sendChatCommand("c on"); }
|
||||
else if (!hasSkin && !skin.equals("off")) { if (serverHasCommand("skin")) networkHandler.sendChatCommand("skin " + skin); }
|
||||
}
|
||||
|
||||
@Override
|
||||
public void packetReceived(Packet<?> packet) {
|
||||
if (packet instanceof GameJoinS2CPacket) packetReceived((GameJoinS2CPacket) packet);
|
||||
else if (packet instanceof GameStateChangeS2CPacket) packetReceived((GameStateChangeS2CPacket) packet);
|
||||
else if (packet instanceof PlayerPositionLookS2CPacket) packetReceived((PlayerPositionLookS2CPacket) packet);
|
||||
}
|
||||
|
||||
public void packetReceived(GameJoinS2CPacket packet) {
|
||||
gameMode = packet.commonPlayerSpawnInfo().gameMode().getId();
|
||||
}
|
||||
|
||||
public void packetReceived(GameStateChangeS2CPacket packet) {
|
||||
if (packet.getReason() != GameStateChangeS2CPacket.GAME_MODE_CHANGED) return;
|
||||
|
||||
gameMode = (int) packet.getValue();
|
||||
}
|
||||
|
||||
public void packetReceived(PlayerPositionLookS2CPacket packet) {
|
||||
if (timer == null) return;
|
||||
|
||||
try {
|
||||
positionPacketsPerSecond++;
|
||||
|
||||
timer.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
positionPacketsPerSecond--;
|
||||
}
|
||||
}, 1000);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
255
src/main/java/land/chipmunk/chipmunkmod/modules/SongPlayer.java
Normal file
255
src/main/java/land/chipmunk/chipmunkmod/modules/SongPlayer.java
Normal file
|
@ -0,0 +1,255 @@
|
|||
package land.chipmunk.chipmunkmod.modules;
|
||||
|
||||
import land.chipmunk.chipmunkmod.song.Note;
|
||||
import land.chipmunk.chipmunkmod.song.Song;
|
||||
import land.chipmunk.chipmunkmod.song.SongLoaderException;
|
||||
import land.chipmunk.chipmunkmod.song.SongLoaderThread;
|
||||
import land.chipmunk.chipmunkmod.util.misc.ColorUtils;
|
||||
import land.chipmunk.chipmunkmod.util.misc.MathUtils;
|
||||
import land.chipmunk.chipmunkmod.util.player.ChatUtils;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import net.kyori.adventure.text.format.TextDecoration;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
||||
import net.minecraft.client.network.ClientPlayerEntity;
|
||||
import net.minecraft.sound.SoundCategory;
|
||||
import net.minecraft.sound.SoundEvent;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import static land.chipmunk.chipmunkmod.ChipmunkMod.*;
|
||||
|
||||
public class SongPlayer {
|
||||
public static File SONG_DIR = new File(MOD_DIR, "songs");
|
||||
static {
|
||||
if (!SONG_DIR.exists()) {
|
||||
SONG_DIR.mkdir();
|
||||
}
|
||||
}
|
||||
|
||||
public static final SongPlayer INSTANCE = new SongPlayer();
|
||||
public static final String SELECTOR = "@a[tag=!nomusic,tag=!zenmod_nomusic,tag=!chimunkmod_nomusic]";
|
||||
|
||||
public SongLoaderThread loaderThread;
|
||||
private int ticksUntilPausedActionbar = 40;
|
||||
|
||||
public LinkedList<Song> songQueue = new LinkedList<>();
|
||||
public Song currentSong;
|
||||
public Timer playTimer;
|
||||
|
||||
public boolean useCore = true;
|
||||
public boolean actionbar = true;
|
||||
public float pitch = 0;
|
||||
|
||||
public void loadSong (Path location) {
|
||||
if (loaderThread != null) {
|
||||
ChatUtils.infoPrefix("Music", "Already loading a song, [hl]cannot load another[def].");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final SongLoaderThread _loaderThread = new SongLoaderThread(location);
|
||||
ChatUtils.infoPrefix("Music", "Loading the song...");
|
||||
|
||||
_loaderThread.start();
|
||||
loaderThread = _loaderThread;
|
||||
} catch (SongLoaderException e) {
|
||||
ChatUtils.warningPrefix("Music", "Failed to load song.");
|
||||
ChatUtils.warning("Error: [hl]%s", e.message.getString());
|
||||
LOGGER.warn("Song player Error (Loading Song File): {}", e.message.getString());
|
||||
|
||||
loaderThread = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void loadSong (URL location) {
|
||||
if (loaderThread != null) {
|
||||
ChatUtils.infoPrefix("Music", "Already loading a song, [hl]cannot load another[def].");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final SongLoaderThread _loaderThread = new SongLoaderThread(location);
|
||||
ChatUtils.infoPrefix("Music", "Loading the song...");
|
||||
|
||||
_loaderThread.start();
|
||||
loaderThread = _loaderThread;
|
||||
} catch (SongLoaderException e) {
|
||||
ChatUtils.warningPrefix("Music", "Failed to load song.");
|
||||
ChatUtils.warning("Error: [hl]%s", e.message.getString());
|
||||
LOGGER.warn("Song player Error (Loading Song URL): {}", e.message.getString());
|
||||
|
||||
loaderThread = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void coreReady () {
|
||||
playTimer = new Timer();
|
||||
|
||||
final TimerTask playTask = new TimerTask() {
|
||||
@Override
|
||||
public void run () {
|
||||
final ClientPlayNetworkHandler networkHandler = MCInstance.getNetworkHandler();
|
||||
if (networkHandler == null) {
|
||||
disconnected();
|
||||
return;
|
||||
}
|
||||
|
||||
if (loaderThread != null && !loaderThread.isAlive()) {
|
||||
if (loaderThread.exception != null) {
|
||||
ChatUtils.warningPrefix("Music", "Failed to load song.");
|
||||
ChatUtils.warning("Error: [hl]%s", loaderThread.exception.message.getString());
|
||||
LOGGER.warn("Song player Error (Loading Exception): {}", loaderThread.exception.message.getString());
|
||||
} else {
|
||||
songQueue.add(loaderThread.song);
|
||||
ChatUtils.infoPrefix("Music", "Added the song to the [hl]Queue[def].");
|
||||
}
|
||||
loaderThread = null;
|
||||
}
|
||||
|
||||
if (currentSong == null) {
|
||||
if (songQueue.isEmpty()) return;
|
||||
|
||||
currentSong = songQueue.poll();
|
||||
ChatUtils.infoPrefix("Music", "Now playing [hl]%s", currentSong.name);
|
||||
|
||||
currentSong.play();
|
||||
}
|
||||
|
||||
if (currentSong.paused && ticksUntilPausedActionbar-- < 0) return;
|
||||
else ticksUntilPausedActionbar = 20;
|
||||
try {
|
||||
if (!useCore && actionbar && MCInstance.player != null) {
|
||||
((Audience) MCInstance.player).sendActionBar(generateActionbar());
|
||||
}
|
||||
else if (actionbar) CommandCore.INSTANCE.run("title " + SELECTOR + " actionbar " + GsonComponentSerializer.gson().serialize(generateActionbar()));
|
||||
} catch (Exception e) {
|
||||
ChatUtils.warningPrefix("Music", "Something went wrong.");
|
||||
ChatUtils.warning("Error: [hl]%s", e.getMessage());
|
||||
LOGGER.warn("Song player Error (Actionbar): {}", e.getMessage());
|
||||
}
|
||||
|
||||
if (currentSong.paused) return;
|
||||
handlePlaying();
|
||||
|
||||
if (currentSong.finished()) {
|
||||
ChatUtils.infoPrefix("Music", "Finished playing [hl]%s", currentSong.name);
|
||||
currentSong = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
playTimer.schedule(playTask, 60, 50);
|
||||
if (currentSong != null) currentSong.play();
|
||||
}
|
||||
|
||||
public Component generateActionbar() {
|
||||
final ClientPlayerEntity player = MCInstance.player;
|
||||
assert player != null;
|
||||
|
||||
TextColor primaryColor = currentSong.paused ? NamedTextColor.DARK_GRAY : TextColor.color(ColorUtils.PRIMARY);
|
||||
TextColor secondaryColor = currentSong.paused ? NamedTextColor.GRAY : TextColor.color(ColorUtils.SECONDARY);
|
||||
TextColor dividerColor = TextColor.color(ColorUtils.GRAY);
|
||||
|
||||
// Build the action bar component
|
||||
Component component = Component.empty()
|
||||
.append(Component.translatable("Now playing %s", Component.empty()
|
||||
.append(Component.text(currentSong.name, secondaryColor))))
|
||||
.append(Component.translatable(" | ", dividerColor))
|
||||
.append(Component.translatable("%s/%s",
|
||||
formatTime(currentSong.time).color(primaryColor),
|
||||
formatTime(currentSong.length).color(secondaryColor)))
|
||||
.append(Component.translatable(" | ", dividerColor))
|
||||
.append(Component.translatable("%s/%s",
|
||||
Component.text(currentSong.position, primaryColor),
|
||||
Component.text(currentSong.size(), secondaryColor)));
|
||||
|
||||
if (currentSong.looping) {
|
||||
if (currentSong.loopCount > 0) {
|
||||
return component
|
||||
.append(Component.text(" \uD83D\uDD01", TextColor.color(ColorUtils.PRIMARY)).decorate(TextDecoration.BOLD))
|
||||
.append(Component.translatable(" (%s/%s)",
|
||||
Component.text(currentSong.currentLoop),
|
||||
Component.text(currentSong.loopCount))
|
||||
.color(TextColor.color(ColorUtils.WARNING)));
|
||||
}
|
||||
return component.append(Component.translatable(" \uD83D\uDD01", TextColor.color(ColorUtils.PRIMARY)).decorate(TextDecoration.BOLD));
|
||||
}
|
||||
|
||||
if (currentSong.paused) {
|
||||
return component.append(Component.translatable(" ||", TextColor.color(ColorUtils.PRIMARY)).decorate(TextDecoration.BOLD));
|
||||
}
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
public Component formatTime (long millis) {
|
||||
final int seconds = (int) millis / 1000;
|
||||
|
||||
final String minutePart = String.valueOf(seconds / 60);
|
||||
final String unpaddedSecondPart = String.valueOf(seconds % 60);
|
||||
|
||||
return Component.translatable(
|
||||
"%s:%s",
|
||||
Component.text(minutePart),
|
||||
Component.text(unpaddedSecondPart.length() < 2 ? "0" + unpaddedSecondPart : unpaddedSecondPart)
|
||||
);
|
||||
}
|
||||
|
||||
public void stopPlaying () {
|
||||
currentSong = null;
|
||||
}
|
||||
|
||||
public void disconnected () {
|
||||
playTimer.cancel();
|
||||
playTimer.purge();
|
||||
|
||||
if (currentSong != null) currentSong.pause();
|
||||
}
|
||||
|
||||
public void handlePlaying () {
|
||||
currentSong.advanceTime();
|
||||
while (currentSong.reachedNextNote()) {
|
||||
final Note note = currentSong.getNextNote();
|
||||
|
||||
try {
|
||||
if (!useCore && MCInstance.player != null) {
|
||||
final float floatingPitch = (float) (0.5 * (Math.pow(2, ((note.pitch + (pitch / 10)) / 12))));
|
||||
final String[] thing = note.instrument.sound.split(":");
|
||||
|
||||
if (thing[1] == null) return;
|
||||
MCInstance.submit(() -> {
|
||||
assert MCInstance.world != null;
|
||||
assert MCInstance.player != null;
|
||||
MCInstance.world.playSound(
|
||||
MCInstance.player.getX(),
|
||||
MCInstance.player.getY(),
|
||||
MCInstance.player.getZ(),
|
||||
SoundEvent.of(Identifier.of(thing[0], thing[1])),
|
||||
SoundCategory.RECORDS,
|
||||
note.volume,
|
||||
floatingPitch,
|
||||
true
|
||||
);
|
||||
});
|
||||
} else {
|
||||
final float floatingPitch = MathUtils.clamp((float) (0.5 * (Math.pow(2, ((note.pitch + (pitch / 10)) / 12)))), 0F, 2F);
|
||||
CommandCore.INSTANCE.run("execute as " + SELECTOR + " at @s run playsound " + note.instrument.sound + " record @s ~ ~ ~ " + note.volume + " " + floatingPitch);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
ChatUtils.warningPrefix("Music", "Something went wrong.");
|
||||
ChatUtils.warning("Error: [hl]%s", e.getMessage());
|
||||
LOGGER.warn("Song player Error (Handle Playing): {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package land.chipmunk.chipmunkmod.modules;
|
||||
|
||||
import land.chipmunk.chipmunkmod.listeners.Listener;
|
||||
import land.chipmunk.chipmunkmod.listeners.ListenerManager;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
||||
import net.minecraft.network.ClientConnection;
|
||||
import net.minecraft.network.packet.Packet;
|
||||
import net.minecraft.network.packet.c2s.play.RequestCommandCompletionsC2SPacket;
|
||||
import net.minecraft.network.packet.s2c.play.CommandSuggestionsS2CPacket;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class TabComplete extends Listener {
|
||||
private final MinecraftClient client;
|
||||
|
||||
private final Map<Integer, CompletableFuture<CommandSuggestionsS2CPacket>> transactions = new HashMap<>();
|
||||
|
||||
public static TabComplete INSTANCE = new TabComplete(MinecraftClient.getInstance());
|
||||
|
||||
public TabComplete (MinecraftClient client) {
|
||||
this.client = client;
|
||||
ListenerManager.addListener(this);
|
||||
}
|
||||
|
||||
public void init () {}
|
||||
|
||||
public CompletableFuture<CommandSuggestionsS2CPacket> complete (String command) {
|
||||
final ClientPlayNetworkHandler networkHandler = client.getNetworkHandler();
|
||||
|
||||
if (networkHandler == null) return null;
|
||||
|
||||
final ClientConnection connection = networkHandler.getConnection();
|
||||
|
||||
if (connection == null) return null;
|
||||
|
||||
final int transactionId = TransactionManager.INSTANCE.nextTransactionId();
|
||||
connection.send(new RequestCommandCompletionsC2SPacket(transactionId, command));
|
||||
|
||||
final CompletableFuture<CommandSuggestionsS2CPacket> future = new CompletableFuture<>();
|
||||
transactions.put(transactionId, future);
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void packetReceived (Packet<?> packet) {
|
||||
if (packet instanceof CommandSuggestionsS2CPacket) packetReceived((CommandSuggestionsS2CPacket) packet);
|
||||
}
|
||||
|
||||
public void packetReceived (CommandSuggestionsS2CPacket packet) {
|
||||
final CompletableFuture<CommandSuggestionsS2CPacket> future = transactions.get(packet.id());
|
||||
|
||||
if (future == null) return;
|
||||
future.complete(packet);
|
||||
transactions.remove(future);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package land.chipmunk.chipmunkmod.modules;
|
||||
|
||||
public class TransactionManager {
|
||||
public static final TransactionManager INSTANCE = new TransactionManager();
|
||||
|
||||
private int transactionId = 0;
|
||||
|
||||
public int transactionId () { return transactionId; }
|
||||
|
||||
public int nextTransactionId () { return transactionId++; }
|
||||
}
|
48
src/main/java/land/chipmunk/chipmunkmod/song/Instrument.java
Normal file
48
src/main/java/land/chipmunk/chipmunkmod/song/Instrument.java
Normal file
|
@ -0,0 +1,48 @@
|
|||
package land.chipmunk.chipmunkmod.song;
|
||||
|
||||
public class Instrument {
|
||||
public static final Instrument HARP = new Instrument(0, "harp", 54);
|
||||
public static final Instrument BASEDRUM = new Instrument(1, "basedrum", 0);
|
||||
public static final Instrument SNARE = new Instrument(2, "snare", 0);
|
||||
public static final Instrument HAT = new Instrument(3, "hat", 0);
|
||||
public static final Instrument BASS = new Instrument(4, "bass", 30);
|
||||
public static final Instrument FLUTE = new Instrument(5, "flute", 66);
|
||||
public static final Instrument BELL = new Instrument(6, "bell", 78);
|
||||
public static final Instrument GUITAR = new Instrument(7, "guitar", 42);
|
||||
public static final Instrument CHIME = new Instrument(8, "chime", 78);
|
||||
public static final Instrument XYLOPHONE = new Instrument(9, "xylophone", 78);
|
||||
public static final Instrument IRON_XYLOPHONE = new Instrument(10, "iron_xylophone", 54);
|
||||
public static final Instrument COW_BELL = new Instrument(11, "cow_bell", 66);
|
||||
public static final Instrument DIDGERIDOO = new Instrument(12, "didgeridoo", 30);
|
||||
public static final Instrument BIT = new Instrument(13, "bit", 54);
|
||||
public static final Instrument BANJO = new Instrument(14, "banjo", 54);
|
||||
public static final Instrument PLING = new Instrument(15, "pling", 54);
|
||||
|
||||
public final int id;
|
||||
public final String name;
|
||||
public final int offset;
|
||||
public final String sound;
|
||||
|
||||
private Instrument (int id, String name, int offset, String sound) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.offset = offset;
|
||||
this.sound = sound;
|
||||
}
|
||||
|
||||
private Instrument (int id, String name, int offset) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.offset = offset;
|
||||
this.sound = "minecraft:block.note_block." + name;
|
||||
}
|
||||
|
||||
public static Instrument of (String sound) {
|
||||
return new Instrument(-1, null, 0, sound);
|
||||
}
|
||||
|
||||
private static final Instrument[] values = {HARP, BASEDRUM, SNARE, HAT, BASS, FLUTE, BELL, GUITAR, CHIME, XYLOPHONE, IRON_XYLOPHONE, COW_BELL, DIDGERIDOO, BIT, BANJO, PLING};
|
||||
public static Instrument fromId (int id) {
|
||||
return values[id];
|
||||
}
|
||||
}
|
371
src/main/java/land/chipmunk/chipmunkmod/song/MidiConverter.java
Normal file
371
src/main/java/land/chipmunk/chipmunkmod/song/MidiConverter.java
Normal file
|
@ -0,0 +1,371 @@
|
|||
package land.chipmunk.chipmunkmod.song;
|
||||
|
||||
import land.chipmunk.chipmunkmod.util.network.DownloadUtils;
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
||||
import javax.sound.midi.*;
|
||||
|
||||
public class MidiConverter {
|
||||
public static final int SET_INSTRUMENT = 0xC0;
|
||||
public static final int SET_TEMPO = 0x51;
|
||||
public static final int NOTE_ON = 0x90;
|
||||
public static final int NOTE_OFF = 0x80;
|
||||
|
||||
public static Song getSongFromUrl(URL url) throws IOException, InvalidMidiDataException, URISyntaxException, NoSuchAlgorithmException, KeyManagementException {
|
||||
Sequence sequence = MidiSystem.getSequence(DownloadUtils.DownloadToInputStream(url));
|
||||
return getSong(sequence, Paths.get(url.toURI().getPath()).getFileName().toString());
|
||||
}
|
||||
|
||||
public static Song getSongFromFile(File file) throws InvalidMidiDataException, IOException {
|
||||
Sequence sequence = MidiSystem.getSequence(file);
|
||||
return getSong(sequence, file.getName());
|
||||
}
|
||||
|
||||
public static Song getSongFromBytes(byte[] bytes, String name) throws InvalidMidiDataException, IOException {
|
||||
Sequence sequence = MidiSystem.getSequence(new ByteArrayInputStream(bytes));
|
||||
return getSong(sequence, name);
|
||||
}
|
||||
|
||||
public static Song getSong(Sequence sequence, String name) {
|
||||
Song song = new Song(name);
|
||||
|
||||
long tpq = sequence.getResolution();
|
||||
|
||||
ArrayList<MidiEvent> tempoEvents = new ArrayList<>();
|
||||
for (Track track : sequence.getTracks()) {
|
||||
for (int i = 0; i < track.size(); i++) {
|
||||
MidiEvent event = track.get(i);
|
||||
MidiMessage message = event.getMessage();
|
||||
if (message instanceof MetaMessage mm) {
|
||||
if (mm.getType() == SET_TEMPO) {
|
||||
tempoEvents.add(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tempoEvents.sort((a, b) -> Long.compare(a.getTick(), b.getTick()));
|
||||
|
||||
for (Track track : sequence.getTracks()) {
|
||||
|
||||
long microTime = 0;
|
||||
int[] ids = new int[16];
|
||||
int mpq = 500000;
|
||||
int tempoEventIdx = 0;
|
||||
long prevTick = 0;
|
||||
|
||||
for (int i = 0; i < track.size(); i++) {
|
||||
MidiEvent event = track.get(i);
|
||||
MidiMessage message = event.getMessage();
|
||||
|
||||
while (tempoEventIdx < tempoEvents.size() && event.getTick() > tempoEvents.get(tempoEventIdx).getTick()) {
|
||||
long deltaTick = tempoEvents.get(tempoEventIdx).getTick() - prevTick;
|
||||
prevTick = tempoEvents.get(tempoEventIdx).getTick();
|
||||
microTime += (mpq/tpq) * deltaTick;
|
||||
|
||||
MetaMessage mm = (MetaMessage) tempoEvents.get(tempoEventIdx).getMessage();
|
||||
byte[] data = mm.getData();
|
||||
int new_mpq = (data[2]&0xFF) | ((data[1]&0xFF)<<8) | ((data[0]&0xFF)<<16);
|
||||
if (new_mpq != 0) mpq = new_mpq;
|
||||
tempoEventIdx++;
|
||||
}
|
||||
|
||||
if (message instanceof ShortMessage sm) {
|
||||
if (sm.getCommand() == SET_INSTRUMENT) {
|
||||
ids[sm.getChannel()] = sm.getData1();
|
||||
}
|
||||
else if (sm.getCommand() == NOTE_ON) {
|
||||
if (sm.getData2() == 0) continue;
|
||||
int pitch = sm.getData1();
|
||||
int velocity = sm.getData2();
|
||||
long deltaTick = event.getTick() - prevTick;
|
||||
prevTick = event.getTick();
|
||||
microTime += (mpq/tpq) * deltaTick;
|
||||
|
||||
Note note;
|
||||
if (sm.getChannel() == 9) {
|
||||
note = getMidiPercussionNote(pitch, velocity, microTime);
|
||||
}
|
||||
else {
|
||||
note = getMidiInstrumentNote(ids[sm.getChannel()], pitch, velocity, microTime);
|
||||
}
|
||||
if (note != null) {
|
||||
song.add(note);
|
||||
}
|
||||
|
||||
long time = microTime / 1000L;
|
||||
if (time > song.length) {
|
||||
song.length = time;
|
||||
}
|
||||
}
|
||||
else if (sm.getCommand() == NOTE_OFF) {
|
||||
long deltaTick = event.getTick() - prevTick;
|
||||
prevTick = event.getTick();
|
||||
microTime += (mpq/tpq) * deltaTick;
|
||||
long time = microTime / 1000L;
|
||||
if (time > song.length) {
|
||||
song.length = time;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
song.sort();
|
||||
|
||||
return song;
|
||||
}
|
||||
|
||||
public static Note getMidiInstrumentNote(int midiInstrument, int midiPitch, int velocity, long microTime) {
|
||||
Instrument instrument = null;
|
||||
Instrument[] instrumentList = instrumentMap.get(midiInstrument);
|
||||
if (instrumentList != null) {
|
||||
for (Instrument candidateInstrument : instrumentList) {
|
||||
if (midiPitch >= candidateInstrument.offset && midiPitch <= candidateInstrument.offset+24) {
|
||||
instrument = candidateInstrument;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (instrument == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int pitch = midiPitch-instrument.offset;
|
||||
float volume = (float) velocity / 127.0f;
|
||||
long time = microTime / 1000L;
|
||||
|
||||
return new Note(instrument, pitch, volume, time);
|
||||
}
|
||||
|
||||
private static Note getMidiPercussionNote (int midiPitch, int velocity, long microTime) {
|
||||
if (percussionMap.containsKey(midiPitch)) {
|
||||
int noteId = percussionMap.get(midiPitch);
|
||||
int pitch = noteId % 25;
|
||||
float volume = (float) velocity / 127.0f;
|
||||
Instrument instrument = Instrument.fromId(noteId / 25);
|
||||
long time = microTime / 1000L;
|
||||
|
||||
return new Note(instrument, pitch, volume, time);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static HashMap<Integer, Instrument[]> instrumentMap = new HashMap<>();
|
||||
static {
|
||||
// Piano (HARP BASS BELL)
|
||||
instrumentMap.put(0, new Instrument[]{Instrument.HARP, Instrument.BASS, Instrument.BELL}); // Acoustic Grand Piano
|
||||
instrumentMap.put(1, new Instrument[]{Instrument.HARP, Instrument.BASS, Instrument.BELL}); // Bright Acoustic Piano
|
||||
instrumentMap.put(2, new Instrument[]{Instrument.BIT, Instrument.DIDGERIDOO, Instrument.BELL}); // Electric Grand Piano
|
||||
instrumentMap.put(3, new Instrument[]{Instrument.HARP, Instrument.BASS, Instrument.BELL}); // Honky-tonk Piano
|
||||
instrumentMap.put(4, new Instrument[]{Instrument.BIT, Instrument.DIDGERIDOO, Instrument.BELL}); // Electric Piano 1
|
||||
instrumentMap.put(5, new Instrument[]{Instrument.BIT, Instrument.DIDGERIDOO, Instrument.BELL}); // Electric Piano 2
|
||||
instrumentMap.put(6, new Instrument[]{Instrument.HARP, Instrument.BASS, Instrument.BELL}); // Harpsichord
|
||||
instrumentMap.put(7, new Instrument[]{Instrument.HARP, Instrument.BASS, Instrument.BELL}); // Clavinet
|
||||
|
||||
// Chromatic Percussion (IRON_XYLOPHONE XYLOPHONE BASS)
|
||||
instrumentMap.put(8, new Instrument[]{Instrument.IRON_XYLOPHONE, Instrument.BASS, Instrument.XYLOPHONE}); // Celesta
|
||||
instrumentMap.put(9, new Instrument[]{Instrument.IRON_XYLOPHONE, Instrument.BASS, Instrument.XYLOPHONE}); // Glockenspiel
|
||||
instrumentMap.put(10, new Instrument[]{Instrument.IRON_XYLOPHONE, Instrument.BASS, Instrument.XYLOPHONE}); // Music Box
|
||||
instrumentMap.put(11, new Instrument[]{Instrument.IRON_XYLOPHONE, Instrument.BASS, Instrument.XYLOPHONE}); // Vibraphone
|
||||
instrumentMap.put(12, new Instrument[]{Instrument.IRON_XYLOPHONE, Instrument.BASS, Instrument.XYLOPHONE}); // Marimba
|
||||
instrumentMap.put(13, new Instrument[]{Instrument.IRON_XYLOPHONE, Instrument.BASS, Instrument.XYLOPHONE}); // Xylophone
|
||||
instrumentMap.put(14, new Instrument[]{Instrument.IRON_XYLOPHONE, Instrument.BASS, Instrument.XYLOPHONE}); // Tubular Bells
|
||||
instrumentMap.put(15, new Instrument[]{Instrument.IRON_XYLOPHONE, Instrument.BASS, Instrument.XYLOPHONE}); // Dulcimer
|
||||
|
||||
// Organ (BIT DIDGERIDOO BELL)
|
||||
instrumentMap.put(16, new Instrument[]{Instrument.DIDGERIDOO, Instrument.BIT, Instrument.XYLOPHONE}); // Drawbar Organ
|
||||
instrumentMap.put(17, new Instrument[]{Instrument.DIDGERIDOO, Instrument.BIT, Instrument.XYLOPHONE}); // Percussive Organ
|
||||
instrumentMap.put(18, new Instrument[]{Instrument.DIDGERIDOO, Instrument.BIT, Instrument.XYLOPHONE}); // Rock Organ
|
||||
instrumentMap.put(19, new Instrument[]{Instrument.DIDGERIDOO, Instrument.BIT, Instrument.XYLOPHONE}); // Church Organ
|
||||
instrumentMap.put(20, new Instrument[]{Instrument.DIDGERIDOO, Instrument.BIT, Instrument.XYLOPHONE}); // Reed Organ
|
||||
instrumentMap.put(21, new Instrument[]{Instrument.DIDGERIDOO, Instrument.BIT, Instrument.XYLOPHONE}); // Accordian
|
||||
instrumentMap.put(22, new Instrument[]{Instrument.DIDGERIDOO, Instrument.BIT, Instrument.XYLOPHONE}); // Harmonica
|
||||
instrumentMap.put(23, new Instrument[]{Instrument.DIDGERIDOO, Instrument.BIT, Instrument.XYLOPHONE}); // Tango Accordian
|
||||
|
||||
// Guitar (BIT DIDGERIDOO BELL)
|
||||
instrumentMap.put(24, new Instrument[]{Instrument.GUITAR, Instrument.HARP, Instrument.BASS, Instrument.BELL}); // Acoustic Guitar (nylon)
|
||||
instrumentMap.put(25, new Instrument[]{Instrument.GUITAR, Instrument.HARP, Instrument.BASS, Instrument.BELL}); // Acoustic Guitar (steel)
|
||||
instrumentMap.put(26, new Instrument[]{Instrument.GUITAR, Instrument.HARP, Instrument.BASS, Instrument.BELL}); // Electric Guitar (jazz)
|
||||
instrumentMap.put(27, new Instrument[]{Instrument.GUITAR, Instrument.HARP, Instrument.BASS, Instrument.BELL}); // Electric Guitar (clean)
|
||||
instrumentMap.put(28, new Instrument[]{Instrument.GUITAR, Instrument.HARP, Instrument.BASS, Instrument.BELL}); // Electric Guitar (muted)
|
||||
instrumentMap.put(29, new Instrument[]{Instrument.DIDGERIDOO, Instrument.BIT, Instrument.XYLOPHONE}); // Overdriven Guitar
|
||||
instrumentMap.put(30, new Instrument[]{Instrument.DIDGERIDOO, Instrument.BIT, Instrument.XYLOPHONE}); // Distortion Guitar
|
||||
instrumentMap.put(31, new Instrument[]{Instrument.GUITAR, Instrument.HARP, Instrument.BASS, Instrument.BELL}); // Guitar Harmonics
|
||||
|
||||
// Bass
|
||||
instrumentMap.put(32, new Instrument[]{Instrument.BASS, Instrument.HARP, Instrument.BELL}); // Acoustic Bass
|
||||
instrumentMap.put(33, new Instrument[]{Instrument.BASS, Instrument.HARP, Instrument.BELL}); // Electric Bass (finger)
|
||||
instrumentMap.put(34, new Instrument[]{Instrument.BASS, Instrument.HARP, Instrument.BELL}); // Electric Bass (pick)
|
||||
instrumentMap.put(35, new Instrument[]{Instrument.BASS, Instrument.HARP, Instrument.BELL}); // Fretless Bass
|
||||
instrumentMap.put(36, new Instrument[]{Instrument.DIDGERIDOO, Instrument.BIT, Instrument.XYLOPHONE}); // Slap Bass 1
|
||||
instrumentMap.put(37, new Instrument[]{Instrument.DIDGERIDOO, Instrument.BIT, Instrument.XYLOPHONE}); // Slap Bass 2
|
||||
instrumentMap.put(38, new Instrument[]{Instrument.DIDGERIDOO, Instrument.BIT, Instrument.XYLOPHONE}); // Synth Bass 1
|
||||
instrumentMap.put(39, new Instrument[]{Instrument.DIDGERIDOO, Instrument.BIT, Instrument.XYLOPHONE}); // Synth Bass 2
|
||||
|
||||
// Strings
|
||||
instrumentMap.put(40, new Instrument[]{Instrument.FLUTE, Instrument.GUITAR, Instrument.BASS, Instrument.BELL}); // Violin
|
||||
instrumentMap.put(41, new Instrument[]{Instrument.FLUTE, Instrument.GUITAR, Instrument.BASS, Instrument.BELL}); // Viola
|
||||
instrumentMap.put(42, new Instrument[]{Instrument.FLUTE, Instrument.GUITAR, Instrument.BASS, Instrument.BELL}); // Cello
|
||||
instrumentMap.put(43, new Instrument[]{Instrument.FLUTE, Instrument.GUITAR, Instrument.BASS, Instrument.BELL}); // Contrabass
|
||||
instrumentMap.put(44, new Instrument[]{Instrument.BIT, Instrument.DIDGERIDOO, Instrument.BELL}); // Tremolo Strings
|
||||
instrumentMap.put(45, new Instrument[]{Instrument.HARP, Instrument.BASS, Instrument.BELL}); // Pizzicato Strings
|
||||
instrumentMap.put(46, new Instrument[]{Instrument.HARP, Instrument.BASS, Instrument.CHIME}); // Orchestral Harp
|
||||
instrumentMap.put(47, new Instrument[]{Instrument.HARP, Instrument.BASS, Instrument.BELL}); // Timpani
|
||||
|
||||
// Ensenble
|
||||
instrumentMap.put(48, new Instrument[]{Instrument.HARP, Instrument.BASS, Instrument.BELL}); // String Ensemble 1
|
||||
instrumentMap.put(49, new Instrument[]{Instrument.HARP, Instrument.BASS, Instrument.BELL}); // String Ensemble 2
|
||||
instrumentMap.put(50, new Instrument[]{Instrument.HARP, Instrument.BASS, Instrument.BELL}); // Synth Strings 1
|
||||
instrumentMap.put(51, new Instrument[]{Instrument.HARP, Instrument.BASS, Instrument.BELL}); // Synth Strings 2
|
||||
instrumentMap.put(52, new Instrument[]{Instrument.HARP, Instrument.BASS, Instrument.BELL}); // Choir Aahs
|
||||
instrumentMap.put(53, new Instrument[]{Instrument.HARP, Instrument.BASS, Instrument.BELL}); // Voice Oohs
|
||||
instrumentMap.put(54, new Instrument[]{Instrument.HARP, Instrument.BASS, Instrument.BELL}); // Synth Choir
|
||||
instrumentMap.put(55, new Instrument[]{Instrument.HARP, Instrument.BASS, Instrument.BELL}); // Orchestra Hit
|
||||
|
||||
// Brass
|
||||
instrumentMap.put(56, new Instrument[]{Instrument.BIT, Instrument.DIDGERIDOO, Instrument.BELL});
|
||||
instrumentMap.put(57, new Instrument[]{Instrument.BIT, Instrument.DIDGERIDOO, Instrument.BELL});
|
||||
instrumentMap.put(58, new Instrument[]{Instrument.BIT, Instrument.DIDGERIDOO, Instrument.BELL});
|
||||
instrumentMap.put(59, new Instrument[]{Instrument.BIT, Instrument.DIDGERIDOO, Instrument.BELL});
|
||||
instrumentMap.put(60, new Instrument[]{Instrument.BIT, Instrument.DIDGERIDOO, Instrument.BELL});
|
||||
instrumentMap.put(61, new Instrument[]{Instrument.BIT, Instrument.DIDGERIDOO, Instrument.BELL});
|
||||
instrumentMap.put(62, new Instrument[]{Instrument.BIT, Instrument.DIDGERIDOO, Instrument.BELL});
|
||||
instrumentMap.put(63, new Instrument[]{Instrument.BIT, Instrument.DIDGERIDOO, Instrument.BELL});
|
||||
|
||||
// Reed
|
||||
instrumentMap.put(64, new Instrument[]{Instrument.FLUTE, Instrument.DIDGERIDOO, Instrument.IRON_XYLOPHONE, Instrument.BELL});
|
||||
instrumentMap.put(65, new Instrument[]{Instrument.FLUTE, Instrument.DIDGERIDOO, Instrument.IRON_XYLOPHONE, Instrument.BELL});
|
||||
instrumentMap.put(66, new Instrument[]{Instrument.FLUTE, Instrument.DIDGERIDOO, Instrument.IRON_XYLOPHONE, Instrument.BELL});
|
||||
instrumentMap.put(67, new Instrument[]{Instrument.FLUTE, Instrument.DIDGERIDOO, Instrument.IRON_XYLOPHONE, Instrument.BELL});
|
||||
instrumentMap.put(68, new Instrument[]{Instrument.FLUTE, Instrument.DIDGERIDOO, Instrument.IRON_XYLOPHONE, Instrument.BELL});
|
||||
instrumentMap.put(69, new Instrument[]{Instrument.FLUTE, Instrument.DIDGERIDOO, Instrument.IRON_XYLOPHONE, Instrument.BELL});
|
||||
instrumentMap.put(70, new Instrument[]{Instrument.FLUTE, Instrument.DIDGERIDOO, Instrument.IRON_XYLOPHONE, Instrument.BELL});
|
||||
instrumentMap.put(71, new Instrument[]{Instrument.FLUTE, Instrument.DIDGERIDOO, Instrument.IRON_XYLOPHONE, Instrument.BELL});
|
||||
|
||||
// Pipe
|
||||
instrumentMap.put(72, new Instrument[]{Instrument.FLUTE, Instrument.DIDGERIDOO, Instrument.IRON_XYLOPHONE, Instrument.BELL});
|
||||
instrumentMap.put(73, new Instrument[]{Instrument.FLUTE, Instrument.DIDGERIDOO, Instrument.IRON_XYLOPHONE, Instrument.BELL});
|
||||
instrumentMap.put(74, new Instrument[]{Instrument.FLUTE, Instrument.DIDGERIDOO, Instrument.IRON_XYLOPHONE, Instrument.BELL});
|
||||
instrumentMap.put(75, new Instrument[]{Instrument.FLUTE, Instrument.DIDGERIDOO, Instrument.IRON_XYLOPHONE, Instrument.BELL});
|
||||
instrumentMap.put(76, new Instrument[]{Instrument.FLUTE, Instrument.DIDGERIDOO, Instrument.IRON_XYLOPHONE, Instrument.BELL});
|
||||
instrumentMap.put(77, new Instrument[]{Instrument.FLUTE, Instrument.DIDGERIDOO, Instrument.IRON_XYLOPHONE, Instrument.BELL});
|
||||
instrumentMap.put(78, new Instrument[]{Instrument.FLUTE, Instrument.DIDGERIDOO, Instrument.IRON_XYLOPHONE, Instrument.BELL});
|
||||
instrumentMap.put(79, new Instrument[]{Instrument.FLUTE, Instrument.DIDGERIDOO, Instrument.IRON_XYLOPHONE, Instrument.BELL});
|
||||
|
||||
// Synth Lead
|
||||
instrumentMap.put(80, new Instrument[]{Instrument.HARP, Instrument.BASS, Instrument.BELL});
|
||||
instrumentMap.put(81, new Instrument[]{Instrument.HARP, Instrument.BASS, Instrument.BELL});
|
||||
instrumentMap.put(82, new Instrument[]{Instrument.HARP, Instrument.BASS, Instrument.BELL});
|
||||
instrumentMap.put(83, new Instrument[]{Instrument.HARP, Instrument.BASS, Instrument.BELL});
|
||||
instrumentMap.put(84, new Instrument[]{Instrument.HARP, Instrument.BASS, Instrument.BELL});
|
||||
instrumentMap.put(85, new Instrument[]{Instrument.HARP, Instrument.BASS, Instrument.BELL});
|
||||
instrumentMap.put(86, new Instrument[]{Instrument.HARP, Instrument.BASS, Instrument.BELL});
|
||||
instrumentMap.put(87, new Instrument[]{Instrument.HARP, Instrument.BASS, Instrument.BELL});
|
||||
|
||||
// Synth Pad
|
||||
instrumentMap.put(88, new Instrument[]{Instrument.HARP, Instrument.BASS, Instrument.BELL});
|
||||
instrumentMap.put(89, new Instrument[]{Instrument.HARP, Instrument.BASS, Instrument.BELL});
|
||||
instrumentMap.put(90, new Instrument[]{Instrument.HARP, Instrument.BASS, Instrument.BELL});
|
||||
instrumentMap.put(91, new Instrument[]{Instrument.HARP, Instrument.BASS, Instrument.BELL});
|
||||
instrumentMap.put(92, new Instrument[]{Instrument.HARP, Instrument.BASS, Instrument.BELL});
|
||||
instrumentMap.put(93, new Instrument[]{Instrument.HARP, Instrument.BASS, Instrument.BELL});
|
||||
instrumentMap.put(94, new Instrument[]{Instrument.HARP, Instrument.BASS, Instrument.BELL});
|
||||
instrumentMap.put(95, new Instrument[]{Instrument.HARP, Instrument.BASS, Instrument.BELL});
|
||||
|
||||
// Synth Effects
|
||||
// instrumentMap.put(96, new Instrument[]{});
|
||||
// instrumentMap.put(97, new Instrument[]{});
|
||||
instrumentMap.put(98, new Instrument[]{Instrument.BIT, Instrument.DIDGERIDOO, Instrument.BELL});
|
||||
instrumentMap.put(99, new Instrument[]{Instrument.HARP, Instrument.BASS, Instrument.BELL});
|
||||
instrumentMap.put(100, new Instrument[]{Instrument.HARP, Instrument.BASS, Instrument.BELL});
|
||||
instrumentMap.put(101, new Instrument[]{Instrument.HARP, Instrument.BASS, Instrument.BELL});
|
||||
instrumentMap.put(102, new Instrument[]{Instrument.HARP, Instrument.BASS, Instrument.BELL});
|
||||
instrumentMap.put(103, new Instrument[]{Instrument.HARP, Instrument.BASS, Instrument.BELL});
|
||||
|
||||
// Ethnic
|
||||
instrumentMap.put(104, new Instrument[]{Instrument.BANJO, Instrument.BASS, Instrument.BELL});
|
||||
instrumentMap.put(105, new Instrument[]{Instrument.BANJO, Instrument.BASS, Instrument.BELL});
|
||||
instrumentMap.put(106, new Instrument[]{Instrument.BANJO, Instrument.BASS, Instrument.BELL});
|
||||
instrumentMap.put(107, new Instrument[]{Instrument.BANJO, Instrument.BASS, Instrument.BELL});
|
||||
instrumentMap.put(108, new Instrument[]{Instrument.BANJO, Instrument.BASS, Instrument.BELL});
|
||||
instrumentMap.put(109, new Instrument[]{Instrument.HARP, Instrument.DIDGERIDOO, Instrument.BELL});
|
||||
instrumentMap.put(110, new Instrument[]{Instrument.HARP, Instrument.DIDGERIDOO, Instrument.BELL});
|
||||
instrumentMap.put(111, new Instrument[]{Instrument.HARP, Instrument.DIDGERIDOO, Instrument.BELL});
|
||||
|
||||
// Percussive
|
||||
instrumentMap.put(112, new Instrument[]{Instrument.IRON_XYLOPHONE, Instrument.BASS, Instrument.XYLOPHONE});
|
||||
instrumentMap.put(113, new Instrument[]{Instrument.IRON_XYLOPHONE, Instrument.BASS, Instrument.XYLOPHONE});
|
||||
instrumentMap.put(114, new Instrument[]{Instrument.IRON_XYLOPHONE, Instrument.BASS, Instrument.XYLOPHONE});
|
||||
instrumentMap.put(115, new Instrument[]{Instrument.IRON_XYLOPHONE, Instrument.BASS, Instrument.XYLOPHONE});
|
||||
instrumentMap.put(116, new Instrument[]{Instrument.IRON_XYLOPHONE, Instrument.BASS, Instrument.XYLOPHONE});
|
||||
instrumentMap.put(117, new Instrument[]{Instrument.IRON_XYLOPHONE, Instrument.BASS, Instrument.XYLOPHONE});
|
||||
instrumentMap.put(118, new Instrument[]{Instrument.IRON_XYLOPHONE, Instrument.BASS, Instrument.XYLOPHONE});
|
||||
instrumentMap.put(119, new Instrument[]{Instrument.IRON_XYLOPHONE, Instrument.BASS, Instrument.XYLOPHONE});
|
||||
}
|
||||
|
||||
public static HashMap<Integer, Integer> percussionMap = new HashMap<>();
|
||||
static {
|
||||
percussionMap.put(35, 10 + 25*Instrument.BASEDRUM.id);
|
||||
percussionMap.put(36, 6 + 25*Instrument.BASEDRUM.id);
|
||||
percussionMap.put(37, 6 + 25*Instrument.HAT.id);
|
||||
percussionMap.put(38, 8 + 25*Instrument.SNARE.id);
|
||||
percussionMap.put(39, 6 + 25*Instrument.HAT.id);
|
||||
percussionMap.put(40, 4 + 25*Instrument.SNARE.id);
|
||||
percussionMap.put(41, 6 + 25*Instrument.BASEDRUM.id);
|
||||
percussionMap.put(42, 22 + 25*Instrument.SNARE.id);
|
||||
percussionMap.put(43, 13 + 25*Instrument.BASEDRUM.id);
|
||||
percussionMap.put(44, 22 + 25*Instrument.SNARE.id);
|
||||
percussionMap.put(45, 15 + 25*Instrument.BASEDRUM.id);
|
||||
percussionMap.put(46, 18 + 25*Instrument.SNARE.id);
|
||||
percussionMap.put(47, 20 + 25*Instrument.BASEDRUM.id);
|
||||
percussionMap.put(48, 23 + 25*Instrument.BASEDRUM.id);
|
||||
percussionMap.put(49, 17 + 25*Instrument.SNARE.id);
|
||||
percussionMap.put(50, 23 + 25*Instrument.BASEDRUM.id);
|
||||
percussionMap.put(51, 24 + 25*Instrument.SNARE.id);
|
||||
percussionMap.put(52, 8 + 25*Instrument.SNARE.id);
|
||||
percussionMap.put(53, 13 + 25*Instrument.SNARE.id);
|
||||
percussionMap.put(54, 18 + 25*Instrument.HAT.id);
|
||||
percussionMap.put(55, 18 + 25*Instrument.SNARE.id);
|
||||
percussionMap.put(56, 1 + 25*Instrument.HAT.id);
|
||||
percussionMap.put(57, 13 + 25*Instrument.SNARE.id);
|
||||
percussionMap.put(58, 2 + 25*Instrument.HAT.id);
|
||||
percussionMap.put(59, 13 + 25*Instrument.SNARE.id);
|
||||
percussionMap.put(60, 9 + 25*Instrument.HAT.id);
|
||||
percussionMap.put(61, 2 + 25*Instrument.HAT.id);
|
||||
percussionMap.put(62, 8 + 25*Instrument.HAT.id);
|
||||
percussionMap.put(63, 22 + 25*Instrument.BASEDRUM.id);
|
||||
percussionMap.put(64, 15 + 25*Instrument.BASEDRUM.id);
|
||||
percussionMap.put(65, 13 + 25*Instrument.SNARE.id);
|
||||
percussionMap.put(66, 8 + 25*Instrument.SNARE.id);
|
||||
percussionMap.put(67, 8 + 25*Instrument.HAT.id);
|
||||
percussionMap.put(68, 3 + 25*Instrument.HAT.id);
|
||||
percussionMap.put(69, 20 + 25*Instrument.HAT.id);
|
||||
percussionMap.put(70, 23 + 25*Instrument.HAT.id);
|
||||
percussionMap.put(71, 24 + 25*Instrument.HAT.id);
|
||||
percussionMap.put(72, 24 + 25*Instrument.HAT.id);
|
||||
percussionMap.put(73, 17 + 25*Instrument.HAT.id);
|
||||
percussionMap.put(74, 11 + 25*Instrument.HAT.id);
|
||||
percussionMap.put(75, 18 + 25*Instrument.HAT.id);
|
||||
percussionMap.put(76, 9 + 25*Instrument.HAT.id);
|
||||
percussionMap.put(77, 5 + 25*Instrument.HAT.id);
|
||||
percussionMap.put(78, 22 + 25*Instrument.HAT.id);
|
||||
percussionMap.put(79, 19 + 25*Instrument.SNARE.id);
|
||||
percussionMap.put(80, 17 + 25*Instrument.HAT.id);
|
||||
percussionMap.put(81, 22 + 25*Instrument.HAT.id);
|
||||
percussionMap.put(82, 22 + 25*Instrument.SNARE.id);
|
||||
percussionMap.put(83, 24 + 25*Instrument.CHIME.id);
|
||||
percussionMap.put(84, 24 + 25*Instrument.CHIME.id);
|
||||
percussionMap.put(85, 21 + 25*Instrument.HAT.id);
|
||||
percussionMap.put(86, 14 + 25*Instrument.BASEDRUM.id);
|
||||
percussionMap.put(87, 7 + 25*Instrument.BASEDRUM.id);
|
||||
}
|
||||
}
|
197
src/main/java/land/chipmunk/chipmunkmod/song/NBSConverter.java
Normal file
197
src/main/java/land/chipmunk/chipmunkmod/song/NBSConverter.java
Normal file
|
@ -0,0 +1,197 @@
|
|||
package land.chipmunk.chipmunkmod.song;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class NBSConverter {
|
||||
public static Instrument[] instrumentIndex = new Instrument[] {
|
||||
Instrument.HARP,
|
||||
Instrument.BASS,
|
||||
Instrument.BASEDRUM,
|
||||
Instrument.SNARE,
|
||||
Instrument.HAT,
|
||||
Instrument.GUITAR,
|
||||
Instrument.FLUTE,
|
||||
Instrument.BELL,
|
||||
Instrument.CHIME,
|
||||
Instrument.XYLOPHONE,
|
||||
Instrument.IRON_XYLOPHONE,
|
||||
Instrument.COW_BELL,
|
||||
Instrument.DIDGERIDOO,
|
||||
Instrument.BIT,
|
||||
Instrument.BANJO,
|
||||
Instrument.PLING,
|
||||
};
|
||||
|
||||
private static class NBSNote {
|
||||
public int tick;
|
||||
public short layer;
|
||||
public byte instrument;
|
||||
public byte key;
|
||||
public byte velocity = 100;
|
||||
public byte panning = 100;
|
||||
public short pitch = 0;
|
||||
}
|
||||
|
||||
private static class NBSLayer {
|
||||
public String name;
|
||||
public byte lock = 0;
|
||||
public byte volume;
|
||||
public byte stereo = 100;
|
||||
}
|
||||
|
||||
private static class NBSCustomInstrument {
|
||||
public String name;
|
||||
public String file;
|
||||
public byte pitch = 0;
|
||||
public boolean key = false;
|
||||
}
|
||||
|
||||
public static Song getSongFromBytes(byte[] bytes, String fileName) throws IOException {
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
short songLength = 0;
|
||||
byte format = 0;
|
||||
byte vanillaInstrumentCount = 0;
|
||||
songLength = buffer.getShort(); // If it's not 0, then it uses the old format
|
||||
if (songLength == 0) {
|
||||
format = buffer.get();
|
||||
}
|
||||
|
||||
if (format >= 1) {
|
||||
vanillaInstrumentCount = buffer.get();
|
||||
}
|
||||
if (format >= 3) {
|
||||
songLength = buffer.getShort();
|
||||
}
|
||||
|
||||
short layerCount = buffer.getShort();
|
||||
String songName = getString(buffer, bytes.length);
|
||||
String songAuthor = getString(buffer, bytes.length);
|
||||
String songOriginalAuthor = getString(buffer, bytes.length);
|
||||
String songDescription = getString(buffer, bytes.length);
|
||||
short tempo = buffer.getShort();
|
||||
byte autoSaving = buffer.get();
|
||||
byte autoSavingDuration = buffer.get();
|
||||
byte timeSignature = buffer.get();
|
||||
int minutesSpent = buffer.getInt();
|
||||
int leftClicks = buffer.getInt();
|
||||
int rightClicks = buffer.getInt();
|
||||
int blocksAdded = buffer.getInt();
|
||||
int blocksRemoved = buffer.getInt();
|
||||
String origFileName = getString(buffer, bytes.length);
|
||||
|
||||
byte loop = 0;
|
||||
byte maxLoopCount = 0;
|
||||
short loopStartTick = 0;
|
||||
if (format >= 4) {
|
||||
loop = buffer.get();
|
||||
maxLoopCount = buffer.get();
|
||||
loopStartTick = buffer.getShort();
|
||||
}
|
||||
|
||||
ArrayList<NBSNote> nbsNotes = new ArrayList<>();
|
||||
short tick = -1;
|
||||
while (true) {
|
||||
int tickJumps = buffer.getShort();
|
||||
if (tickJumps == 0) break;
|
||||
tick += (short) tickJumps;
|
||||
|
||||
short layer = -1;
|
||||
while (true) {
|
||||
int layerJumps = buffer.getShort();
|
||||
if (layerJumps == 0) break;
|
||||
layer += (short) layerJumps;
|
||||
NBSNote note = new NBSNote();
|
||||
note.tick = tick;
|
||||
note.layer = layer;
|
||||
note.instrument = buffer.get();
|
||||
note.key = buffer.get();
|
||||
if (format >= 4) {
|
||||
note.velocity = buffer.get();
|
||||
note.panning = buffer.get();
|
||||
note.pitch = buffer.getShort();
|
||||
}
|
||||
nbsNotes.add(note);
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<NBSLayer> nbsLayers = new ArrayList<>();
|
||||
if (buffer.hasRemaining()) {
|
||||
for (int i=0; i<layerCount; i++) {
|
||||
NBSLayer layer = new NBSLayer();
|
||||
layer.name = getString(buffer, bytes.length);
|
||||
if (format >= 4) {
|
||||
layer.lock = buffer.get();
|
||||
}
|
||||
layer.volume = buffer.get();
|
||||
if (format >= 2) {
|
||||
layer.stereo = buffer.get();
|
||||
}
|
||||
nbsLayers.add(layer);
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<NBSCustomInstrument> customInstruments = new ArrayList<>();
|
||||
if (buffer.hasRemaining()) {
|
||||
byte customInstrumentCount = buffer.get();
|
||||
for (int i = 0; i < customInstrumentCount; i++) {
|
||||
NBSCustomInstrument customInstrument = new NBSCustomInstrument();
|
||||
customInstrument.name = getString(buffer, bytes.length);
|
||||
customInstrument.file = getString(buffer, bytes.length);
|
||||
customInstrument.pitch = buffer.get();
|
||||
customInstrument.key = buffer.get() != 0;
|
||||
customInstruments.add(customInstrument);
|
||||
}
|
||||
}
|
||||
|
||||
Song song = new Song(!songName.trim().isEmpty() ? songName : fileName);
|
||||
if (loop > 0) {
|
||||
song.looping = true;
|
||||
song.loopPosition = getMilliTime(loopStartTick, tempo);
|
||||
song.loopCount = maxLoopCount;
|
||||
}
|
||||
for (NBSNote note : nbsNotes) {
|
||||
Instrument instrument;
|
||||
int key = note.key;
|
||||
if (note.instrument < instrumentIndex.length) {
|
||||
instrument = instrumentIndex[note.instrument];
|
||||
} else {
|
||||
int index = note.instrument - instrumentIndex.length;
|
||||
if (index >= customInstruments.size()) continue;
|
||||
NBSCustomInstrument customInstrument = customInstruments.get(index);
|
||||
instrument = Instrument.of(customInstrument.name);
|
||||
key += customInstrument.pitch;
|
||||
}
|
||||
|
||||
byte layerVolume = 100;
|
||||
if (nbsLayers.size() > note.layer) {
|
||||
layerVolume = nbsLayers.get(note.layer).volume;
|
||||
}
|
||||
|
||||
int pitch = key-33;
|
||||
song.add(new Note(instrument, pitch, (float) note.velocity * (float) layerVolume / 10000f, getMilliTime(note.tick, tempo)));
|
||||
}
|
||||
|
||||
song.length = song.get(song.size()-1).time + 50;
|
||||
|
||||
return song;
|
||||
}
|
||||
|
||||
private static String getString (ByteBuffer buffer, int maxSize) throws IOException {
|
||||
int length = buffer.getInt();
|
||||
if (length > maxSize) {
|
||||
throw new IOException("String is too large");
|
||||
}
|
||||
byte[] arr = new byte[length];
|
||||
buffer.get(arr, 0, length);
|
||||
return new String(arr);
|
||||
}
|
||||
|
||||
private static int getMilliTime(int tick, int tempo) {
|
||||
return 1000 * tick * 100 / tempo;
|
||||
}
|
||||
}
|
25
src/main/java/land/chipmunk/chipmunkmod/song/Note.java
Normal file
25
src/main/java/land/chipmunk/chipmunkmod/song/Note.java
Normal file
|
@ -0,0 +1,25 @@
|
|||
package land.chipmunk.chipmunkmod.song;
|
||||
|
||||
|
||||
public class Note implements Comparable<Note> {
|
||||
public Instrument instrument;
|
||||
public int pitch;
|
||||
public float volume;
|
||||
public long time;
|
||||
|
||||
public Note (Instrument instrument, int pitch, float volume, long time) {
|
||||
this.instrument = instrument;
|
||||
this.pitch = pitch;
|
||||
this.volume = volume;
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Note other) {
|
||||
return Long.compare(time, other.time);
|
||||
}
|
||||
|
||||
public int noteId () {
|
||||
return pitch + instrument.id * 25;
|
||||
}
|
||||
}
|
127
src/main/java/land/chipmunk/chipmunkmod/song/Song.java
Normal file
127
src/main/java/land/chipmunk/chipmunkmod/song/Song.java
Normal file
|
@ -0,0 +1,127 @@
|
|||
package land.chipmunk.chipmunkmod.song;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
public class Song {
|
||||
public ArrayList<Note> notes = new ArrayList<>();
|
||||
public String name;
|
||||
public int position = 0; // Current note index
|
||||
public boolean looping = false;
|
||||
public boolean paused = true;
|
||||
public long startTime = 0; // Start time in millis since unix epoch
|
||||
public long length = 0; // Milliseconds in the song
|
||||
public long time = 0; // Time since start of song
|
||||
public long loopPosition = 0; // Milliseconds into the song to start looping
|
||||
public int loopCount = 0; // Number of times to loop
|
||||
public int currentLoop = 0; // Number of loops so far
|
||||
|
||||
public Song (String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Note get (int i) {
|
||||
return notes.get(i);
|
||||
}
|
||||
|
||||
public void add (Note e) {
|
||||
notes.add(e);
|
||||
}
|
||||
|
||||
public void sort () {
|
||||
Collections.sort(notes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts playing song (does nothing if already playing)
|
||||
*/
|
||||
public void play () {
|
||||
if (paused) {
|
||||
paused = false;
|
||||
startTime = System.currentTimeMillis() - time;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pauses song (does nothing if already paused)
|
||||
*/
|
||||
public void pause () {
|
||||
if (!paused) {
|
||||
paused = true;
|
||||
// Recalculates time so that the song will continue playing after the exact point it was paused
|
||||
advanceTime();
|
||||
}
|
||||
}
|
||||
|
||||
public void setTime (long t) {
|
||||
time = t;
|
||||
startTime = System.currentTimeMillis() - time;
|
||||
position = 0;
|
||||
while (position < notes.size() && notes.get(position).time < t) {
|
||||
position++;
|
||||
}
|
||||
}
|
||||
|
||||
public void advanceTime () {
|
||||
time = System.currentTimeMillis() - startTime;
|
||||
}
|
||||
|
||||
public boolean reachedNextNote () {
|
||||
if (position < notes.size()) {
|
||||
return notes.get(position).time <= time;
|
||||
} else {
|
||||
if (time > length && shouldLoop()) {
|
||||
loop();
|
||||
if (position < notes.size()) {
|
||||
return notes.get(position).time <= time;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Note getNextNote () {
|
||||
if (position >= notes.size()) {
|
||||
if (shouldLoop()) {
|
||||
loop();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return notes.get(position++);
|
||||
}
|
||||
|
||||
public boolean finished () {
|
||||
return time > length && !shouldLoop();
|
||||
}
|
||||
|
||||
private void loop () {
|
||||
position = 0;
|
||||
startTime += length - loopPosition;
|
||||
time -= length - loopPosition;
|
||||
while (position < notes.size() && notes.get(position).time < loopPosition) {
|
||||
position++;
|
||||
}
|
||||
currentLoop++;
|
||||
}
|
||||
|
||||
private boolean shouldLoop () {
|
||||
if (looping) {
|
||||
if (loopCount == 0) {
|
||||
return true;
|
||||
} else {
|
||||
return currentLoop < loopCount;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int size () {
|
||||
return notes.size();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package land.chipmunk.chipmunkmod.song;
|
||||
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
public class SongLoaderException extends Exception {
|
||||
public final Text message;
|
||||
|
||||
public SongLoaderException (Text message) {
|
||||
super();
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public SongLoaderException (Text message, Throwable cause) {
|
||||
super(null, cause);
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage () {
|
||||
return message.getString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
package land.chipmunk.chipmunkmod.song;
|
||||
|
||||
import land.chipmunk.chipmunkmod.modules.SongPlayer;
|
||||
import land.chipmunk.chipmunkmod.util.network.DownloadUtils;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
public class SongLoaderThread extends Thread {
|
||||
private String location;
|
||||
private File songPath;
|
||||
private URL songUrl;
|
||||
public SongLoaderException exception;
|
||||
public Song song;
|
||||
|
||||
private boolean isUrl;
|
||||
|
||||
public SongLoaderThread (URL location) throws SongLoaderException {
|
||||
isUrl = true;
|
||||
songUrl = location;
|
||||
}
|
||||
|
||||
public SongLoaderThread (Path location) throws SongLoaderException {
|
||||
isUrl = false;
|
||||
songPath = location.toFile();
|
||||
}
|
||||
|
||||
public void run () {
|
||||
byte[] bytes;
|
||||
String name;
|
||||
try {
|
||||
if (isUrl) {
|
||||
bytes = DownloadUtils.DownloadToByteArray(songUrl);
|
||||
name = Paths.get(songUrl.toURI().getPath()).getFileName().toString();
|
||||
} else {
|
||||
bytes = Files.readAllBytes(songPath.toPath());
|
||||
name = songPath.getName();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
exception = new SongLoaderException(Text.literal(e.getMessage()), e);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
song = MidiConverter.getSongFromBytes(bytes, name);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
if (song == null) {
|
||||
try {
|
||||
song = NBSConverter.getSongFromBytes(bytes, name);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
if (song == null) {
|
||||
exception = new SongLoaderException(Text.translatable("Invalid song format"));
|
||||
}
|
||||
}
|
||||
|
||||
private File getSongFile (String name) {
|
||||
return new File(SongPlayer.SONG_DIR, name);
|
||||
}
|
||||
}
|
12
src/main/java/land/chipmunk/chipmunkmod/util/PostInit.java
Normal file
12
src/main/java/land/chipmunk/chipmunkmod/util/PostInit.java
Normal file
|
@ -0,0 +1,12 @@
|
|||
package land.chipmunk.chipmunkmod.util;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface PostInit {
|
||||
Class<?>[] dependencies() default { };
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package land.chipmunk.chipmunkmod.util.gson;
|
||||
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonToken;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import java.io.IOException;
|
||||
|
||||
public class BlockPosTypeAdapter extends TypeAdapter<BlockPos> {
|
||||
@Override
|
||||
public BlockPos read (JsonReader reader) throws IOException {
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int z = 0;
|
||||
|
||||
reader.beginObject();
|
||||
|
||||
while (!reader.peek().equals(JsonToken.END_OBJECT)) {
|
||||
if (reader.peek().equals(JsonToken.NAME)) {
|
||||
String name = reader.nextName();
|
||||
|
||||
// ? Is there a better way to do this?
|
||||
if (name.equals("x")) x = reader.nextInt();
|
||||
else if (name.equals("y")) y = reader.nextInt();
|
||||
else if (name.equals("z")) z = reader.nextInt();
|
||||
else reader.skipValue();
|
||||
}
|
||||
}
|
||||
|
||||
reader.endObject();
|
||||
|
||||
return new BlockPos(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write (JsonWriter out, BlockPos vec) throws IOException {
|
||||
// TODO: make this do something lmfaooo
|
||||
}
|
||||
}
|
|
@ -0,0 +1,279 @@
|
|||
package land.chipmunk.chipmunkmod.util.misc;
|
||||
|
||||
import com.mojang.brigadier.Command;
|
||||
import land.chipmunk.chipmunkmod.ChipmunkMod;
|
||||
import land.chipmunk.chipmunkmod.Configuration;
|
||||
import land.chipmunk.chipmunkmod.modules.Chat;
|
||||
import land.chipmunk.chipmunkmod.modules.CustomChat;
|
||||
import land.chipmunk.chipmunkmod.util.player.ChatUtils;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientPlayerEntity;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static land.chipmunk.chipmunkmod.ChipmunkMod.MCInstance;
|
||||
|
||||
public class BotValidationUtils {
|
||||
private static final MinecraftClient client = MCInstance;
|
||||
private static final ClientPlayerEntity player = client.player;
|
||||
|
||||
private static final Configuration.Bots botinfo = ChipmunkMod.CONFIG.bots;
|
||||
|
||||
public static int lambda(String command) throws RuntimeException {
|
||||
final String prefix = botinfo.lambda.prefix;
|
||||
final String key = botinfo.lambda.key;
|
||||
|
||||
if (key == null)
|
||||
throw new RuntimeException("The key of the bot is unspecified (null), did you incorrectly add it to your config?");
|
||||
|
||||
try {
|
||||
String[] arguments = command.split(" ");
|
||||
|
||||
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
|
||||
long time = System.currentTimeMillis() / 6_000;
|
||||
|
||||
String rawKey = key + ";" + player.getUuidAsString() + ";" + time;
|
||||
|
||||
sha256.update(rawKey.getBytes());
|
||||
byte[] hash = sha256.digest();
|
||||
ByteBuffer buffer = ByteBuffer.wrap(hash, 0, 6);
|
||||
long bigInt = (buffer.getInt() & 0xFFFFFFFFL);
|
||||
String stringHash = Long.toString(bigInt, 36);
|
||||
|
||||
final String[] restArguments = Arrays.copyOfRange(arguments, 1, arguments.length);
|
||||
final String toSend = prefix +
|
||||
arguments[0] +
|
||||
" " +
|
||||
stringHash +
|
||||
" " +
|
||||
String.join(" ", restArguments);
|
||||
|
||||
Chat.sendChatMessage(toSend);
|
||||
} catch (NoSuchAlgorithmException err) {
|
||||
ChatUtils.errorPrefix("Validation", "Something went wrong: [hl]%s", err.toString());
|
||||
}
|
||||
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
|
||||
public static int nbot(String command) throws RuntimeException {
|
||||
final String prefix = botinfo.nbot.prefix;
|
||||
final String key = botinfo.nbot.key;
|
||||
|
||||
if (key == null)
|
||||
throw new RuntimeException("The key of the bot is unspecified (null), did you incorrectly add it to your config?");
|
||||
|
||||
try {
|
||||
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
|
||||
long time = System.currentTimeMillis() / 5_000;
|
||||
|
||||
String rawKey = command.replaceAll("&[0-9a-fklmnor]", "") + ";" + player.getUuidAsString() + ";" + time + ";" + key;
|
||||
|
||||
sha256.update(rawKey.getBytes());
|
||||
byte[] hash = sha256.digest();
|
||||
ByteBuffer buffer = ByteBuffer.wrap(hash, 0, 4);
|
||||
long bigInt = (buffer.getInt() & 0xFFFFFFFFL);
|
||||
String stringHash = Long.toString(bigInt, 36);
|
||||
|
||||
Chat.sendChatMessage(prefix + command + " " + stringHash, true);
|
||||
} catch (NoSuchAlgorithmException err) {
|
||||
ChatUtils.errorPrefix("Validation", "Something went wrong: [hl]%s", err.toString());
|
||||
}
|
||||
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
|
||||
public static int qbot (String command) {
|
||||
final String prefix = botinfo.qbot.prefix;
|
||||
final String key = botinfo.qbot.key;
|
||||
|
||||
if (key == null)
|
||||
throw new RuntimeException("The key of the bot is unspecified (null), did you incorrectly add it to your config?");
|
||||
|
||||
try {
|
||||
String[] arguments = command.split(" ");
|
||||
|
||||
long currentTime = System.currentTimeMillis() / 1_000;
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
|
||||
String input = currentTime + key;
|
||||
|
||||
byte[] hash = digest.digest(input.getBytes());
|
||||
StringBuilder hexString = new StringBuilder();
|
||||
for (byte b : hash) {
|
||||
String hex = Integer.toHexString(0xff & b);
|
||||
if (hex.length() == 1) hexString.append('0');
|
||||
hexString.append(hex);
|
||||
}
|
||||
|
||||
final String[] restArguments = Arrays.copyOfRange(arguments, 1, arguments.length);
|
||||
final String result = hexString.substring(0, 16);
|
||||
|
||||
Chat.sendChatMessage(prefix + arguments[0] + " " + result + " " + String.join(" ", restArguments));
|
||||
} catch (NoSuchAlgorithmException err) {
|
||||
ChatUtils.errorPrefix("Validation", "Something went wrong: [hl]%s", err.toString());
|
||||
}
|
||||
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
|
||||
public static int hbot (String command) throws RuntimeException {
|
||||
final String prefix = botinfo.hbot.prefix;
|
||||
final String key = botinfo.hbot.key;
|
||||
|
||||
if (key == null)
|
||||
throw new RuntimeException("The key of the bot is unspecified (null), did you incorrectly add it to your config?");
|
||||
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||
String time = String.valueOf(System.currentTimeMillis() / 10_000);
|
||||
|
||||
String input = prefix + command.replaceAll("&[0-9a-fklmnor]", "") + ";" + player.getUuidAsString() + ";" + time + ";" + key;
|
||||
|
||||
byte[] hash = md.digest(input.getBytes(StandardCharsets.UTF_8));
|
||||
BigInteger bigInt = new BigInteger(1, Arrays.copyOfRange(hash, 0, 4));
|
||||
String stringHash = bigInt.toString(Character.MAX_RADIX);
|
||||
|
||||
Chat.sendChatMessage(prefix + command + " " + stringHash, true);
|
||||
} catch (NoSuchAlgorithmException err) {
|
||||
ChatUtils.errorPrefix("Validation", "Something went wrong: [hl]%s", err.toString());
|
||||
}
|
||||
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
|
||||
public static int sbot (String command) throws RuntimeException {
|
||||
final String prefix = botinfo.sbot.prefix;
|
||||
final String key = botinfo.sbot.key;
|
||||
|
||||
if (key == null)
|
||||
throw new RuntimeException("The key of the bot is unspecified (null), did you incorrectly add it to your config?");
|
||||
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
String time = String.valueOf(System.currentTimeMillis() / 20_000);
|
||||
|
||||
String input = prefix + command.replaceAll("&[0-9a-fklmnorx]", "") + ";" + player.getName().getString() + ";" + time + ";" + key;
|
||||
|
||||
byte[] hash = md.digest(input.getBytes(StandardCharsets.UTF_8));
|
||||
BigInteger bigInt = new BigInteger(1, Arrays.copyOfRange(hash, 0, 4));
|
||||
String stringHash = bigInt.toString(Character.MAX_RADIX);
|
||||
|
||||
Chat.sendChatMessage(prefix + command + " " + stringHash, true);
|
||||
} catch (NoSuchAlgorithmException err) {
|
||||
ChatUtils.errorPrefix("Validation", "Something went wrong: [hl]%s", err.toString());
|
||||
}
|
||||
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
|
||||
public static int chomens (String command) throws RuntimeException {
|
||||
final String prefix = botinfo.chomens.prefix;
|
||||
final String key = botinfo.chomens.key;
|
||||
|
||||
if (key == null)
|
||||
throw new RuntimeException("The key of the bot is unspecified (null), did you incorrectly add it to your config?");
|
||||
|
||||
try {
|
||||
String[] arguments = command.split(" ");
|
||||
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||
String time = String.valueOf(System.currentTimeMillis() / 5_000);
|
||||
|
||||
String input = player.getUuidAsString() + arguments[0] + time + key;
|
||||
|
||||
byte[] hash = md.digest(input.getBytes(StandardCharsets.UTF_8));
|
||||
String stringHash = ((String) Hexadecimal.encode(hash)).substring(0, 16); // Some fix? idk why this giving error "Cannot resolve method 'substring' in 'Object'" for some reason!!
|
||||
|
||||
final boolean shouldSectionSign = CustomChat.INSTANCE.enabled && player.hasPermissionLevel(2) && player.isCreative();
|
||||
|
||||
if (shouldSectionSign) {
|
||||
stringHash = String.join("",
|
||||
Arrays.stream(stringHash.split(""))
|
||||
.map((letter) -> "§" + letter)
|
||||
.toArray(String[]::new)
|
||||
);
|
||||
}
|
||||
|
||||
final String[] restArguments = Arrays.copyOfRange(arguments, 1, arguments.length);
|
||||
final String toSend = prefix +
|
||||
arguments[0] +
|
||||
" " +
|
||||
stringHash +
|
||||
(shouldSectionSign ? "§r" : "") +
|
||||
" " +
|
||||
String.join(" ", restArguments);
|
||||
|
||||
Chat.sendChatMessage(toSend);
|
||||
} catch (NoSuchAlgorithmException err) {
|
||||
ChatUtils.errorPrefix("Validation", "Something went wrong: [hl]%s", err.toString());
|
||||
}
|
||||
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
|
||||
// Why formatted differently?
|
||||
public static int fnfboyfriend (String command) {
|
||||
final String prefix = botinfo.fnfboyfriend.prefix;
|
||||
final String key = botinfo.fnfboyfriend.key;
|
||||
|
||||
if (key == null)
|
||||
throw new RuntimeException("The key of the bot is unspecified (null), did you incorrectly add it to your config?");
|
||||
|
||||
try {
|
||||
String[] arguments = command.split(" ");
|
||||
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
long currentTime = System.currentTimeMillis() / 1_000;
|
||||
|
||||
String input = currentTime + key;
|
||||
|
||||
byte[] hash = digest.digest(input.getBytes());
|
||||
StringBuilder hexString = new StringBuilder();
|
||||
for (byte b : hash) {
|
||||
String hex = Integer.toHexString(0xff & b);
|
||||
if (hex.length() == 1) hexString.append('0');
|
||||
hexString.append(hex);
|
||||
}
|
||||
|
||||
final String[] restArguments = Arrays.copyOfRange(arguments, 1, arguments.length);
|
||||
final String result = hexString.substring(0, 16);
|
||||
|
||||
Chat.sendChatMessage(prefix + arguments[0] + " " + result + " " + String.join(" ", restArguments));
|
||||
} catch (NoSuchAlgorithmException err) {
|
||||
ChatUtils.errorPrefix("Validation", "Something went wrong: [hl]%s", err.toString());
|
||||
}
|
||||
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
|
||||
public static int kittycorp (String command) throws RuntimeException {
|
||||
final String prefix = botinfo.kittycorp.prefix;
|
||||
final String key = botinfo.kittycorp.key;
|
||||
|
||||
if (key == null)
|
||||
throw new RuntimeException("The key of the bot is unspecified (null), did you incorrectly add it to your config?");
|
||||
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||
String time = String.valueOf(System.currentTimeMillis() / 10_000);
|
||||
|
||||
String input = prefix + command.replaceAll("&[0-9a-fklmnorx]", "") + ";" + time + ";" + key;
|
||||
|
||||
byte[] hash = md.digest(input.getBytes(StandardCharsets.UTF_8));
|
||||
BigInteger bigInt = new BigInteger(1, Arrays.copyOfRange(hash, 0, 4));
|
||||
String stringHash = bigInt.toString(Character.MAX_RADIX);
|
||||
|
||||
Chat.sendChatMessage(prefix + command + " " + stringHash, true);
|
||||
} catch (NoSuchAlgorithmException err) {
|
||||
ChatUtils.errorPrefix("Validation", "Something went wrong: [hl]%s", err.toString());
|
||||
}
|
||||
|
||||
return Command.SINGLE_SUCCESS;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,503 @@
|
|||
package land.chipmunk.chipmunkmod.util.misc;
|
||||
|
||||
import land.chipmunk.chipmunkmod.ChipmunkMod;
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
import net.minecraft.text.MutableText;
|
||||
import net.minecraft.text.Style;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.text.TextColor;
|
||||
import net.minecraft.util.Formatting;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* Utility class for handling colors in various formats and performing color-related operations.
|
||||
* Supports RGB, RGBA color spaces and various color conversions.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class ColorUtils implements Copyable<ColorUtils>, Serializable<ColorUtils> {
|
||||
// Default color constants from configuration
|
||||
public static final int PRIMARY = parseHexColor(ChipmunkMod.CONFIG.client.colors.PRIMARY);
|
||||
public static final int SECONDARY = parseHexColor(ChipmunkMod.CONFIG.client.colors.SECONDARY);
|
||||
public static final int SUCCESS = parseHexColor(ChipmunkMod.CONFIG.client.colors.SUCCESS);
|
||||
public static final int DANGER = parseHexColor(ChipmunkMod.CONFIG.client.colors.DANGER);
|
||||
public static final int GRAY = parseHexColor(ChipmunkMod.CONFIG.client.colors.GRAY);
|
||||
public static final int WARNING = parseHexColor("#FFAE1A");
|
||||
|
||||
// Color components (0-255)
|
||||
public int r, g, b, a;
|
||||
|
||||
/**
|
||||
* Default constructor - creates white color with full opacity
|
||||
*/
|
||||
public ColorUtils() {
|
||||
this(255, 255, 255, 255);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a color with RGB values and full opacity
|
||||
*/
|
||||
public ColorUtils(int r, int g, int b) {
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
this.a = 255;
|
||||
|
||||
validate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a color with RGBA values
|
||||
*/
|
||||
public ColorUtils(int r, int g, int b, int a) {
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
this.a = a;
|
||||
|
||||
validate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a color from normalized float values (0.0-1.0)
|
||||
*/
|
||||
public ColorUtils(float r, float g, float b, float a) {
|
||||
this.r = (int)(r*255);
|
||||
this.g = (int)(g*255);
|
||||
this.b = (int)(b*255);
|
||||
this.a = (int)(a*255);
|
||||
|
||||
validate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a color from a packed RGBA integer
|
||||
*/
|
||||
public ColorUtils(int packed) {
|
||||
this.r = toRGBAR(packed);
|
||||
this.g = toRGBAG(packed);
|
||||
this.b = toRGBAB(packed);
|
||||
this.a = toRGBAA(packed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor
|
||||
*/
|
||||
public ColorUtils(ColorUtils color) {
|
||||
this.r = color.r;
|
||||
this.g = color.g;
|
||||
this.b = color.b;
|
||||
this.a = color.a;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a color from Java AWT Color
|
||||
*/
|
||||
public ColorUtils(java.awt.Color color) {
|
||||
this.r = color.getRed();
|
||||
this.g = color.getGreen();
|
||||
this.b = color.getBlue();
|
||||
this.a = color.getAlpha();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a color from Minecraft Formatting
|
||||
* If the formatting is not a color, creates white
|
||||
*/
|
||||
public ColorUtils(Formatting formatting) {
|
||||
if (formatting.isColor()) {
|
||||
this.r = toRGBAR(formatting.getColorValue());
|
||||
this.g = toRGBAG(formatting.getColorValue());
|
||||
this.b = toRGBAB(formatting.getColorValue());
|
||||
this.a = toRGBAA(formatting.getColorValue());
|
||||
} else {
|
||||
this.r = 255;
|
||||
this.g = 255;
|
||||
this.b = 255;
|
||||
this.a = 255;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a color from Minecraft TextColor
|
||||
*/
|
||||
public ColorUtils(TextColor textColor) {
|
||||
this.r = toRGBAR(textColor.getRgb());
|
||||
this.g = toRGBAG(textColor.getRgb());
|
||||
this.b = toRGBAB(textColor.getRgb());
|
||||
this.a = toRGBAA(textColor.getRgb());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a color from Minecraft Style
|
||||
* If style has no color, creates white
|
||||
*/
|
||||
public ColorUtils(Style style) {
|
||||
TextColor textColor = style.getColor();
|
||||
if (textColor == null) {
|
||||
this.r = 255;
|
||||
this.g = 255;
|
||||
this.b = 255;
|
||||
this.a = 255;
|
||||
} else {
|
||||
this.r = toRGBAR(textColor.getRgb());
|
||||
this.g = toRGBAG(textColor.getRgb());
|
||||
this.b = toRGBAB(textColor.getRgb());
|
||||
this.a = toRGBAA(textColor.getRgb());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a hex color string (e.g., "#ffaaff" or "ffaaff") into an RGB integer.
|
||||
*
|
||||
* @param hexColor Hex color string to convert
|
||||
* @return Integer representation of the color
|
||||
*/
|
||||
public static int parseHexColor(String hexColor) {
|
||||
String cleanHex = hexColor.startsWith("#") ? hexColor.substring(1) : hexColor;
|
||||
return Integer.parseInt(cleanHex, 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates text with a gradient effect between two colors.
|
||||
* Each character in the text will be colored with a color interpolated
|
||||
* between startColor and endColor based on its position.
|
||||
*
|
||||
* @param text The text to apply the gradient to
|
||||
* @param startColor The color to start the gradient from
|
||||
* @param endColor The color to end the gradient with
|
||||
* @return A MutableText object with the gradient effect applied
|
||||
*/
|
||||
public static MutableText createGradientText(String text, int startColor, int endColor) {
|
||||
MutableText result = Text.empty();
|
||||
int length = text.length();
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
int color = interpolateColor(startColor, endColor, (float) i / (length - 1));
|
||||
result.append(Text.literal(String.valueOf(text.charAt(i)))
|
||||
.setStyle(Style.EMPTY.withColor(TextColor.fromRgb(color))));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interpolates between two colors based on a progress value.
|
||||
*
|
||||
* @param color1 The starting color
|
||||
* @param color2 The ending color
|
||||
* @param progress The interpolation progress (0.0 to 1.0)
|
||||
* @return The interpolated color as a packed RGB integer
|
||||
*/
|
||||
private static int interpolateColor(int color1, int color2, float progress) {
|
||||
int r = (int) ((color1 >> 16 & 0xFF) + (color2 >> 16 & 0xFF - (color1 >> 16 & 0xFF)) * progress);
|
||||
int g = (int) ((color1 >> 8 & 0xFF) + (color2 >> 8 & 0xFF - (color1 >> 8 & 0xFF)) * progress);
|
||||
int b = (int) ((color1 & 0xFF) + (color2 & 0xFF - (color1 & 0xFF)) * progress);
|
||||
|
||||
return (r << 16) | (g << 8) | b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts RGBA components to packed integer
|
||||
*/
|
||||
public static int fromRGBA(int r, int g, int b, int a) {
|
||||
return (r << 16) + (g << 8) + (b) + (a << 24);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts red component from packed color
|
||||
*/
|
||||
public static int toRGBAR(int color) {
|
||||
return (color >> 16) & 0x000000FF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts green component from packed color
|
||||
*/
|
||||
public static int toRGBAG(int color) {
|
||||
return (color >> 8) & 0x000000FF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts blue component from packed color
|
||||
*/
|
||||
public static int toRGBAB(int color) {
|
||||
return (color) & 0x000000FF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts alpha component from packed color
|
||||
*/
|
||||
public static int toRGBAA(int color) {
|
||||
return (color >> 24) & 0x000000FF;
|
||||
}
|
||||
|
||||
public static int hsvToRgb (int hue, int saturation, int value) {
|
||||
Color color = Color.getHSBColor(hue / 360.0f, saturation / 100.0f, value / 100.0f);
|
||||
return color.getRGB() & 0xFFFFFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a color from HSV (Hue, Saturation, Value) values
|
||||
* @param h Hue (0-360)
|
||||
* @param s Saturation (0-1)
|
||||
* @param v Value (0-1)
|
||||
*/
|
||||
public static ColorUtils fromHsv(double h, double s, double v) {
|
||||
double hh, p, q, t, ff;
|
||||
int i;
|
||||
double r, g, b;
|
||||
|
||||
if (s <= 0.0) { // < is bogus, just shuts up warnings
|
||||
r = v;
|
||||
g = v;
|
||||
b = v;
|
||||
return new ColorUtils((int) (r * 255), (int) (g * 255), (int) (b * 255), 255);
|
||||
}
|
||||
hh = h;
|
||||
if (hh >= 360.0) hh = 0.0;
|
||||
hh /= 60.0;
|
||||
i = (int) hh;
|
||||
ff = hh - i;
|
||||
p = v * (1.0 - s);
|
||||
q = v * (1.0 - (s * ff));
|
||||
t = v * (1.0 - (s * (1.0 - ff)));
|
||||
|
||||
switch (i) {
|
||||
case 0:
|
||||
r = v;
|
||||
g = t;
|
||||
b = p;
|
||||
break;
|
||||
case 1:
|
||||
r = q;
|
||||
g = v;
|
||||
b = p;
|
||||
break;
|
||||
case 2:
|
||||
r = p;
|
||||
g = v;
|
||||
b = t;
|
||||
break;
|
||||
case 3:
|
||||
r = p;
|
||||
g = q;
|
||||
b = v;
|
||||
break;
|
||||
case 4:
|
||||
r = t;
|
||||
g = p;
|
||||
b = v;
|
||||
break;
|
||||
case 5:
|
||||
default:
|
||||
r = v;
|
||||
g = p;
|
||||
b = q;
|
||||
break;
|
||||
}
|
||||
return new ColorUtils((int) (r * 255), (int) (g * 255), (int) (b * 255), 255);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all color components at once
|
||||
*/
|
||||
public ColorUtils set(int r, int g, int b, int a) {
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
this.a = a;
|
||||
|
||||
validate();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// Setter methods for individual components
|
||||
public ColorUtils r(int r) {
|
||||
this.r = r;
|
||||
validate();
|
||||
return this;
|
||||
}
|
||||
|
||||
public ColorUtils g(int g) {
|
||||
this.g = g;
|
||||
validate();
|
||||
return this;
|
||||
}
|
||||
|
||||
public ColorUtils b(int b) {
|
||||
this.b = b;
|
||||
validate();
|
||||
return this;
|
||||
}
|
||||
|
||||
public ColorUtils a(int a) {
|
||||
this.a = a;
|
||||
validate();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets color from another ColorUtils instance
|
||||
*/
|
||||
@Override
|
||||
public ColorUtils set(ColorUtils value) {
|
||||
r = value.r;
|
||||
g = value.g;
|
||||
b = value.b;
|
||||
a = value.a;
|
||||
|
||||
validate();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses color from string in format "r,g,b" or "r,g,b,a"
|
||||
* @return true if parsing was successful
|
||||
*/
|
||||
public boolean parse(String text) {
|
||||
String[] split = text.split(",");
|
||||
if (split.length != 3 && split.length != 4) return false;
|
||||
|
||||
try {
|
||||
// Not assigned directly because of exception handling
|
||||
int r = Integer.parseInt(split[0]);
|
||||
int g = Integer.parseInt(split[1]);
|
||||
int b = Integer.parseInt(split[2]);
|
||||
int a = split.length == 4 ? Integer.parseInt(split[3]) : this.a;
|
||||
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
this.a = a;
|
||||
|
||||
return true;
|
||||
} catch (NumberFormatException ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a copy of this color
|
||||
*/
|
||||
@Override
|
||||
public ColorUtils copy() {
|
||||
return new ColorUtils(r, g, b, a);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts to Minecraft TextColor
|
||||
*/
|
||||
public TextColor toTextColor() {
|
||||
return TextColor.fromRgb(getPacked());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Style with this color
|
||||
*/
|
||||
public Style toStyle() {
|
||||
return Style.EMPTY.withColor(toTextColor());
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies this color to an existing Style
|
||||
*/
|
||||
public Style styleWith(Style style) {
|
||||
return style.withColor(toTextColor());
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures all color components are within valid range (0-255)
|
||||
*/
|
||||
public void validate() {
|
||||
if (r < 0) r = 0;
|
||||
else if (r > 255) r = 255;
|
||||
|
||||
if (g < 0) g = 0;
|
||||
else if (g > 255) g = 255;
|
||||
|
||||
if (b < 0) b = 0;
|
||||
else if (b > 255) b = 255;
|
||||
|
||||
if (a < 0) a = 0;
|
||||
else if (a > 255) a = 255;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts to Vec3d with normalized components (0.0-1.0)
|
||||
*/
|
||||
public Vec3d getVec3d() {
|
||||
return new Vec3d(r / 255.0, g / 255.0, b / 255.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts to Vector3f with normalized components (0.0-1.0)
|
||||
*/
|
||||
public Vector3f getVec3f() {
|
||||
return new Vector3f(r / 255.0f, g / 255.0f, b / 255.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the color as a packed RGBA integer
|
||||
*/
|
||||
public int getPacked() {
|
||||
return fromRGBA(r, g, b, a);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the color to NBT
|
||||
*/
|
||||
@Override
|
||||
public NbtCompound toTag() {
|
||||
NbtCompound tag = new NbtCompound();
|
||||
|
||||
tag.putInt("r", r);
|
||||
tag.putInt("g", g);
|
||||
tag.putInt("b", b);
|
||||
tag.putInt("a", a);
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes the color from NBT
|
||||
*/
|
||||
@Override
|
||||
public ColorUtils fromTag(NbtCompound tag) {
|
||||
r = tag.getInt("r");
|
||||
g = tag.getInt("g");
|
||||
b = tag.getInt("b");
|
||||
a = tag.getInt("a");
|
||||
|
||||
validate();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return r + " " + g + " " + b + " " + a;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
ColorUtils color = (ColorUtils) o;
|
||||
|
||||
return r == color.r && g == color.g && b == color.b && a == color.a;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = r;
|
||||
result = 31 * result + g;
|
||||
result = 31 * result + b;
|
||||
result = 31 * result + a;
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package land.chipmunk.chipmunkmod.util.misc;
|
||||
|
||||
public interface Copyable<T extends Copyable<T>> {
|
||||
T set(T value);
|
||||
|
||||
T copy();
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
package land.chipmunk.chipmunkmod.util.misc;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public interface Hexadecimal {
|
||||
/** Hexadecimal digits lookup table for faster conversion */
|
||||
char[] HEX_CHARS = "0123456789abcdef".toCharArray();
|
||||
|
||||
/**
|
||||
* Encodes a single byte to a two-character hexadecimal string.
|
||||
* @param b The byte to encode
|
||||
* @return A two-character hexadecimal string
|
||||
*/
|
||||
static @NotNull String encode(byte b) {
|
||||
return new String(new char[] {
|
||||
HEX_CHARS[(b >> 4) & 0xF],
|
||||
HEX_CHARS[b & 0xF]
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a byte array to a hexadecimal string.
|
||||
* @param array The byte array to encode
|
||||
* @return The hexadecimal string representation
|
||||
* @throws IllegalArgumentException if array is null
|
||||
*/
|
||||
static @NotNull Object encode(byte @NotNull [] array) {
|
||||
char[] hexChars = new char[array.length * 2];
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
int v = array[i] & 0xFF;
|
||||
hexChars[i * 2] = HEX_CHARS[v >>> 4];
|
||||
hexChars[i * 2 + 1] = HEX_CHARS[v & 0xF];
|
||||
}
|
||||
return new String(hexChars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a string to hexadecimal using UTF-8 encoding.
|
||||
* @param text The string to encode
|
||||
* @return The hexadecimal representation of the string
|
||||
* @throws IllegalArgumentException if text is null
|
||||
*/
|
||||
static @NotNull String encodeString(@NotNull String text) {
|
||||
return (String) encode(text.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a hexadecimal string to a byte array.
|
||||
* @param hex The hexadecimal string to decode
|
||||
* @return The decoded byte array, or null if the input is invalid
|
||||
*/
|
||||
static byte @Nullable [] decode(@Nullable String hex) {
|
||||
if (hex == null || hex.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Remove any spaces or non-hex characters
|
||||
hex = hex.replaceAll("[^0-9A-Fa-f]", "");
|
||||
|
||||
// Check if we have a valid hex string (must be even length)
|
||||
if (hex.length() % 2 != 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] result = new byte[hex.length() / 2];
|
||||
try {
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[i] = (byte) Integer.parseInt(
|
||||
hex.substring(i * 2, i * 2 + 2),
|
||||
16
|
||||
);
|
||||
}
|
||||
return result;
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a hexadecimal string to a UTF-8 string.
|
||||
* @param hex The hexadecimal string to decode
|
||||
* @return The decoded string, or null if the input is invalid
|
||||
*/
|
||||
static @Nullable String decodeString(@Nullable String hex) {
|
||||
byte[] bytes = decode(hex);
|
||||
if (bytes == null) {
|
||||
return null;
|
||||
}
|
||||
return new String(bytes, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a string is a valid hexadecimal string.
|
||||
* @param hex The string to check
|
||||
* @return true if the string is valid hexadecimal, false otherwise
|
||||
*/
|
||||
static boolean isValid(@Nullable String hex) {
|
||||
if (hex == null || hex.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
return hex.matches("^[0-9A-Fa-f]+$") && hex.length() % 2 == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a hexadecimal string with specified separator every n characters.
|
||||
* @param hex The hexadecimal string to format
|
||||
* @param every Number of characters between separators
|
||||
* @param separator The separator to use
|
||||
* @return The formatted string, or null if input is invalid
|
||||
*/
|
||||
static @Nullable String format(@Nullable String hex, int every, char separator) {
|
||||
if (!isValid(hex) || every <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
StringBuilder formatted = new StringBuilder();
|
||||
for (int i = 0; i < hex.length(); i++) {
|
||||
if (i > 0 && i % every == 0) {
|
||||
formatted.append(separator);
|
||||
}
|
||||
formatted.append(hex.charAt(i));
|
||||
}
|
||||
return formatted.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package land.chipmunk.chipmunkmod.util.misc;
|
||||
|
||||
public class MathUtils {
|
||||
public static double clamp (double value, double min, double max) {
|
||||
return Math.max(Math.min(value, max), min);
|
||||
}
|
||||
|
||||
public static float clamp (float value, float min, float max) {
|
||||
return Math.max(Math.min(value, max), min);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package land.chipmunk.chipmunkmod.util.misc;
|
||||
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
|
||||
public interface Serializable<T> {
|
||||
NbtCompound toTag();
|
||||
|
||||
T fromTag(NbtCompound tag);
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package land.chipmunk.chipmunkmod.util.misc;
|
||||
|
||||
import com.google.common.base.Suppliers;
|
||||
import net.minecraft.registry.DynamicRegistryManager;
|
||||
import net.minecraft.registry.Registries;
|
||||
import net.minecraft.text.MutableText;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
public class TextUtil {
|
||||
public static MutableText fromJson (String json) {
|
||||
return Text.Serialization.fromJson(
|
||||
json,
|
||||
Suppliers.ofInstance(DynamicRegistryManager.of(Registries.REGISTRIES)).get()
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package land.chipmunk.chipmunkmod.util.misc;
|
||||
|
||||
public class Version {
|
||||
private final String string;
|
||||
private final int[] numbers;
|
||||
|
||||
public Version(String string) {
|
||||
this.string = string;
|
||||
this.numbers = new int[3];
|
||||
|
||||
String[] split = string.split("\\.");
|
||||
if (split.length != 3) throw new IllegalArgumentException("Version string needs to have 3 numbers.");
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
try {
|
||||
numbers[i] = Integer.parseInt(split[i]);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("Failed to parse version string.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isZero() {
|
||||
return numbers[0] == 0 && numbers[1] == 0 && numbers[2] == 0;
|
||||
}
|
||||
|
||||
public boolean isHigherThan(Version version) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (numbers[i] > version.numbers[i]) return true;
|
||||
if (numbers[i] < version.numbers[i]) return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return string;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package land.chipmunk.chipmunkmod.util.network;
|
||||
|
||||
import javax.net.ssl.KeyManager;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
public class DownloadUtils {
|
||||
private static class DefaultTrustManager implements X509TrustManager {
|
||||
|
||||
@Override
|
||||
public void checkClientTrusted(X509Certificate[] arg0, String arg1) {}
|
||||
|
||||
@Override
|
||||
public void checkServerTrusted(X509Certificate[] arg0, String arg1) {}
|
||||
|
||||
@Override
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] DownloadToByteArray(URL url) throws IOException, KeyManagementException, NoSuchAlgorithmException {
|
||||
SSLContext ctx = SSLContext.getInstance("TLS");
|
||||
ctx.init(new KeyManager[0], new TrustManager[] {new DefaultTrustManager()}, new SecureRandom());
|
||||
SSLContext.setDefault(ctx);
|
||||
URLConnection conn = url.openConnection();
|
||||
conn.setConnectTimeout(5000);
|
||||
conn.setReadTimeout(10000);
|
||||
conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:86.0) Gecko/20100101 Firefox/86.0");
|
||||
|
||||
try (BufferedInputStream downloadStream = new BufferedInputStream(conn.getInputStream())) {
|
||||
ByteArrayOutputStream byteArrayStream = new ByteArrayOutputStream();
|
||||
byte[] buf = new byte[1024];
|
||||
int n;
|
||||
while ((n = downloadStream.read(buf)) > 0) {
|
||||
byteArrayStream.write(buf, 0, n);
|
||||
|
||||
if (Thread.interrupted()) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return byteArrayStream.toByteArray();
|
||||
}
|
||||
// Closing a ByteArrayInputStream has no effect, so I do not close it.
|
||||
}
|
||||
|
||||
public static InputStream DownloadToInputStream(URL url) throws KeyManagementException, NoSuchAlgorithmException, IOException {
|
||||
return new ByteArrayInputStream(DownloadToByteArray(url));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package land.chipmunk.chipmunkmod.util.network;
|
||||
|
||||
import com.mojang.brigadier.tree.CommandNode;
|
||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
||||
|
||||
public class ServerUtil {
|
||||
public static boolean serverHasCommand(String name) {
|
||||
final MinecraftClient client = MinecraftClient.getInstance();
|
||||
final ClientPlayNetworkHandler networkHandler = client.getNetworkHandler();
|
||||
|
||||
if (networkHandler == null) return false;
|
||||
|
||||
for (CommandNode node : networkHandler.getCommandDispatcher().getRoot().getChildren()) {
|
||||
if (!(node instanceof LiteralCommandNode literal)) continue;
|
||||
|
||||
if (literal.getLiteral().equals(name)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
package land.chipmunk.chipmunkmod.util.player;
|
||||
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import land.chipmunk.chipmunkmod.ChipmunkMod;
|
||||
import land.chipmunk.chipmunkmod.util.misc.ColorUtils;
|
||||
import net.minecraft.text.*;
|
||||
import net.minecraft.util.Formatting;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static land.chipmunk.chipmunkmod.ChipmunkMod.MCInstance;
|
||||
|
||||
public class ChatUtils {
|
||||
// Define the main prefix that appears before all messages
|
||||
private static final Text PREFIX = Text.empty()
|
||||
.setStyle(Style.EMPTY.withColor(TextColor.fromRgb(ColorUtils.GRAY)))
|
||||
.append("[")
|
||||
.append(ColorUtils.createGradientText(ChipmunkMod.NAME, ColorUtils.PRIMARY, ColorUtils.SECONDARY))
|
||||
.append("] ");
|
||||
|
||||
/**
|
||||
* Basic message sending methods with predefined colors
|
||||
*/
|
||||
// Info messages
|
||||
public static void info(String message, Object... args) {
|
||||
sendMsg(Style.EMPTY.withColor(Formatting.GRAY), message, args);
|
||||
}
|
||||
public static void infoPrefix(String prefix, String message, Object... args) {
|
||||
sendMsg(prefix, Style.EMPTY.withColor(TextColor.fromRgb(ColorUtils.PRIMARY)), Style.EMPTY.withColor(Formatting.GRAY), message, args); }
|
||||
|
||||
// Warning messages
|
||||
public static void warning(String message, Object... args) {
|
||||
sendMsg(Style.EMPTY.withColor(TextColor.fromRgb(ColorUtils.WARNING)), message, args);
|
||||
}
|
||||
public static void warningPrefix(String prefix, String message, Object... args) {
|
||||
sendMsg(prefix, Style.EMPTY.withColor(TextColor.fromRgb(ColorUtils.PRIMARY)), Style.EMPTY.withColor(TextColor.fromRgb(ColorUtils.WARNING)), message, args);
|
||||
}
|
||||
|
||||
// Error messages
|
||||
public static void error(String message, Object... args) {
|
||||
sendMsg(Style.EMPTY.withColor(TextColor.fromRgb(ColorUtils.DANGER)), message, args);
|
||||
}
|
||||
public static void errorPrefix(String prefix, String message, Object... args) {
|
||||
sendMsg(prefix, Style.EMPTY.withColor(TextColor.fromRgb(ColorUtils.PRIMARY)), Style.EMPTY.withColor(TextColor.fromRgb(ColorUtils.DANGER)), message, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Core message sending methods
|
||||
*/
|
||||
public static void sendMsg(Style color, String message, Object... args) {
|
||||
sendMsg(null, null, color, message, args);
|
||||
}
|
||||
|
||||
public static void sendMsg(@Nullable String prefixTitle, @Nullable Style prefixStyle, Style messageStyle, String messageContent, Object... args) {
|
||||
// Combine the args to a single message
|
||||
String formattedContent = String.format(messageContent, args);
|
||||
MutableText message = formatMsg(formattedContent, messageStyle);
|
||||
|
||||
sendMsg(prefixTitle, prefixStyle, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Final message sending method that handles all formatting and sends to chat.
|
||||
* Format:
|
||||
* Without custom prefix: "[ModPrefix] message"
|
||||
* With custom prefix: "[ModPrefix] [CustomPrefix] message"
|
||||
*/
|
||||
public static void sendMsg(@Nullable String prefixTitle, @Nullable Style prefixStyle, Text msg) {
|
||||
// Safety check - ensure we're in-game
|
||||
if (MCInstance.world == null) return;
|
||||
|
||||
MutableText message = Text.empty();
|
||||
message.append(PREFIX);
|
||||
if (prefixTitle != null) {
|
||||
message.append(getCustomPrefix(prefixTitle, prefixStyle)); // Add custom prefix if provided
|
||||
}
|
||||
message.append(msg);
|
||||
|
||||
// Send to chat
|
||||
MCInstance.inGameHud.getChatHud().addMessage(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a custom prefix
|
||||
*/
|
||||
private static MutableText getCustomPrefix(String prefixTitle, Style prefixStyle) {
|
||||
MutableText prefix = Text.empty();
|
||||
// Set brackets to dark gray
|
||||
prefix.setStyle(Style.EMPTY.withColor(TextColor.fromRgb(ColorUtils.GRAY)));
|
||||
prefix.append("[");
|
||||
|
||||
// Add the prefix title with its color
|
||||
MutableText moduleTitle = Text.literal(prefixTitle);
|
||||
moduleTitle.setStyle(prefixStyle);
|
||||
prefix.append(moduleTitle);
|
||||
|
||||
prefix.append("] ");
|
||||
|
||||
return prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats message text with support for formatting tags
|
||||
* Supported tags:
|
||||
* [default]/[def] - Reset to default color
|
||||
* [highlight]/[hl] - White text
|
||||
* [underline]/[ul] - Underlined text
|
||||
* [bold]/[b] - Bold text
|
||||
*/
|
||||
private static MutableText formatMsg(String message, Style defaultStyle) {
|
||||
Style style = defaultStyle;
|
||||
StringReader reader = new StringReader(message);
|
||||
StringBuilder result = new StringBuilder();
|
||||
MutableText text = Text.empty();
|
||||
boolean formatting = false;
|
||||
|
||||
while (reader.canRead()) {
|
||||
char ch = reader.read();
|
||||
if (ch == '[') {
|
||||
text.append(Text.literal(result.toString()).setStyle(style));
|
||||
result.setLength(0);
|
||||
result.append(ch);
|
||||
formatting = true;
|
||||
} else {
|
||||
result.append(ch);
|
||||
|
||||
if (formatting && ch == ']') {
|
||||
String tag = result.toString().toLowerCase();
|
||||
switch (tag) {
|
||||
case "[default]", "[def]" -> {
|
||||
style = defaultStyle;
|
||||
result.setLength(0);
|
||||
}
|
||||
case "[highlight]", "[hl]" -> {
|
||||
style = style.withFormatting(Formatting.WHITE);
|
||||
result.setLength(0);
|
||||
}
|
||||
case "[underline]", "[ul]" -> {
|
||||
style = style.withFormatting(Formatting.UNDERLINE);
|
||||
result.setLength(0);
|
||||
}
|
||||
case "[bold]", "[b]" -> {
|
||||
style = style.withFormatting(Formatting.BOLD);
|
||||
result.setLength(0);
|
||||
}
|
||||
}
|
||||
formatting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!result.isEmpty()) {
|
||||
text.append(Text.literal(result.toString()).setStyle(style));
|
||||
}
|
||||
return text;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package land.chipmunk.chipmunkmod.util.player;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.UUID;
|
||||
|
||||
public class UUIDUtil {
|
||||
public static int[] intArray (UUID uuid) {
|
||||
final ByteBuffer buffer = ByteBuffer.wrap(new byte[16]);
|
||||
buffer.putLong(0, uuid.getMostSignificantBits());
|
||||
buffer.putLong(8, uuid.getLeastSignificantBits());
|
||||
|
||||
final int[] intArray = new int[4];
|
||||
for (int i = 0; i < intArray.length; i++) intArray[i] = buffer.getInt();
|
||||
|
||||
return intArray;
|
||||
}
|
||||
|
||||
public static String snbt (UUID uuid) {
|
||||
int[] array = intArray(uuid);
|
||||
return "[I;" + array[0] + "," + array[1] + "," + array[2] + "," + array[3] + "]"; // TODO: improve lol
|
||||
}
|
||||
|
||||
public static String selector (UUID uuid) { return "@a[limit=1,nbt={UUID:" + snbt(uuid) + "}]"; }
|
||||
}
|
BIN
src/main/resources/assets/chipmunkmod/icon.png
Normal file
BIN
src/main/resources/assets/chipmunkmod/icon.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 47 KiB |
BIN
src/main/resources/assets/chipmunkmod/icon2.png
Normal file
BIN
src/main/resources/assets/chipmunkmod/icon2.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 62 KiB |
33
src/main/resources/chipmunkmod.mixins.json
Normal file
33
src/main/resources/chipmunkmod.mixins.json
Normal file
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"required": true,
|
||||
"minVersion": "0.8",
|
||||
"package": "land.chipmunk.chipmunkmod.mixin",
|
||||
"compatibilityLevel": "JAVA_21",
|
||||
"client": [
|
||||
"ChatInputSuggestorMixin",
|
||||
"ChatScreenMixin",
|
||||
"ClientConnectionAccessor",
|
||||
"ClientConnectionInvoker",
|
||||
"ClientConnectionMixin",
|
||||
"ClientPlayerEntityMixin",
|
||||
"ClientPlayNetworkHandlerAccessor",
|
||||
"ClientPlayNetworkHandlerMixin",
|
||||
"CommandDispatcherMixin",
|
||||
"ElderGuardianAppearanceParticleMixin",
|
||||
"IdentifierMixin",
|
||||
"MinecraftClientAccessor",
|
||||
"PlayerListEntryAccessor",
|
||||
"SoundSystemMixin",
|
||||
"StringHelperMixin",
|
||||
"TextFieldWidgetMixin",
|
||||
"TextMixin",
|
||||
"TextSerializerMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
},
|
||||
"mixins": [
|
||||
"TextMixin",
|
||||
"TextSerializerMixin"
|
||||
]
|
||||
}
|
84
src/main/resources/default_config.json
Normal file
84
src/main/resources/default_config.json
Normal file
|
@ -0,0 +1,84 @@
|
|||
{
|
||||
"client": {
|
||||
"prefix": ";",
|
||||
"autoSkinUsername": "off",
|
||||
"colors": {
|
||||
"PRIMARY": "#3b82f6",
|
||||
"SECONDARY": "#a855f7",
|
||||
"DANGER": "#f43f5e",
|
||||
"SUCCESS": "#10b981",
|
||||
"GRAY": "#353b40"
|
||||
},
|
||||
|
||||
"customChat": {
|
||||
"format": {
|
||||
"color": "#353B40",
|
||||
"translate": "[%s] %s › %s",
|
||||
"with": [
|
||||
{
|
||||
"translate": "%s%s%s",
|
||||
"with": [
|
||||
{ "color": "#3B82F6", "text": "Z", "bold": false },
|
||||
{ "color": "#726CF7", "text": "E", "bold": false },
|
||||
{ "color": "#A855F7", "text": "N", "bold": false }
|
||||
],
|
||||
"clickEvent": {
|
||||
"action": "open_url",
|
||||
"value": "https://zenzoya.netlify.app/"
|
||||
},
|
||||
"hoverEvent": {
|
||||
"action": "show_text",
|
||||
"contents": [
|
||||
{
|
||||
"color": "#726CF7",
|
||||
"text": "Click to go to my Website :3",
|
||||
"underlined": true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{ "color": "#726CF7", "selector": "USERNAME" },
|
||||
{
|
||||
"color": "#C8E6EA",
|
||||
"text": "MESSAGE",
|
||||
"clickEvent": { "action": "copy_to_clipboard", "value": "MESSAGE" },
|
||||
"hoverEvent": {
|
||||
"action": "show_text",
|
||||
"contents": [
|
||||
{ "color": "#726CF7", "text": "Click to Copy message to Clipboard!" }
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"core": {
|
||||
"relativeArea": {
|
||||
"start": {
|
||||
"x": 8,
|
||||
"y": 0,
|
||||
"z": 8
|
||||
},
|
||||
"end": {
|
||||
"x": 23,
|
||||
"y": 2,
|
||||
"z": 23
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"bots": {
|
||||
"lambda": { "prefix": "\\", "key": "" },
|
||||
"chomens": { "prefix": "*", "key": null, "authKey": null, "formatKey": null },
|
||||
"nbot": { "prefix": "?", "key": null },
|
||||
"qbot": {"prefix":"}", "key": null},
|
||||
"fnfboyfriend": { "prefix": "~", "key": null },
|
||||
"hbot": { "prefix": "#", "key": null },
|
||||
"sbot": { "prefix": ":", "key": null },
|
||||
"testbot": { "prefix": "-", "webhookUrl": null },
|
||||
"chipmunk": { "prefix": "'", "key": null },
|
||||
"kittycorp": { "prefix": "^", "key": null }
|
||||
}
|
||||
}
|
43
src/main/resources/fabric.mod.json
Normal file
43
src/main/resources/fabric.mod.json
Normal file
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "zenmod",
|
||||
"version": "${version}",
|
||||
|
||||
"name": "ZenMod",
|
||||
"description": "My fork of ChipmunkMod\n\nThanks for _ChipMC_, chayapak, 7cc5c4f330d47060\nbecause they are the base contributor of this fork",
|
||||
"authors": [
|
||||
"_ChipMC_",
|
||||
"chayapak",
|
||||
"7cc5c4f330d47060",
|
||||
"ZenZoya"
|
||||
],
|
||||
"contact": {
|
||||
"homepage": "https://name3.chipmunk.land/",
|
||||
"Zen's homepage" : "https://zenzoya.netlify.app/",
|
||||
"sources": "https://github.com/vortres/zenmod",
|
||||
"issues": "https://github.com/vortres/zenmod/issues"
|
||||
},
|
||||
|
||||
"license": "MIT",
|
||||
"icon": "assets/chipmunkmod/icon2.png",
|
||||
|
||||
"environment": "*",
|
||||
"entrypoints": {
|
||||
"main": [
|
||||
"land.chipmunk.chipmunkmod.ChipmunkMod"
|
||||
]
|
||||
},
|
||||
"mixins": [
|
||||
"chipmunkmod.mixins.json"
|
||||
],
|
||||
|
||||
"depends": {
|
||||
"fabricloader": ">=0.16.5",
|
||||
"fabric-api": "*",
|
||||
"minecraft": ">=1.21",
|
||||
"java": ">=21"
|
||||
},
|
||||
"suggests": {
|
||||
"another-mod": "*"
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue