Initial commit

This commit is contained in:
ZenZoya 2024-12-20 01:16:29 +08:00
commit 3e884e6711
99 changed files with 7466 additions and 0 deletions
.gitattributes.gitignoreLICENSEREADME.mdbuild.gradlegradle.properties
gradle/wrapper
gradlewgradlew.batsettings.gradle
src/main
java/land/chipmunk/chipmunkmod
ChipmunkMod.javaConfiguration.java
command
data
listeners
mixin
modules
song
util
resources

2
.gitattributes vendored Normal file
View file

@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto

33
.gitignore vendored Normal file
View 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
View 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
View 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
View 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
View 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

Binary file not shown.

View 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
View 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
View 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
View file

@ -0,0 +1,10 @@
pluginManagement {
repositories {
maven {
name = 'Fabric'
url = 'https://maven.fabricmc.net/'
}
mavenCentral()
gradlePluginPortal()
}
}

View 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);
}
}
}

View 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;
}
}
}

View file

@ -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); }
}

View file

@ -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; }
}

View file

@ -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; }
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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 + "\"");
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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");
}
}

View file

@ -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!");
}
}

View file

@ -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);
}
}

View file

@ -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")))))
);
}
}

View 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;
}
}

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -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());
}
}

View file

@ -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
}
}

View file

@ -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 () {}
}

View file

@ -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);
}
}

View file

@ -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);
}
}
}

View file

@ -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();
}
}

View file

@ -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 ();
}

View file

@ -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();
}
}

View file

@ -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);
}
}
}

View file

@ -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();
}

View file

@ -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();
}
}
}

View file

@ -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);
}
}
}

View file

@ -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();
// }
}
}

View file

@ -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();
}
}

View file

@ -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();
}
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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();
}
}

View file

@ -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();
}
}

View file

@ -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();
}
}

View 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());
}
}

View file

@ -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();
}
}

View 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);
}
}

View file

@ -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();
}
}

View file

@ -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) {}
}
}

View 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;
}
}

View file

@ -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);
}
};
}
}
}

View 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() );
}
}
}

View file

@ -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();
}
}

View file

@ -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();
}
}

View file

@ -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) {}
}
}

View 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);
}
}
}

View file

@ -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();
}
}

View file

@ -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) {}
}
}

View 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;
}
}

View 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();
}
}
}

View 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());
}
}
}
}

View file

@ -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);
}
}

View file

@ -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++; }
}

View 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];
}
}

View 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);
}
}

View 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;
}
}

View 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;
}
}

View 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();
}
}

View file

@ -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();
}
}

View file

@ -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);
}
}

View 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 { };
}

View file

@ -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
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -0,0 +1,7 @@
package land.chipmunk.chipmunkmod.util.misc;
public interface Copyable<T extends Copyable<T>> {
T set(T value);
T copy();
}

View file

@ -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();
}
}

View file

@ -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);
}
}

View file

@ -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);
}

View file

@ -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()
);
}
}

View file

@ -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;
}
}

View file

@ -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));
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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) + "}]"; }
}

Binary file not shown.

After

(image error) Size: 47 KiB

Binary file not shown.

After

(image error) Size: 62 KiB

View 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"
]
}

View 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 }
}
}

View 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": "*"
}
}