commit f4f043e4d6bc3ccf0e2850b41f8893e604ef72dd Author: Kalle Struik Date: Sun Jan 30 18:09:54 2022 +0100 Initial commit. diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..304999b --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,60 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + kotlin("jvm") version "1.4.32" + id("com.github.johnrengelman.shadow") version "5.2.0" +} + +group = "nl.kallestruik" +version = "1.0" + +repositories { + mavenCentral() + mavenLocal() + + maven("https://papermc.io/repo/repository/maven-public/") + maven("https://repo.aikar.co/content/groups/aikar/") + maven("https://repo.dmulloy2.net/repository/public/") +} + +dependencies { + implementation("co.aikar:acf-paper:0.5.0-SNAPSHOT") + compileOnly("com.destroystokyo.paper:paper:1.16.5-R0.1-SNAPSHOT") + compileOnly("com.comphenix.protocol:ProtocolLib:4.6.0") + compileOnly(kotlin("stdlib-jdk8")) + + compileOnly("nl.kallestruik:DLib:1.0") + + testImplementation(kotlin("test-junit5")) + testImplementation("org.junit.jupiter:junit-jupiter-api:5.6.0") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.6.0") +} + +tasks.compileJava { + options.compilerArgs.add("-parameters") +} + +tasks.compileKotlin { + kotlinOptions.javaParameters = true +} + +tasks.test { + useJUnitPlatform() +} + +tasks.shadowJar { + relocate("co.aikar.commands", "nl.kallestruik.dtweaks.acf") + relocate("co.aikar.locales", "nl.kallestruik.dtweaks.locales") +} + +tasks.build { + dependsOn(tasks.shadowJar) +} + +tasks.withType { + kotlinOptions.jvmTarget = "11" +} + +tasks.processResources { + expand("version" to project.version) +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..7fc6f1f --- /dev/null +++ b/gradle.properties @@ -0,0 +1 @@ +kotlin.code.style=official diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..e708b1c Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..da9702f --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..4f906e0 --- /dev/null +++ b/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or 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 UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# 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 + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..fd693a2 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,3 @@ + +rootProject.name = "DTweaks" + diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/Const.kt b/src/main/kotlin/nl/kallestruik/dtweaks/Const.kt new file mode 100644 index 0000000..d6dbdf1 --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/Const.kt @@ -0,0 +1,6 @@ +package nl.kallestruik.dtweaks + +object Const { + const val TRAMPLE_ENABLED_FILE_NAME = "tramplestore.yml" + const val TWEAK_STATE_FILE_NAME = "tweak_states.yml" +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/DTweaks.kt b/src/main/kotlin/nl/kallestruik/dtweaks/DTweaks.kt new file mode 100644 index 0000000..155bb89 --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/DTweaks.kt @@ -0,0 +1,139 @@ +package nl.kallestruik.dtweaks + +import co.aikar.commands.ConditionFailedException +import co.aikar.commands.PaperCommandManager +import nl.kallestruik.dlib.DUtil +import nl.kallestruik.dlib.MathHelper +import nl.kallestruik.dlib.ReflectionUtil +import nl.kallestruik.dtweaks.commands.CommandMobcaps +import nl.kallestruik.dtweaks.commands.CommandPlayer +import nl.kallestruik.dtweaks.commands.CommandPocketdim +import nl.kallestruik.dtweaks.commands.CommandToggletrample +import nl.kallestruik.dtweaks.managers.FakePlayerManager +import nl.kallestruik.dtweaks.managers.PocketDimensionManager +import nl.kallestruik.dtweaks.managers.TrampleManager +import nl.kallestruik.dtweaks.managers.TweakManager +import nl.kallestruik.dtweaks.tweaks.craftingtweaks.* +import nl.kallestruik.dtweaks.tweaks.croptweaks.* +import nl.kallestruik.dtweaks.tweaks.dispsensertweaks.DispensersCanPlantSaplings +import nl.kallestruik.dtweaks.tweaks.miscellaneoustweaks.ArmorStandArmorSwapping +import nl.kallestruik.dtweaks.tweaks.miscellaneoustweaks.CarpetBlockPlacingProtocol +import nl.kallestruik.dtweaks.tweaks.miscellaneoustweaks.FakePlayers +import nl.kallestruik.dtweaks.tweaks.miscellaneoustweaks.SpaceTimePockets +import nl.kallestruik.dtweaks.tweaks.mobtweaks.NoCreeperGrief +import nl.kallestruik.dtweaks.tweaks.mobtweaks.NoDoorBreaking +import nl.kallestruik.dtweaks.tweaks.mobtweaks.NoEndermanGrief +import nl.kallestruik.dtweaks.tweaks.mobtweaks.VillagerInfo +import org.bukkit.plugin.java.JavaPlugin +import java.io.File +import java.util.* + + +class DTweaks: JavaPlugin() { + companion object { + private lateinit var random: Random + private lateinit var tweakManager: TweakManager + private lateinit var util: DUtil + private lateinit var mathHelper: MathHelper + lateinit var reflectionUtil: ReflectionUtil + private lateinit var trampleManager: TrampleManager + private lateinit var commandManager: PaperCommandManager + private lateinit var fakePlayerManager: FakePlayerManager + private lateinit var pocketDimensionManager: PocketDimensionManager + } + + override fun onEnable() { + /* + * Initialize stuff + */ + random = Random() + tweakManager = TweakManager(this) + util = DUtil(random) + mathHelper = MathHelper() + reflectionUtil = ReflectionUtil() + trampleManager = TrampleManager() + commandManager = PaperCommandManager(this) + fakePlayerManager = FakePlayerManager() + pocketDimensionManager = PocketDimensionManager(mathHelper) + + // Enable brigadier support on ACF + commandManager.enableUnstableAPI("brigadier") + + registerCommandConditions() + + /* + * Commands + */ + commandManager.registerCommand(CommandMobcaps(reflectionUtil)) + commandManager.registerCommand(CommandPlayer(fakePlayerManager, tweakManager)) + commandManager.registerCommand(CommandPocketdim(pocketDimensionManager)) + commandManager.registerCommand(CommandToggletrample(trampleManager)) + + /* + * Load data from disk + */ + tweakManager.loadFromFile(File(dataFolder, Const.TWEAK_STATE_FILE_NAME)) + trampleManager.loadFromFile(File(dataFolder, Const.TRAMPLE_ENABLED_FILE_NAME)) + pocketDimensionManager.loadData() + + /* + * Crafting tweaks + */ + tweakManager.registerTweak(AlternativeDispenserRecipe(this)) + tweakManager.registerTweak(CraftableDragonsBreath(this)) + tweakManager.registerTweak(CraftableNametag(this)) + tweakManager.registerTweak(CraftableSaddle(this)) + tweakManager.registerTweak(CraftableShulkerShell(this)) + tweakManager.registerTweak(CraftableSponge(this)) + tweakManager.registerTweak(IceDecompression(this)) + tweakManager.registerTweak(LogsToChests(this)) + tweakManager.registerTweak(WoolToString(this)) + + /* + * Crop Tweaks + */ + tweakManager.registerTweak(HoeHarvestArea(this)) + tweakManager.registerTweak(LilypadBonemealing(this, util)) + tweakManager.registerTweak(MobsCantTrampleCrops(this)) + tweakManager.registerTweak(PlayersCantTrampleCrops(this, trampleManager)) + tweakManager.registerTweak(SeedDropPlanting(this)) + tweakManager.registerTweak(SugarcaneBonemealing(this)) + + /* + * Dispenser Tweaks + */ + tweakManager.registerTweak(DispensersCanPlantSaplings(this)) + + /* + * Miscellaneous Tweaks + */ + tweakManager.registerTweak(ArmorStandArmorSwapping(this)) + tweakManager.registerTweak(CarpetBlockPlacingProtocol(this, mathHelper)) + tweakManager.registerTweak(FakePlayers(fakePlayerManager)) + tweakManager.registerTweak(SpaceTimePockets(this, pocketDimensionManager)) + + /* + * Mob Tweaks + */ + tweakManager.registerTweak(NoCreeperGrief(this)) + tweakManager.registerTweak(NoDoorBreaking(this)) + tweakManager.registerTweak(NoEndermanGrief(this)) + tweakManager.registerTweak(VillagerInfo(this)) + } + + override fun onDisable() { + /* + * Save data to disk + */ + tweakManager.saveToFile(File(dataFolder, Const.TWEAK_STATE_FILE_NAME)) + trampleManager.saveToFile(File(dataFolder, Const.TRAMPLE_ENABLED_FILE_NAME)) + pocketDimensionManager.saveData() + } + + private fun registerCommandConditions() { + commandManager.commandConditions.addCondition("tweakEnabled") { context -> + if (!tweakManager.isTweakEnabled(context.getConfigValue("tweak", ""))) + throw ConditionFailedException("The tweak ${context.getConfigValue("tweak", "")} is not enabled!") + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/commands/CommandMobcaps.kt b/src/main/kotlin/nl/kallestruik/dtweaks/commands/CommandMobcaps.kt new file mode 100644 index 0000000..8d7aee5 --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/commands/CommandMobcaps.kt @@ -0,0 +1,75 @@ +package nl.kallestruik.dtweaks.commands + +import co.aikar.commands.BaseCommand +import co.aikar.commands.annotation.* +import co.aikar.commands.annotation.Optional +import com.mojang.datafixers.util.Pair +import it.unimi.dsi.fastutil.objects.Object2IntMap +import net.kyori.adventure.text.Component +import net.kyori.adventure.text.format.TextColor +import net.minecraft.server.v1_16_R3.ChunkMapDistance +import net.minecraft.server.v1_16_R3.EnumCreatureType +import nl.kallestruik.dlib.ReflectionUtil +import org.bukkit.Bukkit +import org.bukkit.command.CommandSender +import org.bukkit.craftbukkit.v1_16_R3.CraftWorld +import org.bukkit.entity.Player +import java.util.* + +@CommandAlias("mobcaps") +class CommandMobcaps( + private val reflectionUtil: ReflectionUtil +): BaseCommand() { + + @Default + @Subcommand("view") + @CommandCompletion("@worlds") + fun onMobcaps(sender: CommandSender, @Optional worldName: String?) { + var craftWorld: CraftWorld? = null + + if (sender is Player) + craftWorld = (sender.world as CraftWorld) + else if (worldName == null) + sender.sendMessage(Component.text("ERROR: You need to be a player or supply a world name!").color(TextColor.color(0x660000))) + var name = worldName + if (worldName == null) { + name = craftWorld?.name + } else { + val bukkitWorld = Bukkit.getWorld(worldName) + if (bukkitWorld == null) + name = craftWorld?.name + else + craftWorld = (bukkitWorld as CraftWorld) + } + + val world = craftWorld?.handle ?: return + + val chunkMapDistance = ( + reflectionUtil.getValueFromField(world.getChunkProvider(), "chunkMapDistance") + ?: return + ) as ChunkMapDistance + + val chunks = chunkMapDistance.b() + + // world.getChunkManager().getSpawnInfo(); + val spawnInfo = world.getChunkProvider()?.k() ?: return + + val mobs: Object2IntMap = spawnInfo.b() + val mobcaps = EnumMap>( + EnumCreatureType::class.java + ) + for (category in EnumCreatureType.values()) { + if (category == EnumCreatureType.MISC) continue + val cur: Int = mobs[category]?: 0 + val max = chunks * category.c() / (17 * 17) + mobcaps[category] = Pair(cur, max) + } + + sender.sendMessage("=============Mobcaps [${name}]=============") + for ((key, value) in mobcaps) { + val cur = value.first + val max = value.second + sender.sendMessage("${key.getName()}: ${cur}/${max}") + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/commands/CommandPlayer.kt b/src/main/kotlin/nl/kallestruik/dtweaks/commands/CommandPlayer.kt new file mode 100644 index 0000000..54f8deb --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/commands/CommandPlayer.kt @@ -0,0 +1,28 @@ +package nl.kallestruik.dtweaks.commands + +import co.aikar.commands.BaseCommand +import co.aikar.commands.annotation.* +import nl.kallestruik.dtweaks.managers.FakePlayerManager +import nl.kallestruik.dtweaks.managers.TweakManager +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player + +@CommandAlias("player") +@Conditions("tweakEnabled:tweak=FakePlayers") +class CommandPlayer( + private val fakePlayerManager: FakePlayerManager, + private val tweakManager: TweakManager +): BaseCommand() { + + @Subcommand("spawn") + @CommandCompletion("@players") + fun onSpawn(sender: Player, @Single playerName: String) { + fakePlayerManager.spawnFakePlayer(sender.location, playerName) + } + + @Subcommand("kill") + @CommandCompletion("@players") + fun onKill(sender: Player, @Single playerName: String) { + fakePlayerManager.killFakePlayer(playerName) + } +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/commands/CommandPocketdim.kt b/src/main/kotlin/nl/kallestruik/dtweaks/commands/CommandPocketdim.kt new file mode 100644 index 0000000..f7bd7d4 --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/commands/CommandPocketdim.kt @@ -0,0 +1,25 @@ +package nl.kallestruik.dtweaks.commands + +import co.aikar.commands.BaseCommand +import co.aikar.commands.annotation.CommandAlias +import co.aikar.commands.annotation.Conditions +import co.aikar.commands.annotation.Subcommand +import nl.kallestruik.dtweaks.managers.PocketDimensionManager +import org.bukkit.entity.Player + +@CommandAlias("pocketdim") +@Conditions("tweakEnabled:tweak=SpaceTimePockets") +class CommandPocketdim( + private val pocketDimensionManager: PocketDimensionManager +): BaseCommand() { + + @Subcommand("create") + fun onCreate(sender: Player) { + pocketDimensionManager.createPocketForPlayer(sender) + } + + @Subcommand("tp") + fun onTp(sender: Player) { + pocketDimensionManager.teleportPlayerIntoPocket(sender, sender.uniqueId) + } +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/commands/CommandToggletrample.kt b/src/main/kotlin/nl/kallestruik/dtweaks/commands/CommandToggletrample.kt new file mode 100644 index 0000000..ee3b364 --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/commands/CommandToggletrample.kt @@ -0,0 +1,26 @@ +package nl.kallestruik.dtweaks.commands + +import co.aikar.commands.BaseCommand +import co.aikar.commands.annotation.CommandAlias +import co.aikar.commands.annotation.Conditions +import co.aikar.commands.annotation.Default +import nl.kallestruik.dtweaks.managers.TrampleManager +import org.bukkit.ChatColor +import org.bukkit.entity.Player + +@CommandAlias("toggletrample") +@Conditions("tweakEnabled:tweak=PlayersCantTrampleCrops") +class CommandToggletrample( + private val trampleManager: TrampleManager +): BaseCommand() { + + @Default + fun onToggle(sender: Player) { + if (trampleManager.trampleEnabled.remove(sender.uniqueId)) { + sender.sendMessage(ChatColor.translateAlternateColorCodes('&', "&2Crop Trampling: &4&lDisabled")) + } else { + trampleManager.trampleEnabled.add(sender.uniqueId) + sender.sendMessage(ChatColor.translateAlternateColorCodes('&', "&2Crop Trampling: &2&lEnabled")) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/fakeplayer/FakeConnection.kt b/src/main/kotlin/nl/kallestruik/dtweaks/fakeplayer/FakeConnection.kt new file mode 100644 index 0000000..85bf30c --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/fakeplayer/FakeConnection.kt @@ -0,0 +1,30 @@ +package nl.kallestruik.dtweaks.fakeplayer + +import io.netty.util.concurrent.Future +import io.netty.util.concurrent.GenericFutureListener +import net.minecraft.server.v1_16_R3.EnumProtocolDirection +import net.minecraft.server.v1_16_R3.NetworkManager +import net.minecraft.server.v1_16_R3.Packet + +class FakeConnection( + enumProtocolDirection: EnumProtocolDirection +): NetworkManager(enumProtocolDirection) { + var open = true + + // isOpen() + override fun isConnected(): Boolean { + return open + } + + // hasChannel() + override fun i(): Boolean { + return false + } + + override fun sendPacket(packet: Packet<*>?, genericfuturelistener: GenericFutureListener?>?) {} + + // disableAutoRead() + override fun stopReading() {} + + override fun handleDisconnection() {} +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/fakeplayer/FakePlayer.kt b/src/main/kotlin/nl/kallestruik/dtweaks/fakeplayer/FakePlayer.kt new file mode 100644 index 0000000..e508dce --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/fakeplayer/FakePlayer.kt @@ -0,0 +1,168 @@ +package nl.kallestruik.dtweaks.fakeplayer + +import com.mojang.authlib.GameProfile +import net.minecraft.server.v1_16_R3.* +import nl.kallestruik.dtweaks.DTweaks +import org.bukkit.Location +import org.bukkit.craftbukkit.v1_16_R3.CraftWorld + +class FakePlayer( + minecraftServer: MinecraftServer?, + worldServer: WorldServer?, + gameProfile: GameProfile?, + playerInteractManager: PlayerInteractManager? +): EntityPlayer(minecraftServer, worldServer, gameProfile, playerInteractManager) { + val locale = "en_US" + private var hasStartingPos = false + private var startingX = 0.0 + private var startingY = 0.0 + private var startingZ = 0.0 + private var startingYaw = 0f + private var startingPitch = 0f + + private constructor( + server: MinecraftServer, + worldIn: WorldServer, + profile: GameProfile?, + interactionManagerIn: PlayerInteractManager, + x: Double, + y: Double, + z: Double, + yaw: Float, + pitch: Float + ) : this(server, worldIn, profile, interactionManagerIn) { + this.hasStartingPos = true + this.startingX = x + this.startingY = y + this.startingZ = z + this.startingYaw = yaw + this.startingPitch = pitch + applyStartingPosition() + } + + companion object { + fun atLocation(location: Location, name: String?): FakePlayer? { + // Split the location into its parts for use later. + val x = location.x + val y = location.y + val z = location.z + val yaw = location.yaw.toDouble() + val pitch = location.pitch.toDouble() + try { + // Get the minecraft server instance. + val minecraftServer = MinecraftServer.getServer() + // Create an empty variable for the world server. + val worldServer = (location.world as CraftWorld).handle + // Get the game profile from the cache (or retrieve it if it is not cached) + var gameProfile = minecraftServer.userCache.getProfile(name) ?: return null + if (gameProfile.properties.containsKey("textures")) { + gameProfile = TileEntitySkull.b(gameProfile, null, true).get() + } + + // Create a player interact manager using the world server. + val playerInteractManager = PlayerInteractManager(worldServer) + + // Create an instance of our fake player. + val instance = FakePlayer( + minecraftServer, worldServer, gameProfile, playerInteractManager, x, y, z, + yaw.toFloat(), + pitch.toFloat() + ) + // Create a fake connection for our fake player. + val connection = FakeConnection(EnumProtocolDirection.SERVERBOUND) + + // Set access to connected channels so we can add our own connection. + val serverConnection = minecraftServer.serverConnection + + (DTweaks.reflectionUtil.getValueFromField( + serverConnection!!, + "connectedChannels" + ) as MutableList).add(connection) + + // Connect the fake player to the server using the fake connection. + FakePlayerList.a(minecraftServer.playerList, connection, instance) + + // Check if the fake player is not yet in the correct world. + if (instance.world.dimensionKey != worldServer.dimensionKey) { + // Store the old world of the fake player. + val old_world = instance.world as WorldServer + // Remove the fake player from the old world. + old_world.removePlayer(instance) + // Make the fake player not be dead. + instance.dead = false + // Create the fake player in the new world. + worldServer.addEntity(instance) + // Spawn the fake player in the new world. + instance.spawnIn(worldServer) + // Move the fake player to the new world for the server. + minecraftServer.playerList.moveToWorld(instance, old_world, true, null, false) + // requestTeleport(x, y, z, yaw, pitch) + // Request the teleport from the fake player. + instance.playerConnection.a(x, y, z, yaw.toFloat(), pitch.toFloat()) + // Set the fake player's world to the new world. + instance.playerInteractManager.world = worldServer + instance.teleportTo(worldServer, BlockPosition(x, y, z)) + } + + /// Set the fake players health to max. + instance.health = 20.0f + // Make the fake player not be dead. + instance.dead = false + // a == requestTeleport + // Request the teleport from the fake player. + instance.playerConnection.a(x, y, z, yaw.toFloat(), pitch.toFloat()) + // G == stepHeight + // Set the fake players step height to 0.6 (The normal value). + instance.G = 0.6f + // Set the fake players gamemode to survival. + playerInteractManager.gameMode = EnumGamemode.SURVIVAL + // Tell everyone in the world about the fake player and where he is. + minecraftServer.playerList.a( + PacketPlayOutEntityHeadRotation(instance, (instance.yaw * 256 / 360).toByte()), + instance.world.dimensionKey + ) + minecraftServer.playerList.a(PacketPlayOutEntityTeleport(instance), instance.world.dimensionKey) + // Move the fake player for the worlds chunk provider. + instance.worldServer.getChunkProvider().movePlayer(instance) + // bp == PLAYER_MODEL_PARTS + instance.datawatcher.set(bj, 0x7f.toByte()) // show all model layers (incl. capes) + return instance + } catch (e: Exception) { + e.printStackTrace() + } + return null + } + } + + + fun applyStartingPosition() { + if (hasStartingPos) { + this.setPositionRotation(startingX, startingY, startingZ, startingYaw, startingPitch) + mot = Vec3D(0.0, 0.0, 0.0) + } + } + + + override fun killEntity() { + server.a(TickTask(server.ai()) { playerConnection.a(ChatComponentText("Killed")) }) + } + + override fun tick() { + super.tick() + if (H()) { + I() + } + movementTick() + if (server.ai() % 10 == 0) { + playerConnection.syncPosition() + this.worldServer.getChunkProvider().movePlayer(this) + } + } + + override fun die(cause: DamageSource?) { + super.die(cause) + health = 20f + foodData = FoodMetaData(this) + killEntity() + } +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/fakeplayer/FakePlayerConnection.kt b/src/main/kotlin/nl/kallestruik/dtweaks/fakeplayer/FakePlayerConnection.kt new file mode 100644 index 0000000..f8be696 --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/fakeplayer/FakePlayerConnection.kt @@ -0,0 +1,36 @@ +package nl.kallestruik.dtweaks.fakeplayer + +import net.minecraft.server.v1_16_R3.* +import nl.kallestruik.dtweaks.DTweaks +import java.lang.reflect.Field + +class FakePlayerConnection( + minecraftServer: MinecraftServer, + networkManager: NetworkManager, + entityPlayer: EntityPlayer +): PlayerConnection(minecraftServer, networkManager, entityPlayer) { + override fun sendPacket(packet: Packet<*>?) { + if (packet is PacketPlayOutKeepAlive) { + val pong = PacketPlayInKeepAlive() + try { + val pingId: Field = DTweaks.reflectionUtil.getField(PacketPlayOutKeepAlive::class.java, "a") + val pongId: Field = DTweaks.reflectionUtil.getField(PacketPlayInKeepAlive::class.java, "a") + pingId.isAccessible = true + pongId.isAccessible = true + pongId[pong] = pingId[packet] + } catch (e: Exception) { + e.printStackTrace() + } + this.a(pong) + } + } + + override fun disconnect(message: String?) { + player.killEntity() + } + + override fun a(disconnectReason: IChatBaseComponent?) { + super.a(disconnectReason) + (this.a() as FakeConnection).open = false + } +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/fakeplayer/FakePlayerList.kt b/src/main/kotlin/nl/kallestruik/dtweaks/fakeplayer/FakePlayerList.kt new file mode 100644 index 0000000..afd01bd --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/fakeplayer/FakePlayerList.kt @@ -0,0 +1,258 @@ +package nl.kallestruik.dtweaks.fakeplayer + +import com.mojang.serialization.DataResult +import com.mojang.serialization.Dynamic +import io.netty.buffer.Unpooled +import net.minecraft.server.v1_16_R3.* +import nl.kallestruik.dtweaks.DTweaks +import org.apache.logging.log4j.Logger +import org.bukkit.Bukkit +import org.bukkit.craftbukkit.v1_16_R3.CraftWorld +import org.bukkit.craftbukkit.v1_16_R3.util.CraftChatMessage +import org.bukkit.entity.Player +import org.spigotmc.event.player.PlayerSpawnLocationEvent +import java.util.* + +object FakePlayerList { + fun a(playerList: PlayerList, networkManager: NetworkManager, entityPlayer: EntityPlayer) { + // Setup what are normally class level variables + val server = DTweaks.reflectionUtil.getValueFromField(playerList, "server") as MinecraftServer + val LOGGER = DTweaks.reflectionUtil.getValueFromField(playerList, "LOGGER") as Logger + + val gameProfile = entityPlayer.profile + val userCache = server.userCache + val oldGameProfile = userCache.getProfile(gameProfile.id) + var oldName = if (oldGameProfile == null) gameProfile.name else oldGameProfile.name + + userCache.a(gameProfile) + val playerData = playerList.a(entityPlayer) + if (playerData != null && playerData.hasKey("bukkit")) { + val bukkit = playerData.getCompound("bukkit") + oldName = if (bukkit.hasKeyOfType("lastKnownName", 8)) bukkit.getString("lastKnownName") else oldName + } + val worldKey: ResourceKey = if (playerData != null) { + val dataResult: DataResult> = DimensionManager.a( + Dynamic( + DynamicOpsNBT.a, + playerData["Dimension"] + ) + ) + dataResult.resultOrPartial { o -> LOGGER.error(o) } + .orElse(World.OVERWORLD) as ResourceKey + } else { + World.OVERWORLD + } + var worldServer = server.getWorldServer(worldKey) ?: server.E() + + entityPlayer.spawnIn(worldServer) + entityPlayer.playerInteractManager.a(entityPlayer.world as WorldServer) + var s1 = "local" + if (networkManager.getSocketAddress() != null) { + s1 = networkManager.getSocketAddress().toString() + } + val bukkitPlayer: Player = entityPlayer.bukkitEntity + val ev = PlayerSpawnLocationEvent(bukkitPlayer, bukkitPlayer.location) + Bukkit.getPluginManager().callEvent(ev) + val loc = ev.spawnLocation + worldServer = (loc.world as CraftWorld).handle + entityPlayer.spawnIn(worldServer) + entityPlayer.playerInteractManager.a(entityPlayer.world as WorldServer) + entityPlayer.setPosition(loc.x, loc.y, loc.z) + + //entityplayer.setYawPitch(loc.getYaw(), loc.getPitch()); + try { + val setYawPitch = Entity::class.java.getDeclaredMethod( + "setYawPitch", + Float::class.javaPrimitiveType, + Float::class.javaPrimitiveType + ) + setYawPitch.isAccessible = true + setYawPitch.invoke(entityPlayer, loc.yaw, loc.pitch) + } catch (e: Exception) { + e.printStackTrace() + } + val worldData = worldServer.getWorldData() + + //playerList.a(entityplayer, (EntityPlayer)null, worldserver1); + try { + val a = PlayerList::class.java.getDeclaredMethod( + "a", + EntityPlayer::class.java, + EntityPlayer::class.java, + WorldServer::class.java + ) + a.isAccessible = true + a.invoke(playerList, entityPlayer, null, worldServer) + } catch (e: Exception) { + e.printStackTrace() + } + + //PlayerConnection playerconnection = new PlayerConnection(server, networkmanager, entityplayer); + val playerConnection: PlayerConnection = + (entityPlayer as? FakePlayer)?.let { FakePlayerConnection(server, networkManager, it) } + ?: PlayerConnection(server, networkManager, entityPlayer) + val gameRules = worldServer.gameRules + val doImmediateRespawn = gameRules.getBoolean(GameRules.DO_IMMEDIATE_RESPAWN) + val reducedDebugInfo = gameRules.getBoolean(GameRules.REDUCED_DEBUG_INFO) + try { + playerConnection.sendPacket( + PacketPlayOutLogin( + entityPlayer.id, + entityPlayer.playerInteractManager.gameMode, + entityPlayer.playerInteractManager.c(), + BiomeManager.a( + worldServer.seed + ), + worldData.isHardcore, + server.F(), + DTweaks.reflectionUtil.getValueFromField(playerList, "s") as IRegistryCustom.Dimension, + worldServer.dimensionManager, + worldServer.dimensionKey, + playerList.maxPlayers, + worldServer.spigotConfig.viewDistance, + reducedDebugInfo, + !doImmediateRespawn, + worldServer.isDebugWorld, + worldServer.isFlatWorld + ) + ) + } catch (e: Exception) { + e.printStackTrace() + } + entityPlayer.bukkitEntity.sendSupportedChannels() + playerConnection.sendPacket( + PacketPlayOutCustomPayload( + PacketPlayOutCustomPayload.a, + PacketDataSerializer(Unpooled.buffer()).a(playerList.server.serverModName) + ) + ) + playerConnection.sendPacket(PacketPlayOutServerDifficulty(worldData.difficulty, worldData.isDifficultyLocked)) + playerConnection.sendPacket(PacketPlayOutAbilities(entityPlayer.abilities)) + playerConnection.sendPacket(PacketPlayOutHeldItemSlot(entityPlayer.inventory.itemInHandIndex)) + playerConnection.sendPacket(PacketPlayOutRecipeUpdate(server.craftingManager.b())) + playerConnection.sendPacket(PacketPlayOutTags(server.tagRegistry)) + playerList.d(entityPlayer) + entityPlayer.statisticManager.c() + entityPlayer.recipeBook.a(entityPlayer) + playerList.sendScoreboard(worldServer.scoreboard, entityPlayer) + server.invalidatePingSample() + val chatMessage = if (entityPlayer.profile.name.equals(oldName, ignoreCase = true)) { + ChatMessage("multiplayer.player.joined", entityPlayer.scoreboardDisplayName) + } else { + ChatMessage("multiplayer.player.joined.renamed", entityPlayer.scoreboardDisplayName, oldName) + } + chatMessage.a(EnumChatFormat.YELLOW) + var joinMessage = CraftChatMessage.fromComponent(chatMessage) + playerConnection.a( + entityPlayer.locX(), + entityPlayer.locY(), + entityPlayer.locZ(), + entityPlayer.yaw, + entityPlayer.pitch + ) + playerList.players.add(entityPlayer) + + //playerList.playersByName.put(entityplayer.getName().toLowerCase(Locale.ROOT), entityplayer); + val playersByName = DTweaks.reflectionUtil.getValueFromField(playerList, "playersByName") as HashMap + playersByName[entityPlayer.name.toLowerCase(Locale.ROOT)] = entityPlayer + + //playerList.j.put(entityplayer.getUniqueID(), entityplayer); + val playersByUUID = DTweaks.reflectionUtil.getValueFromField(playerList, "j") as HashMap + playersByUUID[entityPlayer.uniqueID] = entityPlayer + + if (entityPlayer.playerConnection.networkManager.isConnected) { + var i: Int + if (joinMessage != null && joinMessage.isNotEmpty()) { + var var27: Array + val var26 = CraftChatMessage.fromString(joinMessage).also { var27 = it }.size + i = 0 + while (i < var26) { + val line = var27[i] + server.playerList.sendAll(PacketPlayOutChat(line, ChatMessageType.SYSTEM, SystemUtils.b)) + ++i + } + } + val packet = PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, entityPlayer) + for (player in playerList.players) { + if (player.bukkitEntity.canSee(entityPlayer.bukkitEntity)) { + player.playerConnection.sendPacket(packet) + } + if (entityPlayer.bukkitEntity.canSee(player.bukkitEntity)) { + entityPlayer.playerConnection.sendPacket( + PacketPlayOutPlayerInfo( + PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, + player + ) + ) + } + } + entityPlayer.sentListPacket = true + entityPlayer.playerConnection.sendPacket( + PacketPlayOutEntityMetadata( + entityPlayer.id, + DTweaks.reflectionUtil.getValueFromField(entityPlayer, "datawatcher") as DataWatcher, true + ) + ) + + if (entityPlayer.world === worldServer && !worldServer.getPlayers().contains(entityPlayer)) { + worldServer.addPlayerJoin(entityPlayer) + server.bossBattleCustomData.a(entityPlayer) + } + + worldServer = entityPlayer.worldServer + playerList.a(entityPlayer, worldServer) + if (server.resourcePack.isNotEmpty()) { + entityPlayer.setResourcePack(server.resourcePack, server.resourcePackHash) + } + + for (mobEffect in entityPlayer.getEffects()) { + playerConnection.sendPacket(PacketPlayOutEntityEffect(entityPlayer.id, mobEffect)) + } + + if (playerData != null && playerData.hasKeyOfType("RootVehicle", 10)) { + val rootVehicleData = playerData.getCompound("RootVehicle") + val entity = EntityTypes.a( + rootVehicleData.getCompound("Entity"), worldServer + ) { entity1x: Entity? -> + if (!worldServer.addEntitySerialized(entity1x) + ) null else entity1x + } + if (entity != null) { + val uuid: UUID? = if (rootVehicleData.b("Attach")) { + rootVehicleData.a("Attach") + } else { + null + } + if (entity.uniqueID == uuid) { + entityPlayer.a(entity, true) + } else { + for (passenger in entity.allPassengers) { + if (passenger.uniqueID == uuid) { + entityPlayer.a(passenger, true) + break + } + } + } + if (!entityPlayer.isPassenger) { + LOGGER.warn("Couldn't reattach entity to player") + worldServer.removeEntity(entity) + for (passenger in entity.allPassengers) { + worldServer.removeEntity(passenger) + } + } + } + } + entityPlayer.syncInventory() + LOGGER.info( + "{}[{}] logged in with entity id {} at ([{}]{}, {}, {})", + entityPlayer.getDisplayName().string, + s1, + entityPlayer.id, + worldServer?.worldDataServer?.name, + entityPlayer.locX(), + entityPlayer.locY(), + entityPlayer.locZ() + ) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/managers/FakePlayerManager.kt b/src/main/kotlin/nl/kallestruik/dtweaks/managers/FakePlayerManager.kt new file mode 100644 index 0000000..e613918 --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/managers/FakePlayerManager.kt @@ -0,0 +1,30 @@ +package nl.kallestruik.dtweaks.managers + +import nl.kallestruik.dtweaks.fakeplayer.FakePlayer +import org.bukkit.Bukkit +import org.bukkit.Location +import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer + +class FakePlayerManager { + + fun spawnFakePlayer(loc: Location, name: String) { + val player = Bukkit.getPlayer(name) + if (player != null && player.isOnline) return + FakePlayer.atLocation(loc, name) + } + + fun killFakePlayer(name: String) { + val player = Bukkit.getPlayer(name) + if (player == null || !player.isOnline) return + val entityPlayer = (player as CraftPlayer).handle as? FakePlayer ?: return + entityPlayer.killEntity() + } + + fun killAllFakePlayers() { + for (player in Bukkit.getOnlinePlayers()) { + if (!player.isOnline) continue + val entityPlayer = (player as CraftPlayer).handle as? FakePlayer ?: continue + entityPlayer.killEntity() + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/managers/PocketDimensionManager.kt b/src/main/kotlin/nl/kallestruik/dtweaks/managers/PocketDimensionManager.kt new file mode 100644 index 0000000..45138d8 --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/managers/PocketDimensionManager.kt @@ -0,0 +1,110 @@ +package nl.kallestruik.dtweaks.managers + +import net.minecraft.server.v1_16_R3.MathHelper +import nl.kallestruik.dlib.MathHelper +import nl.kallestruik.dtweaks.spacetimepockets.SpaceTimePocketChunkGenerator +import org.bukkit.* +import org.bukkit.entity.Player +import java.util.* + +class PocketDimensionManager( + private val mathHelper: MathHelper +) { + val pocketDimension: World = WorldCreator.name("space-time-pockets").generator(SpaceTimePocketChunkGenerator()).createWorld()!! + private val data = HashMap() + private var lastCreatedPocket = -1 + + fun loadData() { + //TODO: Load data from data file on disk. + } + + fun saveData() { + //TODO: Save data to file on disk. + } + + fun teleportPlayerIntoPocket(player: Player, pocketOwner: UUID) { + var pocketData = data[pocketOwner] + if (pocketData == null) pocketData = if (player.uniqueId == pocketOwner) { + createPocketForPlayer(player) + } else return + player.teleport( + Location( + pocketDimension, (pocketData.startX + 9).toDouble(), 128.0, + (pocketData.startZ + 9).toDouble() + ) + ) + Bukkit.unloadWorld() + } + + fun createPocketForPlayer(player: Player): PocketData { + // Find a spot + val point = findNextFree() + + // Load the chunks + for (x in 0..2) { + for (y in 0..2) { + pocketDimension.loadChunk(point.x / 16 + x, point.z / 16 + y) + } + } + + // Build the box + for (x in 0..17) { + for (z in 0..17) { + for (y in 0..255) { + if (x == 0 || y == 0 || z == 0 || x == 17 || y == 255 || z == 17) { + pocketDimension.getBlockAt(point.x + x, y, point.z + z).type = Material.BLACK_CONCRETE + } + } + } + } + + // Create the spawning platform + pocketDimension.getBlockAt(point.x + 8, 127, point.z + 8).type = Material.STONE + pocketDimension.getBlockAt(point.x + 8, 127, point.z + 9).type = Material.STONE + pocketDimension.getBlockAt(point.x + 9, 127, point.z + 8).type = Material.STONE + pocketDimension.getBlockAt(point.x + 9, 127, point.z + 9).type = Material.STONE + + // Unload the chunks + for (x in 0..2) { + for (y in 0..2) { + pocketDimension.unloadChunk(point.x / 16 + x, point.z / 16 + y) + } + } + val pocketData = PocketData(player.uniqueId, point.x, point.z, ArrayList()) + data[player.uniqueId] = pocketData + return pocketData + } + + private fun findNextFree(): Point { + while (true) { + lastCreatedPocket++ + val toCheck = getXYForIndex(lastCreatedPocket) + val chunk = pocketDimension.getChunkAt( + Location( + pocketDimension, + toCheck.x.toDouble(), 0.0, toCheck.z.toDouble() + ) + ) + val corner = chunk.getBlock(mathHelper.chunkAbs(toCheck.x % 16), 0, mathHelper.chunkAbs(toCheck.z % 16)) + if (corner.type == Material.AIR) { + return toCheck + } + } + } + + private fun getXYForIndex(index: Int): Point { + // Space the pockets 256 blocks (16 chunks) from each other with each pocket being 16 blocks (and two for walls) + // Also subtract 1 from each point so the interior is chunk aligned. + val x = (256 + 16) * (index % 1000) - 1 + val z = (256 + 16) * (index / 1000) - 1 + return Point(x, z) + } + + class PocketData(var owner: UUID, var startX: Int, var startZ: Int, var queuedGateways: ArrayList) + + class GatewayData + + class Point(var x: Int, var y: Int, var z: Int) { + constructor(x: Int, z: Int) : this(x, 0, z) {} + } +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/managers/TrampleManager.kt b/src/main/kotlin/nl/kallestruik/dtweaks/managers/TrampleManager.kt new file mode 100644 index 0000000..f0e7786 --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/managers/TrampleManager.kt @@ -0,0 +1,36 @@ +package nl.kallestruik.dtweaks.managers + +import org.bukkit.configuration.InvalidConfigurationException +import org.bukkit.configuration.file.YamlConfiguration +import java.io.File +import java.io.IOException +import java.util.* + +class TrampleManager { + var trampleEnabled = HashSet() + + @Throws(IOException::class, InvalidConfigurationException::class) + fun loadFromFile(file: File) { + file.parentFile.mkdirs() + file.createNewFile() + val config = YamlConfiguration() + config.load(file) + for (item in config.getStringList("enabled")) + trampleEnabled.add(UUID.fromString(item)) + } + + @Throws(IOException::class, InvalidConfigurationException::class) + fun saveToFile(file: File) { + file.parentFile.mkdirs() + file.createNewFile() + val config = YamlConfiguration() + config.load(file) + val list: MutableList = ArrayList() + for (entry in trampleEnabled) + list.add(entry.toString()) + + config["enabled"] = list + config.save(file) + } + +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/managers/TweakManager.kt b/src/main/kotlin/nl/kallestruik/dtweaks/managers/TweakManager.kt new file mode 100644 index 0000000..e423138 --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/managers/TweakManager.kt @@ -0,0 +1,116 @@ +package nl.kallestruik.dtweaks.managers + +import nl.kallestruik.dtweaks.Const +import nl.kallestruik.dtweaks.tweaks.ITweak +import org.bukkit.configuration.InvalidConfigurationException +import org.bukkit.configuration.file.YamlConfiguration +import org.bukkit.plugin.java.JavaPlugin +import java.io.File +import java.io.IOException +import java.util.concurrent.ConcurrentHashMap + +class TweakManager( + private val plugin: JavaPlugin +) { + + // A HashMap containing the tweak identifier mapped to the instance of the tweak. + private val tweaks: ConcurrentHashMap = ConcurrentHashMap() + + // A HashMap containing categories and the tweak identifiers that belong to them. + private val tweakCategories = HashMap>() + + // A HashMap containing the sate of all the known tweaks. + private val tweakStates = HashMap() + + + /** + * Register a new tweak. + * + * @param tweak An instance of the tweak. + * @return True if the tweak did not yet exist false otherwise. + */ + fun registerTweak(tweak: ITweak): Boolean { + if (tweaks.containsKey(tweak.getIdentifier())) return false + tweaks[tweak.getIdentifier()] = tweak + tweakCategories[tweak.getIdentifier()] = tweak.getCategories() + tweak.onRegister() + if (tweakStates.getOrDefault(tweak.getIdentifier(), false)) tweak.onEnable() + return true + } + + /** + * Unregister a tweak. + * + * @param identifier The identifier of the tweak to unregister. + * @return True if the tweak was successfully unregistered false otherwise. + */ + fun unRegisterTweak(identifier: String): Boolean { + if (!tweaks.containsKey(identifier)) return false + val tweak: ITweak? = tweaks[identifier] + tweak?.onDisable() + tweak?.onUnRegister() + tweaks.remove(identifier) + return true + } + + /** + * Unregister every tweak that is currently registered. + */ + fun unRegisterAllTweaks() { + for (identifier in tweaks.keys) { + unRegisterTweak(identifier) + } + } + + /** + * Enable a tweak. + * + * @param identifier The identifier of the tweak. + * @return True if the tweak exists false otherwise. + */ + fun enableTweak(identifier: String): Boolean { + if (!tweaks.containsKey(identifier)) return false + tweakStates[identifier] = true + tweaks[identifier]?.onEnable() + return true + } + + /** + * Disable a tweak. + * + * @param identifier The identifier of the tweak. + * @return True if the tweak exists false otherwise. + */ + fun disableTweak(identifier: String): Boolean { + if (!tweaks.containsKey(identifier)) return false + tweakStates[identifier] = false + tweaks[identifier]?.onDisable() + return true + } + + @Throws(IOException::class, InvalidConfigurationException::class) + fun loadFromFile(file: File) { + if (!file.parentFile.exists()) file.parentFile.mkdirs() + if (!file.exists()) + plugin.saveResource(file.name, false) + + val config = YamlConfiguration() + config.load(file) + for (key in config.getKeys(false)) { + tweakStates[key] = config.getBoolean(key!!) + } + } + + @Throws(IOException::class) + fun saveToFile(file: File) { + val config = YamlConfiguration() + for ((key, value) in tweakStates.entries) { + config[key] = value + } + config.save(file) + } + + fun isTweakEnabled(tweakId: String): Boolean { + return tweakStates[tweakId] ?: false + } +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/spacetimepockets/SpaceTimePocketChunkGenerator.kt b/src/main/kotlin/nl/kallestruik/dtweaks/spacetimepockets/SpaceTimePocketChunkGenerator.kt new file mode 100644 index 0000000..b8683ac --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/spacetimepockets/SpaceTimePocketChunkGenerator.kt @@ -0,0 +1,27 @@ +package nl.kallestruik.dtweaks.spacetimepockets + +import org.bukkit.World +import org.bukkit.block.Biome +import org.bukkit.craftbukkit.v1_16_R3.generator.CraftChunkData +import org.bukkit.generator.ChunkGenerator +import java.util.* + +class SpaceTimePocketChunkGenerator: ChunkGenerator() { + + override fun generateChunkData( + world: World, + random: Random, + x: Int, + z: Int, + biome: BiomeGrid + ): ChunkData { + for (cx in 0..15) { + for (cz in 0..15) { + for (cy in 0..264) { + biome.setBiome(cx, cy, cz, Biome.THE_VOID) + } + } + } + return CraftChunkData(world) + } +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/ITweak.kt b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/ITweak.kt new file mode 100644 index 0000000..4b5cb7b --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/ITweak.kt @@ -0,0 +1,17 @@ +package nl.kallestruik.dtweaks.tweaks + +import org.bukkit.plugin.java.JavaPlugin + +interface ITweak { + fun getIdentifier(): String + + fun getCategories(): List + + fun onRegister() {} + + fun onUnRegister() {} + + fun onEnable() {} + + fun onDisable() {} +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/craftingtweaks/AlternativeDispenserRecipe.kt b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/craftingtweaks/AlternativeDispenserRecipe.kt new file mode 100644 index 0000000..bfe324b --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/craftingtweaks/AlternativeDispenserRecipe.kt @@ -0,0 +1,32 @@ +package nl.kallestruik.dtweaks.tweaks.craftingtweaks + +import nl.kallestruik.dtweaks.tweaks.ITweak +import org.bukkit.Bukkit +import org.bukkit.Material +import org.bukkit.NamespacedKey +import org.bukkit.inventory.ItemStack +import org.bukkit.inventory.ShapedRecipe +import org.bukkit.plugin.java.JavaPlugin + +class AlternativeDispenserRecipe( + private val plugin: JavaPlugin +): ITweak { + private var recipeKey = NamespacedKey(plugin, "dispenser") + + override fun getIdentifier(): String = "AlternativeDispenserRecipe" + override fun getCategories(): List = listOf("crafting", "survival") + + override fun onEnable() { + val dispenserRecipe = ShapedRecipe(recipeKey, ItemStack(Material.DISPENSER)) + dispenserRecipe.shape(" TS", "TDS", " TS") + dispenserRecipe.setIngredient('T', Material.STICK) + dispenserRecipe.setIngredient('D', Material.DROPPER) + dispenserRecipe.setIngredient('S', Material.STRING) + + plugin.server.addRecipe(dispenserRecipe) + } + + override fun onDisable() { + plugin.server.removeRecipe(recipeKey) + } +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/craftingtweaks/CraftableDragonsBreath.kt b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/craftingtweaks/CraftableDragonsBreath.kt new file mode 100644 index 0000000..1ff1beb --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/craftingtweaks/CraftableDragonsBreath.kt @@ -0,0 +1,31 @@ +package nl.kallestruik.dtweaks.tweaks.craftingtweaks + +import nl.kallestruik.dtweaks.tweaks.ITweak +import org.bukkit.Bukkit +import org.bukkit.Material +import org.bukkit.NamespacedKey +import org.bukkit.inventory.ItemStack +import org.bukkit.inventory.ShapedRecipe +import org.bukkit.plugin.java.JavaPlugin + +class CraftableDragonsBreath( + private val plugin: JavaPlugin +): ITweak { + private var recipeKey = NamespacedKey(plugin, "dragon_breath") + + override fun getIdentifier(): String = "CraftableDragonsBreath" + override fun getCategories(): List = listOf("crafting", "survival") + + override fun onEnable() { + val dragonBreathRecipe = ShapedRecipe(recipeKey, ItemStack(Material.DRAGON_BREATH, 3)) + dragonBreathRecipe.shape("GCG", " G ") + dragonBreathRecipe.setIngredient('G', Material.GLASS) + dragonBreathRecipe.setIngredient('C', Material.POPPED_CHORUS_FRUIT) + + plugin.server.addRecipe(dragonBreathRecipe) + } + + override fun onDisable() { + plugin.server.removeRecipe(recipeKey) + } +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/craftingtweaks/CraftableNametag.kt b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/craftingtweaks/CraftableNametag.kt new file mode 100644 index 0000000..c401e81 --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/craftingtweaks/CraftableNametag.kt @@ -0,0 +1,32 @@ +package nl.kallestruik.dtweaks.tweaks.craftingtweaks + +import nl.kallestruik.dtweaks.tweaks.ITweak +import org.bukkit.Bukkit +import org.bukkit.Material +import org.bukkit.NamespacedKey +import org.bukkit.inventory.ItemStack +import org.bukkit.inventory.ShapedRecipe +import org.bukkit.plugin.java.JavaPlugin + +class CraftableNametag( + private val plugin: JavaPlugin +): ITweak { + private var recipeKey = NamespacedKey(plugin, "nametag") + + override fun getIdentifier(): String = "CraftableNametag" + override fun getCategories(): List = listOf("crafting", "survival") + + override fun onEnable() { + val nametagRecipe = ShapedRecipe(recipeKey, ItemStack(Material.NAME_TAG)) + nametagRecipe.shape(" IS", " PI", "P ") + nametagRecipe.setIngredient('I', Material.IRON_INGOT) + nametagRecipe.setIngredient('P', Material.PAPER) + nametagRecipe.setIngredient('S', Material.STRING) + + plugin.server.addRecipe(nametagRecipe) + } + + override fun onDisable() { + plugin.server.removeRecipe(recipeKey) + } +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/craftingtweaks/CraftableSaddle.kt b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/craftingtweaks/CraftableSaddle.kt new file mode 100644 index 0000000..2e02daa --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/craftingtweaks/CraftableSaddle.kt @@ -0,0 +1,32 @@ +package nl.kallestruik.dtweaks.tweaks.craftingtweaks + +import nl.kallestruik.dtweaks.tweaks.ITweak +import org.bukkit.Bukkit +import org.bukkit.Material +import org.bukkit.NamespacedKey +import org.bukkit.inventory.ItemStack +import org.bukkit.inventory.ShapedRecipe +import org.bukkit.plugin.java.JavaPlugin + +class CraftableSaddle( + private val plugin: JavaPlugin +): ITweak { + private var recipeKey = NamespacedKey(plugin, "saddle") + + override fun getIdentifier(): String = "CraftableSaddle" + override fun getCategories(): List = listOf("crafting", "survival") + + override fun onEnable() { + val saddleRecipe = ShapedRecipe(recipeKey, ItemStack(Material.SADDLE)) + saddleRecipe.shape("LLL", "S S", "I I") + saddleRecipe.setIngredient('L', Material.LEATHER) + saddleRecipe.setIngredient('S', Material.STRING) + saddleRecipe.setIngredient('I', Material.IRON_INGOT) + + plugin.server.addRecipe(saddleRecipe) + } + + override fun onDisable() { + plugin.server.removeRecipe(recipeKey) + } +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/craftingtweaks/CraftableShulkerShell.kt b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/craftingtweaks/CraftableShulkerShell.kt new file mode 100644 index 0000000..92071ea --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/craftingtweaks/CraftableShulkerShell.kt @@ -0,0 +1,31 @@ +package nl.kallestruik.dtweaks.tweaks.craftingtweaks + +import nl.kallestruik.dtweaks.tweaks.ITweak +import org.bukkit.Bukkit +import org.bukkit.Material +import org.bukkit.NamespacedKey +import org.bukkit.inventory.ItemStack +import org.bukkit.inventory.ShapedRecipe +import org.bukkit.plugin.java.JavaPlugin + +class CraftableShulkerShell( + private val plugin: JavaPlugin +): ITweak { + private var recipeKey = NamespacedKey(plugin, "shulker_shell") + + override fun getIdentifier(): String = "CraftableShulkerShell" + override fun getCategories(): List = listOf("crafting", "survival") + + override fun onEnable() { + val shulkerShellRecipe = ShapedRecipe(recipeKey, ItemStack(Material.SHULKER_SHELL)) + shulkerShellRecipe.shape("BBB", "F F") + shulkerShellRecipe.setIngredient('B', Material.PURPUR_SLAB) + shulkerShellRecipe.setIngredient('F', Material.POPPED_CHORUS_FRUIT) + + plugin.server.addRecipe(shulkerShellRecipe) + } + + override fun onDisable() { + plugin.server.removeRecipe(recipeKey) + } +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/craftingtweaks/CraftableSponge.kt b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/craftingtweaks/CraftableSponge.kt new file mode 100644 index 0000000..42cd50f --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/craftingtweaks/CraftableSponge.kt @@ -0,0 +1,31 @@ +package nl.kallestruik.dtweaks.tweaks.craftingtweaks + +import nl.kallestruik.dtweaks.tweaks.ITweak +import org.bukkit.Bukkit +import org.bukkit.Material +import org.bukkit.NamespacedKey +import org.bukkit.inventory.ItemStack +import org.bukkit.inventory.ShapedRecipe +import org.bukkit.plugin.java.JavaPlugin + +class CraftableSponge( + private val plugin: JavaPlugin +): ITweak { + private var recipeKey = NamespacedKey(plugin, "sponge") + + override fun getIdentifier(): String = "CraftableSponge" + override fun getCategories(): List = listOf("crafting", "survival") + + override fun onEnable() { + val spongeRecipe = ShapedRecipe(recipeKey, ItemStack(Material.SPONGE)) + spongeRecipe.shape("KKK", "KDK", "KKK") + spongeRecipe.setIngredient('K', Material.KELP) + spongeRecipe.setIngredient('D', Material.YELLOW_DYE) + + plugin.server.addRecipe(spongeRecipe) + } + + override fun onDisable() { + plugin.server.removeRecipe(recipeKey) + } +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/craftingtweaks/IceDecompression.kt b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/craftingtweaks/IceDecompression.kt new file mode 100644 index 0000000..82319f5 --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/craftingtweaks/IceDecompression.kt @@ -0,0 +1,36 @@ +package nl.kallestruik.dtweaks.tweaks.craftingtweaks + +import nl.kallestruik.dtweaks.tweaks.ITweak +import org.bukkit.Bukkit +import org.bukkit.Material +import org.bukkit.NamespacedKey +import org.bukkit.inventory.ItemStack +import org.bukkit.inventory.ShapelessRecipe +import org.bukkit.plugin.java.JavaPlugin + +class IceDecompression( + private val plugin: JavaPlugin +): ITweak { + private var packedIceKey = NamespacedKey(plugin, "packed_ice") + private var iceKey = NamespacedKey(plugin, "ice") + + override fun getIdentifier(): String = "IceDecompression" + override fun getCategories(): List = listOf("crafting", "survival") + + override fun onEnable() { + // Blue Ice -> Packed Ice + val packedIceRecipe = ShapelessRecipe(packedIceKey, ItemStack(Material.PACKED_ICE, 9)) + packedIceRecipe.addIngredient(Material.BLUE_ICE) + plugin.server.addRecipe(packedIceRecipe) + + // Packed Ice -> Ice + val iceRecipe = ShapelessRecipe(iceKey, ItemStack(Material.ICE, 9)) + iceRecipe.addIngredient(Material.PACKED_ICE) + plugin.server.addRecipe(iceRecipe) + } + + override fun onDisable() { + plugin.server.removeRecipe(packedIceKey) + plugin.server.removeRecipe(iceKey) + } +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/craftingtweaks/LogsToChests.kt b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/craftingtweaks/LogsToChests.kt new file mode 100644 index 0000000..141b140 --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/craftingtweaks/LogsToChests.kt @@ -0,0 +1,32 @@ +package nl.kallestruik.dtweaks.tweaks.craftingtweaks + +import nl.kallestruik.dtweaks.tweaks.ITweak +import org.bukkit.Bukkit +import org.bukkit.Material +import org.bukkit.NamespacedKey +import org.bukkit.Tag +import org.bukkit.inventory.ItemStack +import org.bukkit.inventory.RecipeChoice +import org.bukkit.inventory.ShapedRecipe +import org.bukkit.plugin.java.JavaPlugin + +class LogsToChests( + private val plugin: JavaPlugin +): ITweak { + private var recipeKey = NamespacedKey(plugin, "chest") + + override fun getIdentifier(): String = "LogsToChests" + override fun getCategories(): List = listOf("crafting", "survival") + + override fun onEnable() { + val chestRecipe = ShapedRecipe(recipeKey, ItemStack(Material.CHEST, 4)) + chestRecipe.shape("WWW", "W W", "WWW") + chestRecipe.setIngredient('W', RecipeChoice.MaterialChoice(Tag.LOGS)) + + plugin.server.addRecipe(chestRecipe) + } + + override fun onDisable() { + plugin.server.removeRecipe(recipeKey) + } +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/craftingtweaks/RedyeTerracotta.kt b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/craftingtweaks/RedyeTerracotta.kt new file mode 100644 index 0000000..d495e86 --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/craftingtweaks/RedyeTerracotta.kt @@ -0,0 +1,47 @@ +package nl.kallestruik.dtweaks.tweaks.craftingtweaks + +import nl.kallestruik.dtweaks.tweaks.ITweak +import org.bukkit.Material +import org.bukkit.NamespacedKey +import org.bukkit.inventory.ItemStack +import org.bukkit.inventory.RecipeChoice +import org.bukkit.inventory.ShapedRecipe +import org.bukkit.plugin.java.JavaPlugin + +class RedyeTerracotta( + private val plugin: JavaPlugin +): ITweak { + private val blackRecipeKey = NamespacedKey(plugin, "black_terracotta") + private val blueRecipeKey = NamespacedKey(plugin, "blue_terracotta") + private val brownRecipeKey = NamespacedKey(plugin, "brown_terracotta") + private val cyanRecipeKey = NamespacedKey(plugin, "cyan_terracotta") + private val blueRecipeKey = NamespacedKey(plugin, "blue_terracotta") + private val blueRecipeKey = NamespacedKey(plugin, "blue_terracotta") + + private val allTerracotta = RecipeChoice.MaterialChoice(Material.TERRACOTTA, Material.BLACK_TERRACOTTA, + Material.BLUE_TERRACOTTA, Material.BROWN_TERRACOTTA, Material.CYAN_TERRACOTTA, Material.GRAY_TERRACOTTA, + Material.GREEN_TERRACOTTA, Material.LIGHT_BLUE_TERRACOTTA, + Material.LIGHT_GRAY_TERRACOTTA, Material.LIME_TERRACOTTA, Material.MAGENTA_TERRACOTTA, + Material.ORANGE_TERRACOTTA, Material.PINK_TERRACOTTA, Material.PURPLE_TERRACOTTA, Material.RED_TERRACOTTA, + Material.WHITE_TERRACOTTA, Material.YELLOW_TERRACOTTA) + + override fun getIdentifier(): String = "RedyeTerracotta" + override fun getCategories(): List = listOf("crafting", "survival") + + override fun onEnable() { + addRecipe(blackRecipeKey, Material.BLACK_TERRACOTTA, Material.BLACK_DYE) + } + + override fun onDisable() { + plugin.server.removeRecipe(blackRecipeKey) + } + + private fun addRecipe(key: NamespacedKey, output: Material, dye: Material) { + val recipe = ShapedRecipe(key, ItemStack(output)) + recipe.shape("TTT", "TDT", "TTT") + recipe.setIngredient('T', allTerracotta) + recipe.setIngredient('D', dye) + + plugin.server.addRecipe(recipe) + } +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/craftingtweaks/WoolToString.kt b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/craftingtweaks/WoolToString.kt new file mode 100644 index 0000000..edce4cb --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/craftingtweaks/WoolToString.kt @@ -0,0 +1,31 @@ +package nl.kallestruik.dtweaks.tweaks.craftingtweaks + +import nl.kallestruik.dtweaks.tweaks.ITweak +import org.bukkit.Bukkit +import org.bukkit.Material +import org.bukkit.NamespacedKey +import org.bukkit.Tag +import org.bukkit.inventory.ItemStack +import org.bukkit.inventory.RecipeChoice +import org.bukkit.inventory.ShapelessRecipe +import org.bukkit.plugin.java.JavaPlugin + +class WoolToString( + private val plugin: JavaPlugin +): ITweak { + private var recipeKey = NamespacedKey(plugin, "string") + + override fun getIdentifier(): String = "WoolToString" + override fun getCategories(): List = listOf("crafting", "survival") + + override fun onEnable() { + val stringRecipe = ShapelessRecipe(recipeKey, ItemStack(Material.STRING, 4)) + stringRecipe.addIngredient(RecipeChoice.MaterialChoice(Tag.WOOL)) + + plugin.server.addRecipe(stringRecipe) + } + + override fun onDisable() { + plugin.server.removeRecipe(recipeKey) + } +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/croptweaks/HoeHarvestArea.kt b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/croptweaks/HoeHarvestArea.kt new file mode 100644 index 0000000..3b11c22 --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/croptweaks/HoeHarvestArea.kt @@ -0,0 +1,62 @@ +package nl.kallestruik.dtweaks.tweaks.croptweaks + +import nl.kallestruik.dtweaks.tweaks.ITweak +import org.bukkit.Material +import org.bukkit.block.data.Ageable +import org.bukkit.event.EventHandler +import org.bukkit.event.HandlerList +import org.bukkit.event.Listener +import org.bukkit.event.block.BlockBreakEvent +import org.bukkit.plugin.java.JavaPlugin + +class HoeHarvestArea( + private val plugin: JavaPlugin +): ITweak, Listener { + override fun getIdentifier(): String = "HoesHarvestArea" + override fun getCategories(): List = listOf("crops", "survival") + + override fun onEnable() { + plugin.server.pluginManager.registerEvents(this, plugin) + } + + override fun onDisable() { + HandlerList.unregisterAll(this) + } + + @EventHandler + fun onHoeBreaksBlock(event: BlockBreakEvent) { + val player = event.player + val block = event.block + val world = block.world + val itemInHand = player.inventory.itemInMainHand + val range = when(itemInHand.type) { + Material.WOODEN_HOE -> 1 + Material.STONE_HOE -> 1 + Material.IRON_HOE -> 1 + Material.GOLDEN_HOE -> 1 + Material.DIAMOND_HOE -> 2 + Material.NETHERITE_HOE -> 2 + else -> return + } + + if (!isCrop(block.type)) return + + val startLocation = block.location + for (x in -range..range) { + for (z in -range..range) { + val b = world.getBlockAt(startLocation.clone().add(x.toDouble(), 0.0, z.toDouble())) + if (isCrop(b.type)) { + val ageable = b.blockData as Ageable + if (ageable.age == ageable.maximumAge) { + b.breakNaturally() + } + } + } + } + } + + + private fun isCrop(material: Material): Boolean { + return material == Material.WHEAT || material == Material.BEETROOTS || material == Material.CARROTS || material == Material.POTATOES + } +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/croptweaks/LilypadBonemealing.kt b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/croptweaks/LilypadBonemealing.kt new file mode 100644 index 0000000..e30d6ef --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/croptweaks/LilypadBonemealing.kt @@ -0,0 +1,54 @@ +package nl.kallestruik.dtweaks.tweaks.croptweaks + +import nl.kallestruik.dlib.DUtil +import nl.kallestruik.dtweaks.tweaks.ITweak +import org.bukkit.Location +import org.bukkit.Material +import org.bukkit.Particle +import org.bukkit.event.EventHandler +import org.bukkit.event.HandlerList +import org.bukkit.event.Listener +import org.bukkit.event.block.Action +import org.bukkit.event.player.PlayerInteractEvent +import org.bukkit.inventory.EquipmentSlot +import org.bukkit.plugin.java.JavaPlugin + +class LilypadBonemealing( + private val plugin: JavaPlugin, + private val util: DUtil +): ITweak, Listener { + override fun getIdentifier(): String = "LilypadBonemealing" + override fun getCategories(): List = listOf("crops", "survival") + + override fun onEnable() { + plugin.server.pluginManager.registerEvents(this, plugin) + } + + override fun onDisable() { + HandlerList.unregisterAll(this) + } + + @EventHandler + fun onRightClickBlock(event: PlayerInteractEvent) { + val player = event.player + if (event.hand != EquipmentSlot.HAND) return + if (event.action != Action.RIGHT_CLICK_BLOCK) return + if (event.clickedBlock == null) return + if (event.clickedBlock!!.type != Material.LILY_PAD) return + + val itemInHand = player.inventory.itemInMainHand + if (itemInHand.type != Material.BONE_MEAL) return + + val world = event.clickedBlock!!.world + val origin = event.clickedBlock!!.location + for (i in 0..5) { + val newPos: Location = origin.clone().add(util.getRandomLocationOffset(0, 3, false)) + val newBlock = world.getBlockAt(newPos) + if (newBlock.type != Material.AIR) continue + if (newBlock.getRelative(0, -1, 0).type != Material.WATER) continue + newBlock.type = Material.LILY_PAD + world.spawnParticle(Particle.VILLAGER_HAPPY, newPos, 5, 0.5, 0.5, 0.5) + } + itemInHand.amount = itemInHand.amount - 1 + } +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/croptweaks/MobsCantTrampleCrops.kt b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/croptweaks/MobsCantTrampleCrops.kt new file mode 100644 index 0000000..b4e1b93 --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/croptweaks/MobsCantTrampleCrops.kt @@ -0,0 +1,32 @@ +package nl.kallestruik.dtweaks.tweaks.croptweaks + +import nl.kallestruik.dtweaks.tweaks.ITweak +import org.bukkit.Material +import org.bukkit.entity.Player +import org.bukkit.event.EventHandler +import org.bukkit.event.HandlerList +import org.bukkit.event.Listener +import org.bukkit.event.entity.EntityChangeBlockEvent +import org.bukkit.plugin.java.JavaPlugin + +class MobsCantTrampleCrops( + private val plugin: JavaPlugin +): ITweak, Listener { + override fun getIdentifier(): String = "MobsCantTrampleCrops" + override fun getCategories(): List = listOf("crops", "mobs", "survival") + + override fun onEnable() { + plugin.server.pluginManager.registerEvents(this, plugin) + } + + override fun onDisable() { + HandlerList.unregisterAll(this) + } + + @EventHandler + fun onBlockBreak(event: EntityChangeBlockEvent) { + if (event.block.type != Material.FARMLAND) return + if (event.entity is Player) return + event.isCancelled = true + } +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/croptweaks/PlayersCantTrampleCrops.kt b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/croptweaks/PlayersCantTrampleCrops.kt new file mode 100644 index 0000000..38d7f60 --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/croptweaks/PlayersCantTrampleCrops.kt @@ -0,0 +1,35 @@ +package nl.kallestruik.dtweaks.tweaks.croptweaks + +import nl.kallestruik.dtweaks.managers.TrampleManager +import nl.kallestruik.dtweaks.tweaks.ITweak +import org.bukkit.Material +import org.bukkit.entity.Player +import org.bukkit.event.EventHandler +import org.bukkit.event.HandlerList +import org.bukkit.event.Listener +import org.bukkit.event.entity.EntityChangeBlockEvent +import org.bukkit.plugin.java.JavaPlugin + +class PlayersCantTrampleCrops( + private val plugin: JavaPlugin, + private val trampleManager: TrampleManager +): ITweak, Listener { + override fun getIdentifier(): String = "PlayersCantTrampleCrops" + override fun getCategories(): List = listOf("crops", "survival", "players") + + override fun onEnable() { + plugin.server.pluginManager.registerEvents(this, plugin) + } + + override fun onDisable() { + HandlerList.unregisterAll(this) + } + + @EventHandler + fun onBlockBreak(event: EntityChangeBlockEvent) { + if (event.block.type != Material.FARMLAND) return + if (event.entity !is Player) return + if (trampleManager.trampleEnabled.contains(event.entity.uniqueId)) return + event.isCancelled = true + } +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/croptweaks/SeedDropPlanting.kt b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/croptweaks/SeedDropPlanting.kt new file mode 100644 index 0000000..ea26193 --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/croptweaks/SeedDropPlanting.kt @@ -0,0 +1,65 @@ +package nl.kallestruik.dtweaks.tweaks.croptweaks + +import nl.kallestruik.dtweaks.tweaks.ITweak +import org.bukkit.Bukkit +import org.bukkit.Material +import org.bukkit.Particle +import org.bukkit.World +import org.bukkit.block.Block +import org.bukkit.entity.Item +import org.bukkit.event.Listener +import org.bukkit.plugin.java.JavaPlugin +import org.bukkit.scheduler.BukkitTask + +class SeedDropPlanting( + private val plugin: JavaPlugin +): ITweak, Listener { + private lateinit var task: BukkitTask + + override fun getIdentifier(): String = "SeedDropPlanting" + override fun getCategories(): List = listOf("crops", "survival") + + override fun onEnable() { + task = Bukkit.getScheduler().runTaskTimer(plugin, SeedPlantTask(plugin), (20 * 10).toLong(), (20 * 10).toLong()) + } + + override fun onDisable() { + task.cancel() + } + + private class SeedPlantTask( + private val plugin: JavaPlugin + ): Runnable { + override fun run() { + for (world in plugin.server.worlds) { + for (entity in world.entities) { + if (entity is Item) { + val b = world.getBlockAt(entity.location) + val above = world.getBlockAt(entity.location.add(0.0, 1.0, 0.0)) + if (b.type == Material.FARMLAND && above.type == Material.AIR) { + when (entity.itemStack.type) { + Material.WHEAT_SEEDS -> plantSeed(entity, world, above, Material.WHEAT) + Material.BEETROOT_SEEDS -> plantSeed(entity, world, above, Material.BEETROOTS) + Material.MELON_SEEDS -> plantSeed(entity, world, above, Material.MELON_STEM) + Material.PUMPKIN_SEEDS -> plantSeed(entity, world, above, Material.PUMPKIN_STEM) + Material.POTATO -> plantSeed(entity, world, above, Material.POTATOES) + Material.CARROT -> plantSeed(entity, world, above, Material.CARROTS) + else -> continue + } + } + } + } + } + } + + private fun plantSeed(entity: Item, world: World, block: Block, material: Material) { + block.type = material + world.spawnParticle(Particle.VILLAGER_HAPPY, entity.location, 10) + if (entity.itemStack.amount == 1) { + entity.remove() + } else { + entity.itemStack.amount = entity.itemStack.amount - 1 + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/croptweaks/SugarcaneBonemealing.kt b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/croptweaks/SugarcaneBonemealing.kt new file mode 100644 index 0000000..4989a38 --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/croptweaks/SugarcaneBonemealing.kt @@ -0,0 +1,53 @@ +package nl.kallestruik.dtweaks.tweaks.croptweaks + +import nl.kallestruik.dtweaks.tweaks.ITweak +import org.bukkit.Material +import org.bukkit.Particle +import org.bukkit.event.EventHandler +import org.bukkit.event.HandlerList +import org.bukkit.event.Listener +import org.bukkit.event.block.Action +import org.bukkit.event.player.PlayerInteractEvent +import org.bukkit.inventory.EquipmentSlot +import org.bukkit.plugin.java.JavaPlugin + +class SugarcaneBonemealing( + private val plugin: JavaPlugin +): ITweak, Listener { + override fun getIdentifier(): String = "SugarcaneBonemealing" + override fun getCategories(): List = listOf("crops", "survival") + + override fun onEnable() { + plugin.server.pluginManager.registerEvents(this, plugin) + } + + override fun onDisable() { + HandlerList.unregisterAll(this) + } + + @EventHandler + fun onRightClickBlock(event: PlayerInteractEvent) { + val player = event.player + + if (event.hand != EquipmentSlot.HAND) return + if (event.action != Action.RIGHT_CLICK_BLOCK) return + if (event.clickedBlock == null) return + if (event.clickedBlock!!.type != Material.SUGAR_CANE) return + val itemInHand = player.inventory.itemInMainHand + if (itemInHand.type != Material.BONE_MEAL) return + + val world = event.clickedBlock!!.world + val origin = event.clickedBlock!!.location + + for (i in 0..3) { + val newPos = origin.clone().add(0.0, i.toDouble(), 0.0) + val newBlock = world.getBlockAt(newPos) + if (newBlock.type != Material.AIR) continue + if (newBlock.getRelative(0, -1, 0).type != Material.SUGAR_CANE) continue + newBlock.type = Material.SUGAR_CANE + world.spawnParticle(Particle.VILLAGER_HAPPY, newPos, 5, 0.5, 0.5, 0.5) + itemInHand.amount = itemInHand.amount - 1 + break + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/dispsensertweaks/DispensersCanPlantSaplings.kt b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/dispsensertweaks/DispensersCanPlantSaplings.kt new file mode 100644 index 0000000..192a793 --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/dispsensertweaks/DispensersCanPlantSaplings.kt @@ -0,0 +1,74 @@ +package nl.kallestruik.dtweaks.tweaks.dispsensertweaks + +import nl.kallestruik.dtweaks.tweaks.ITweak +import org.bukkit.Material +import org.bukkit.Particle +import org.bukkit.Tag +import org.bukkit.block.BlockFace +import org.bukkit.block.data.type.Dispenser +import org.bukkit.event.EventHandler +import org.bukkit.event.HandlerList +import org.bukkit.event.Listener +import org.bukkit.event.block.BlockDispenseEvent +import org.bukkit.plugin.java.JavaPlugin + +class DispensersCanPlantSaplings( + private val plugin: JavaPlugin +): ITweak, Listener { + private val treePlantable = setOf(Material.DIRT, Material.COARSE_DIRT, Material.GRASS_BLOCK, Material.PODZOL, Material.MYCELIUM) + + override fun getIdentifier(): String = "DispensersCanPlantSaplings" + override fun getCategories(): List = listOf("dispenser", "survival") + + override fun onEnable() { + plugin.server.pluginManager.registerEvents(this, plugin) + } + + override fun onDisable() { + HandlerList.unregisterAll(this) + } + + @EventHandler + fun onDispenserDispense(event: BlockDispenseEvent) { + if (event.block.type != Material.DISPENSER) return + if (Tag.SAPLINGS.isTagged(event.item.type)) { + val dispenser = event.block.blockData as Dispenser + val facing = dispenser.facing + val targetBlock = if (facing == BlockFace.UP) { + event.block.world.getBlockAt(event.block.location.add(0.0, 2.0, 0.0)) + } else { + event.block.world.getBlockAt( + event.block.location.add( + facing.modX.toDouble(), + facing.modY.toDouble(), + facing.modZ.toDouble() + ) + ) + } + if (targetBlock.type == Material.AIR + && treePlantable.contains( + event.block.world.getBlockAt( + targetBlock.location.add( + 0.0, + -1.0, + 0.0 + ) + ).type + ) + ) { + targetBlock.type = event.item.type + event.isCancelled = true + val dispenserInventory = (event.block.state as org.bukkit.block.Dispenser).inventory + var slot = 0 + for (`is` in dispenserInventory.contents) { + if (`is` != null && `is`.type == event.item.type) break + slot++ + } + val newItemStack = dispenserInventory.getItem(slot) + newItemStack!!.amount = newItemStack.amount - 1 + dispenserInventory.setItem(slot, newItemStack) + event.block.world.spawnParticle(Particle.VILLAGER_HAPPY, targetBlock.location, 5) + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/miscellaneoustweaks/ArmorStandArmorSwapping.kt b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/miscellaneoustweaks/ArmorStandArmorSwapping.kt new file mode 100644 index 0000000..b23473c --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/miscellaneoustweaks/ArmorStandArmorSwapping.kt @@ -0,0 +1,52 @@ +package nl.kallestruik.dtweaks.tweaks.miscellaneoustweaks + +import nl.kallestruik.dtweaks.tweaks.ITweak +import org.bukkit.entity.ArmorStand +import org.bukkit.event.EventHandler +import org.bukkit.event.HandlerList +import org.bukkit.event.Listener +import org.bukkit.event.player.PlayerInteractAtEntityEvent +import org.bukkit.inventory.EquipmentSlot +import org.bukkit.plugin.java.JavaPlugin + +class ArmorStandArmorSwapping( + private val plugin: JavaPlugin +): ITweak, Listener { + override fun getIdentifier(): String = "ArmorStandArmorSwapping" + override fun getCategories(): List = listOf("miscellaneous", "survival") + + override fun onEnable() { + plugin.server.pluginManager.registerEvents(this, plugin) + } + + override fun onDisable() { + HandlerList.unregisterAll(this) + } + + @EventHandler + fun onClickEntity(event: PlayerInteractAtEntityEvent) { + if (event.player.isSneaking) { + if (event.rightClicked is ArmorStand) { + val armorStand = event.rightClicked as ArmorStand + val standEquipment = armorStand.equipment + val player = event.player + val playerEquipment = player.equipment + if (standEquipment == null || playerEquipment == null) return + + val playerHelmet = playerEquipment.helmet + val playerChestplate = playerEquipment.chestplate + val playerLeggings = playerEquipment.leggings + val playerBoots = playerEquipment.boots + player.inventory.helmet = standEquipment.helmet + player.inventory.chestplate = standEquipment.chestplate + player.inventory.leggings = standEquipment.leggings + player.inventory.boots = standEquipment.boots + armorStand.setItem(EquipmentSlot.HEAD, playerHelmet) + armorStand.setItem(EquipmentSlot.CHEST, playerChestplate) + armorStand.setItem(EquipmentSlot.LEGS, playerLeggings) + armorStand.setItem(EquipmentSlot.FEET, playerBoots) + event.isCancelled = true + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/miscellaneoustweaks/CarpetBlockPlacingProtocol.kt b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/miscellaneoustweaks/CarpetBlockPlacingProtocol.kt new file mode 100644 index 0000000..1ffcac8 --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/miscellaneoustweaks/CarpetBlockPlacingProtocol.kt @@ -0,0 +1,151 @@ +package nl.kallestruik.dtweaks.tweaks.miscellaneoustweaks + +import com.comphenix.protocol.PacketType +import com.comphenix.protocol.ProtocolLibrary +import com.comphenix.protocol.events.PacketAdapter +import com.comphenix.protocol.events.PacketEvent +import com.comphenix.protocol.wrappers.BlockPosition +import com.comphenix.protocol.wrappers.EnumWrappers.Hand +import nl.kallestruik.dlib.MathHelper +import nl.kallestruik.dtweaks.tweaks.ITweak +import org.bukkit.Bukkit +import org.bukkit.block.BlockFace +import org.bukkit.block.data.Bisected +import org.bukkit.block.data.BlockData +import org.bukkit.block.data.Directional +import org.bukkit.block.data.type.Comparator +import org.bukkit.block.data.type.Repeater +import org.bukkit.block.data.type.Stairs +import org.bukkit.block.data.type.TrapDoor +import org.bukkit.entity.Player +import org.bukkit.event.Listener +import org.bukkit.inventory.ItemStack +import org.bukkit.plugin.java.JavaPlugin + +class CarpetBlockPlacingProtocol( + private val plugin: JavaPlugin, + private val mathHelper: MathHelper +): ITweak, Listener { + private val protocolManager = ProtocolLibrary.getProtocolManager() + private var packetListener: PacketListener? = null + + override fun getIdentifier(): String = "CarpetBlockPlacingProtocol" + override fun getCategories(): List = listOf("player", "survival") + + override fun onEnable() { + this.packetListener = PacketListener(plugin, PacketType.Play.Client.USE_ITEM, mathHelper) + protocolManager.addPacketListener(this.packetListener) + } + + override fun onDisable() { + protocolManager.removePacketListener(this.packetListener) + } + + internal class PacketListener( + plugin: JavaPlugin?, + packetType: PacketType?, + private val mathHelper: MathHelper + ): PacketAdapter(plugin, packetType) { + override fun onPacketReceiving(event: PacketEvent) { + // Make sure the packet is the one we expect. + if (event.packetType !== PacketType.Play.Client.USE_ITEM) return + + // Get the hand that the player used. + val hand = event.packet.hands.values[0] ?: return + + // Get the MovingObjectPositionBlock and make sure it is not null. + val mopd = event.packet.movingBlockPositions.values[0] ?: return + val player = event.player + + // Get the item stack that was clicked with. + val itemStack = + if (hand == Hand.MAIN_HAND) player.inventory.itemInMainHand else player.inventory.itemInOffHand + + // Get the hitPos and BlockPos from the mopd. + val hitPos = mopd.posVector + val pos = mopd.blockPosition + + // Calculate hitX. + val hitX = hitPos.x - pos.x + + // Nothing special so ignore this packet. + if (hitX < 2) return + val code = (hitX - 2).toInt() / 2 + val blockData: BlockData = getBlockDataFromCode(code, itemStack, player, pos) ?: return + Bukkit.getScheduler().runTask(plugin, Runnable { + val blockToChange = player.world.getBlockAt(pos.x, pos.y, pos.z) + blockToChange.blockData = blockData + + // Cancel any further processing + event.isCancelled = true + + // Take one item from the players inventory. + itemStack.amount = itemStack.amount - 1 + }) + } + + private fun getBlockDataFromCode(code: Int, itemStack: ItemStack, player: Player, pos: BlockPosition): BlockData? { + val blockData = Bukkit.createBlockData(itemStack.type) + if (blockData is Repeater) { + val repeater = blockData + val delay: Int = mathHelper.clamp(code shr 4, 1, 4) + repeater.facing = horizontalBlockFaceFromCode(code and 0xF, player)!! + repeater.delay = delay + return repeater + } else if (blockData is TrapDoor) { + val trapDoor = blockData + val block = player.world.getBlockAt(pos.x, pos.y, pos.z) + trapDoor.facing = blockFaceFromCode(code and 0xF) + trapDoor.half = if (code > 0xF) Bisected.Half.TOP else Bisected.Half.BOTTOM + trapDoor.isOpen = block.isBlockPowered || block.isBlockIndirectlyPowered + return trapDoor + } else if (blockData is Comparator) { + val comparator = blockData + val mode = if (code > 0xF) Comparator.Mode.SUBTRACT else Comparator.Mode.COMPARE + comparator.facing = horizontalBlockFaceFromCode(code and 0xF, player)!! + comparator.mode = mode + return comparator + } else if (blockData is Stairs) { + val stairs = blockData + stairs.facing = blockFaceFromCode(code and 0xF) + stairs.half = if (code > 0xF) Bisected.Half.TOP else Bisected.Half.BOTTOM + return stairs + } else if (blockData is Directional) { + val directional = blockData + val possibilities = directional.faces + + // Check what type of directional we are dealing with. + if (possibilities.contains(BlockFace.UP)) { + directional.facing = blockFaceFromCode(code) + } else { + directional.facing = horizontalBlockFaceFromCode(code, player)!! + } + return directional + } + return null + } + + private fun blockFaceFromCode(code: Int): BlockFace { + return when (code) { + 0 -> BlockFace.DOWN + 1 -> BlockFace.UP + 2 -> BlockFace.NORTH + 3 -> BlockFace.SOUTH + 4 -> BlockFace.WEST + 5 -> BlockFace.EAST + else -> BlockFace.SELF + } + } + + private fun blockFaceFromRotation(rotation: Float): BlockFace? { + return if (rotation < 45 && rotation >= -45) BlockFace.SOUTH else if (rotation < 135 && rotation >= 45) BlockFace.WEST else if (rotation >= 135 && rotation < -135) BlockFace.NORTH else BlockFace.EAST + } + + private fun horizontalBlockFaceFromCode(code: Int, player: Player): BlockFace? { + val face = blockFaceFromCode(code) + return if (face == BlockFace.UP || face == BlockFace.DOWN) { + blockFaceFromRotation(player.location.yaw) + } else face + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/miscellaneoustweaks/FakePlayers.kt b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/miscellaneoustweaks/FakePlayers.kt new file mode 100644 index 0000000..6db4c15 --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/miscellaneoustweaks/FakePlayers.kt @@ -0,0 +1,15 @@ +package nl.kallestruik.dtweaks.tweaks.miscellaneoustweaks + +import nl.kallestruik.dtweaks.managers.FakePlayerManager +import nl.kallestruik.dtweaks.tweaks.ITweak + +class FakePlayers( + private val fakePlayerManager: FakePlayerManager +): ITweak { + override fun getIdentifier(): String = "FakePlayers" + override fun getCategories(): List = listOf("players") + + override fun onDisable() { + fakePlayerManager.killAllFakePlayers() + } +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/miscellaneoustweaks/SpaceTimePockets.kt b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/miscellaneoustweaks/SpaceTimePockets.kt new file mode 100644 index 0000000..ab10f39 --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/miscellaneoustweaks/SpaceTimePockets.kt @@ -0,0 +1,70 @@ +package nl.kallestruik.dtweaks.tweaks.miscellaneoustweaks + +import nl.kallestruik.dtweaks.managers.PocketDimensionManager +import nl.kallestruik.dtweaks.tweaks.ITweak +import org.bukkit.entity.Player +import org.bukkit.event.EventHandler +import org.bukkit.event.HandlerList +import org.bukkit.event.Listener +import org.bukkit.event.block.BlockBreakEvent +import org.bukkit.event.entity.CreatureSpawnEvent +import org.bukkit.event.entity.EntityDamageEvent +import org.bukkit.event.player.PlayerMoveEvent +import org.bukkit.plugin.java.JavaPlugin + +class SpaceTimePockets( + private val plugin: JavaPlugin, + private val pocketDimensionManager: PocketDimensionManager +): ITweak, Listener { + override fun getIdentifier(): String = "SpaceTimePockets" + override fun getCategories(): List = listOf("miscellaneous", "survival") + + override fun onRegister() { + plugin.server.pluginManager.registerEvents(this, plugin) + } + + override fun onUnRegister() { + HandlerList.unregisterAll(this) + } + + + @EventHandler + fun onBlockBreak(event: BlockBreakEvent) { + if (event.player.world != pocketDimensionManager.pocketDimension) { + return + } + if (event.block.location.chunk == event.player.location.chunk && event.block.y != 0 && event.block.y != 255) return + event.isCancelled = true + event.player.sendMessage("It seems like a bad idea to break the only thing keeping you from getting lost into the endlessness around you.") + } + + @EventHandler + fun onMobSpawn(event: CreatureSpawnEvent) { + if (pocketDimensionManager.pocketDimension != event.location.world) return + event.isCancelled = true + } + + @EventHandler + fun onPlayerDamage(event: EntityDamageEvent) { + if (event.entity !is Player) return + if (pocketDimensionManager.pocketDimension != event.entity.world) return + event.isCancelled = true + } + + @EventHandler + fun onPlayerFall(event: PlayerMoveEvent) { + if (pocketDimensionManager.pocketDimension != event.from.world + || pocketDimensionManager.pocketDimension != event.to.world + ) return + if (event.from.chunk != event.to.chunk) { + event.player.sendMessage("Something pulls you back from the darkness.") + event.isCancelled = true + } + if (event.to.blockY < 0 || event.to.blockY > 256) { + event.player.sendMessage("Something pulls you back from the darkness.") + val safeLocation = event.player.location + safeLocation.y = 128.0 + event.player.teleport(safeLocation) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/mobtweaks/NoCreeperGrief.kt b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/mobtweaks/NoCreeperGrief.kt new file mode 100644 index 0000000..185d2f1 --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/mobtweaks/NoCreeperGrief.kt @@ -0,0 +1,33 @@ +package nl.kallestruik.dtweaks.tweaks.mobtweaks + +import nl.kallestruik.dtweaks.tweaks.ITweak +import org.bukkit.entity.Creeper +import org.bukkit.entity.EntityType +import org.bukkit.event.EventHandler +import org.bukkit.event.HandlerList +import org.bukkit.event.Listener +import org.bukkit.event.entity.EntityExplodeEvent +import org.bukkit.plugin.java.JavaPlugin + +class NoCreeperGrief( + private val plugin: JavaPlugin +): ITweak, Listener { + override fun getIdentifier(): String = "NoCreeperGrief" + override fun getCategories(): List = listOf("mobs", "survival") + + override fun onEnable() { + plugin.server.pluginManager.registerEvents(this, plugin) + } + + override fun onDisable() { + HandlerList.unregisterAll(this) + } + + @EventHandler + fun onCreeperExplode(event: EntityExplodeEvent) { + if (event.entityType != EntityType.CREEPER) return + val creeper = event.entity as Creeper + creeper.world.createExplosion(creeper.location, event.yield, false, false) + event.isCancelled = true + } +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/mobtweaks/NoDoorBreaking.kt b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/mobtweaks/NoDoorBreaking.kt new file mode 100644 index 0000000..cd0cc83 --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/mobtweaks/NoDoorBreaking.kt @@ -0,0 +1,28 @@ +package nl.kallestruik.dtweaks.tweaks.mobtweaks + +import nl.kallestruik.dtweaks.tweaks.ITweak +import org.bukkit.event.EventHandler +import org.bukkit.event.HandlerList +import org.bukkit.event.Listener +import org.bukkit.event.entity.EntityBreakDoorEvent +import org.bukkit.plugin.java.JavaPlugin + +class NoDoorBreaking( + private val plugin: JavaPlugin +): ITweak, Listener { + override fun getIdentifier(): String = "NoDoorBreaking" + override fun getCategories(): List = listOf("mobs", "survival") + + override fun onEnable() { + plugin.server.pluginManager.registerEvents(this, plugin) + } + + override fun onDisable() { + HandlerList.unregisterAll(this) + } + + @EventHandler + fun onDoorBreak(event: EntityBreakDoorEvent) { + event.isCancelled = true + } +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/mobtweaks/NoEndermanGrief.kt b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/mobtweaks/NoEndermanGrief.kt new file mode 100644 index 0000000..aeaaa7b --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/mobtweaks/NoEndermanGrief.kt @@ -0,0 +1,30 @@ +package nl.kallestruik.dtweaks.tweaks.mobtweaks + +import nl.kallestruik.dtweaks.tweaks.ITweak +import org.bukkit.entity.EntityType +import org.bukkit.event.EventHandler +import org.bukkit.event.HandlerList +import org.bukkit.event.Listener +import org.bukkit.event.entity.EntityChangeBlockEvent +import org.bukkit.plugin.java.JavaPlugin + +class NoEndermanGrief( + private val plugin: JavaPlugin +): ITweak, Listener { + override fun getIdentifier(): String = "NoEndermanGrief" + override fun getCategories(): List = listOf("mobs", "survival") + + override fun onEnable() { + plugin.server.pluginManager.registerEvents(this, plugin) + } + + override fun onDisable() { + HandlerList.unregisterAll(this) + } + + @EventHandler + fun onEndermanPickupBlock(event: EntityChangeBlockEvent) { + if (event.entityType != EntityType.ENDERMAN) return + event.isCancelled = true + } +} \ No newline at end of file diff --git a/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/mobtweaks/VillagerInfo.kt b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/mobtweaks/VillagerInfo.kt new file mode 100644 index 0000000..b881316 --- /dev/null +++ b/src/main/kotlin/nl/kallestruik/dtweaks/tweaks/mobtweaks/VillagerInfo.kt @@ -0,0 +1,112 @@ +package nl.kallestruik.dtweaks.tweaks.mobtweaks + +import net.minecraft.server.v1_16_R3.Activity +import net.minecraft.server.v1_16_R3.EntityVillager +import net.minecraft.server.v1_16_R3.GlobalPos +import net.minecraft.server.v1_16_R3.MemoryModuleType +import nl.kallestruik.dtweaks.tweaks.ITweak +import org.bukkit.craftbukkit.v1_16_R3.entity.CraftVillager +import org.bukkit.entity.Villager +import org.bukkit.event.EventHandler +import org.bukkit.event.HandlerList +import org.bukkit.event.Listener +import org.bukkit.event.player.PlayerInteractEntityEvent +import org.bukkit.plugin.java.JavaPlugin +import java.util.concurrent.atomic.AtomicReference + +class VillagerInfo( + private val plugin: JavaPlugin +): ITweak, Listener { + override fun getIdentifier(): String = "VillagerInfo" + override fun getCategories(): List = listOf("mobs") + + override fun onEnable() { + plugin.server.pluginManager.registerEvents(this, plugin) + } + + override fun onDisable() { + HandlerList.unregisterAll(this) + } + + @EventHandler + fun onInteractEvent(event: PlayerInteractEntityEvent) { + if (!event.player.isSneaking) return + val clicked = event.rightClicked as? Villager ?: return + val eVillager: EntityVillager = (clicked as CraftVillager).handle + val homePos: AtomicReference = AtomicReference(null) + eVillager.behaviorController.getMemory(MemoryModuleType.HOME) + .ifPresent { newValue -> homePos.set(newValue) } + val workPos: AtomicReference = AtomicReference(null) + eVillager.behaviorController.getMemory(MemoryModuleType.JOB_SITE).ifPresent { newValue -> + workPos.set( + newValue + ) + } + val meetingPos: AtomicReference = AtomicReference(null) + eVillager.behaviorController.getMemory(MemoryModuleType.MEETING_POINT).ifPresent { newValue -> + meetingPos.set( + newValue + ) + } + val lastWork: AtomicReference = AtomicReference(null) + eVillager.behaviorController.getMemory(MemoryModuleType.LAST_WORKED_AT_POI).ifPresent { newValue -> + lastWork.set( + newValue + ) + } + val lastSleep: AtomicReference = AtomicReference(null) + eVillager.behaviorController.getMemory(MemoryModuleType.LAST_SLEPT).ifPresent { newValue -> + lastSleep.set( + newValue + ) + } + val seenIGRecently: AtomicReference = AtomicReference(null) + eVillager.behaviorController.getMemory(MemoryModuleType.GOLEM_DETECTED_RECENTLY).ifPresent { newValue -> + seenIGRecently.set( + newValue + ) + } + + val isPanicking: Boolean = eVillager.behaviorController.c(Activity.PANIC) + val player = event.player + + player.sendMessage("=====Villager Info=====") + player.sendMessage("Type: " + clicked.villagerType) + player.sendMessage("Profession: " + clicked.profession) + player.sendMessage("Level: " + clicked.villagerLevel + "(" + clicked.villagerExperience + " xp)") + player.sendMessage("Panic: $isPanicking") + + if (homePos.get() != null) player.sendMessage( + java.lang.String.format( + "Home: %s, %s, %s", + homePos.get()?.blockPosition?.x, + homePos.get()?.blockPosition?.y, + homePos.get()?.blockPosition?.z + ) + ) + + if (workPos.get() != null) player.sendMessage( + java.lang.String.format( + "Work: %s, %s, %s", + workPos.get()?.blockPosition?.x, + workPos.get()?.blockPosition?.y, + workPos.get()?.blockPosition?.z + ) + ) + + if (homePos.get() != null) player.sendMessage( + java.lang.String.format( + "Meeting point: %s, %s, %s", + meetingPos.get()?.blockPosition?.x, + meetingPos.get()?.blockPosition?.y, + meetingPos.get()?.blockPosition?.z + ) + ) + + if (lastSleep.get() != null) + player.sendMessage("Last slept: ${lastSleep.get()}") + + if (seenIGRecently.get() != null) + player.sendMessage("Seen iron golem Recently: ${seenIGRecently.get()}") + } +} \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..3070c90 --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,17 @@ +name: DTweaks +version: ${version} +main: nl.kallestruik.dtweaks.DTweaks +api-version: 1.16 +depend: + - "ProtocolLib" + - "DLib" +commands: + toggletrample: + description: "Toggles whether you can trample crops." + player: + description: "Interact with fake players." + permission: op + mobcaps: + description: "Display mobcap information from your current dimension." + pocketdim: + description: "Interact with pocket dimensions. Testing command." diff --git a/src/main/resources/tweak_states.yml b/src/main/resources/tweak_states.yml new file mode 100644 index 0000000..7010d68 --- /dev/null +++ b/src/main/resources/tweak_states.yml @@ -0,0 +1,24 @@ +CraftableNametag: true +CraftableSaddle: true +AlternativeDispenserRecipe: true +DispensersCanPlantSaplings: true +CraftableShulkerShell: true +IceDecompression: true +LogsToChests: true +CraftableSponge: true +PlayersCantTrampleCrops: true +WoolToString: true +LilypadBonemealing: true +SeedDropPlanting: true +MobsCantTrampleCrops: true +CraftableDragonsBreath: true +ArmorSwapping: true +HoesHarvestArea: true +FakePlayers: true +CarpetBlockPlacingProtocol: true +NoDoorBreaking: true +NoCreeperGrief: true +NoEndermanGrief: true +SugarcaneBonemealing: true +SpaceTimePockets: true +VillagerInfo: true \ No newline at end of file