feat: Add Better Bundles, Hammers, No Spawn Zones, No Melting, and More Sandstone tweaks

main
kalle 2022-06-21 15:46:17 +02:00
parent 471a683ae3
commit 10145032e2
72 changed files with 1803 additions and 519 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
/.gradle/
/.idea/
/build/
/run/
/textures/out/

View File

@ -2,7 +2,8 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
kotlin("jvm") version "1.5.31"
id("com.github.johnrengelman.shadow") version "5.2.0"
id("com.github.johnrengelman.shadow") version "7.1.2"
id("xyz.jpenilla.run-paper") version "1.0.6" // Adds runServer and runMojangMappedServer tasks for testing
id("io.papermc.paperweight.userdev") version "1.3.4"
}
@ -24,18 +25,13 @@ dependencies {
compileOnly("com.comphenix.protocol:ProtocolLib:4.6.0")
compileOnly(kotlin("stdlib-jdk8"))
compileOnly("nl.kallestruik:DLib:1.3.5")
compileOnly("nl.kallestruik:DLib:1.3.6")
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.shadowJar {
relocate("co.aikar.commands", "nl.kallestruik.dtweaks.acf")
relocate("co.aikar.locales", "nl.kallestruik.dtweaks.locales")
}
tasks.build {
dependsOn(tasks.shadowJar)
}
@ -49,6 +45,11 @@ tasks {
useJUnitPlatform()
}
shadowJar {
relocate("co.aikar.commands", "nl.kallestruik.dtweaks.acf")
relocate("co.aikar.locales", "nl.kallestruik.dtweaks.locales")
}
compileJava {
options.compilerArgs.add("-parameters")
}
@ -56,7 +57,7 @@ tasks {
kotlinOptions.javaParameters = true
}
withType<KotlinCompile> {
kotlinOptions.jvmTarget = "11"
kotlinOptions.jvmTarget = "16"
}
processResources {

View File

@ -0,0 +1,25 @@
package nl.kallestruik.dtweaks
import org.bukkit.Material
enum class BundleColor(
val modelData: Int,
val dye: Material,
) {
BLACK(0, Material.BLACK_DYE),
BLUE(1, Material.BLUE_DYE),
BROWN(2, Material.BROWN_DYE),
CYAN(3, Material.CYAN_DYE),
GRAY(4, Material.GRAY_DYE),
GREEN(5, Material.GREEN_DYE),
LIGHT_BLUE(6, Material.LIGHT_BLUE_DYE),
LIGHT_GRAY(7, Material.LIGHT_GRAY_DYE),
LIME(8, Material.LIME_DYE),
MAGENTA(9, Material.MAGENTA_DYE),
ORANGE(10, Material.ORANGE_DYE),
PINK(11, Material.PINK_DYE),
PURPLE(12, Material.PURPLE_DYE),
RED(13, Material.RED_DYE),
WHITE(14, Material.WHITE_DYE),
YELLOW(15, Material.YELLOW_DYE)
}

View File

@ -0,0 +1,15 @@
package nl.kallestruik.dtweaks
import org.bukkit.Material
enum class BundleVariant(
val modelDataStart: Int,
val maxWeight: Int,
val material: Material?,
) {
NORMAL(0, 1 * 64, null),
IRON(100, 2 * 64, Material.IRON_INGOT),
GOLD(200, 4 * 64, Material.GOLD_INGOT),
DIAMOND(300, 6 * 64, Material.DIAMOND),
NETHERITE(400, 8 * 64, Material.NETHERITE_INGOT)
}

View File

@ -3,4 +3,5 @@ package nl.kallestruik.dtweaks
object Const {
const val TRAMPLE_ENABLED_FILE_NAME = "tramplestore.yml"
const val TWEAK_STATE_FILE_NAME = "tweak_states.yml"
const val NO_SPAWN_ZONE_FILE_NAME = "no_spawn_store.yml"
}

View File

@ -5,18 +5,19 @@ 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.CommandBundle
import nl.kallestruik.dtweaks.commands.CommandPlayer
import nl.kallestruik.dtweaks.commands.CommandSpawnzones
import nl.kallestruik.dtweaks.commands.CommandToggletrample
import nl.kallestruik.dtweaks.managers.FakePlayerManager
import nl.kallestruik.dtweaks.managers.NoSpawnZoneManager
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.mobtweaks.NoCreeperGrief
import nl.kallestruik.dtweaks.tweaks.mobtweaks.NoDoorBreaking
import nl.kallestruik.dtweaks.tweaks.mobtweaks.NoEndermanGrief
import nl.kallestruik.dtweaks.tweaks.mobtweaks.VillagerInfo
import nl.kallestruik.dtweaks.tweaks.miscellaneoustweaks.*
import nl.kallestruik.dtweaks.tweaks.mobtweaks.*
import org.bukkit.plugin.java.JavaPlugin
import java.io.File
import java.util.*
@ -31,7 +32,8 @@ class DTweaks: JavaPlugin() {
lateinit var reflectionUtil: ReflectionUtil
private lateinit var trampleManager: TrampleManager
private lateinit var commandManager: PaperCommandManager
// private lateinit var fakePlayerManager: FakePlayerManager
private lateinit var fakePlayerManager: FakePlayerManager
private lateinit var noSpawnZoneManager: NoSpawnZoneManager
// private lateinit var pocketDimensionManager: PocketDimensionManager
}
@ -46,7 +48,8 @@ class DTweaks: JavaPlugin() {
reflectionUtil = ReflectionUtil()
trampleManager = TrampleManager()
commandManager = PaperCommandManager(this)
// fakePlayerManager = FakePlayerManager()
fakePlayerManager = FakePlayerManager(this)
noSpawnZoneManager = NoSpawnZoneManager()
// pocketDimensionManager = PocketDimensionManager(mathHelper)
// Enable brigadier support on ACF
@ -54,19 +57,24 @@ class DTweaks: JavaPlugin() {
registerCommandConditions()
val betterBundles = BetterBundles(this)
/*
* Commands
*/
// commandManager.registerCommand(CommandMobcaps(reflectionUtil))
// commandManager.registerCommand(CommandPlayer(fakePlayerManager, tweakManager))
commandManager.registerCommand(CommandPlayer(fakePlayerManager))
// commandManager.registerCommand(CommandPocketdim(pocketDimensionManager))
commandManager.registerCommand(CommandToggletrample(trampleManager))
commandManager.registerCommand(CommandBundle(betterBundles))
commandManager.registerCommand(CommandSpawnzones(noSpawnZoneManager))
/*
* Load data from disk
*/
tweakManager.loadFromFile(File(dataFolder, Const.TWEAK_STATE_FILE_NAME))
trampleManager.loadFromFile(File(dataFolder, Const.TRAMPLE_ENABLED_FILE_NAME))
noSpawnZoneManager.loadFromFile(File(dataFolder, Const.NO_SPAWN_ZONE_FILE_NAME))
// pocketDimensionManager.loadData()
/*
@ -80,8 +88,10 @@ class DTweaks: JavaPlugin() {
tweakManager.registerTweak(CraftableSponge(this))
tweakManager.registerTweak(IceDecompression(this))
tweakManager.registerTweak(LogsToChests(this))
tweakManager.registerTweak(MoreSandstone(this))
tweakManager.registerTweak(RedyeTerracotta(this))
tweakManager.registerTweak(WoolToString(this))
tweakManager.registerTweak(CraftableChainmail(this))
/*
* Crop Tweaks
@ -102,8 +112,12 @@ class DTweaks: JavaPlugin() {
* Miscellaneous Tweaks
*/
tweakManager.registerTweak(ArmorStandArmorSwapping(this))
tweakManager.registerTweak(CarpetBlockPlacingProtocol(this, mathHelper))
// tweakManager.registerTweak(FakePlayers(fakePlayerManager))
// tweakManager.registerTweak(CarpetBlockPlacingProtocol(this, mathHelper))
tweakManager.registerTweak(betterBundles)
// tweakManager.registerTweak(DyeableNetherite(this))
tweakManager.registerTweak(FakePlayers(fakePlayerManager, this))
tweakManager.registerTweak(Hammers(this))
tweakManager.registerTweak(NoMelting(this))
// tweakManager.registerTweak(SpaceTimePockets(this, pocketDimensionManager))
/*
@ -113,6 +127,7 @@ class DTweaks: JavaPlugin() {
tweakManager.registerTweak(NoDoorBreaking(this))
tweakManager.registerTweak(NoEndermanGrief(this))
tweakManager.registerTweak(VillagerInfo(this))
tweakManager.registerTweak(NoSpawnZones(this, noSpawnZoneManager))
}
override fun onDisable() {
@ -121,6 +136,7 @@ class DTweaks: JavaPlugin() {
*/
tweakManager.saveToFile(File(dataFolder, Const.TWEAK_STATE_FILE_NAME))
trampleManager.saveToFile(File(dataFolder, Const.TRAMPLE_ENABLED_FILE_NAME))
noSpawnZoneManager.saveToFile()
// pocketDimensionManager.saveData()
}
@ -130,4 +146,6 @@ class DTweaks: JavaPlugin() {
throw ConditionFailedException("The tweak ${context.getConfigValue("tweak", "")} is not enabled!")
}
}
fun getNamespace(): String = this.name.lowercase(Locale.ROOT)
}

View File

@ -0,0 +1,40 @@
package nl.kallestruik.dtweaks.commands
import co.aikar.commands.BaseCommand
import co.aikar.commands.annotation.*
import nl.kallestruik.dtweaks.BundleColor
import nl.kallestruik.dtweaks.BundleVariant
import nl.kallestruik.dtweaks.tweaks.miscellaneoustweaks.BetterBundles
import org.bukkit.Material
import org.bukkit.entity.Player
import org.bukkit.persistence.PersistentDataType
@CommandAlias("bundle")
@Conditions("tweakEnabled:tweak=BetterBundles")
class CommandBundle(
private val betterBundles: BetterBundles,
): BaseCommand() {
@Subcommand("variant")
fun onVariant(sender: Player, variant: BundleVariant) {
val bundle = sender.inventory.itemInMainHand
if (bundle.type != Material.BUNDLE)
return
val bundleMeta = bundle.itemMeta
bundleMeta.persistentDataContainer.set(betterBundles.VARIANT_KEY, PersistentDataType.STRING, variant.toString())
bundle.itemMeta = bundleMeta
betterBundles.updateBundle(bundle)
}
@Subcommand("color")
fun onVariant(sender: Player, color: BundleColor) {
val bundle = sender.inventory.itemInMainHand
if (bundle.type != Material.BUNDLE)
return
val bundleMeta = bundle.itemMeta
bundleMeta.persistentDataContainer.set(betterBundles.COLOR_KEY, PersistentDataType.STRING, color.toString())
bundle.itemMeta = bundleMeta
betterBundles.updateBundle(bundle)
}
}

View File

@ -1,28 +1,37 @@
//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)
// }
//}
package nl.kallestruik.dtweaks.commands
import co.aikar.commands.BaseCommand
import co.aikar.commands.annotation.*
import nl.kallestruik.dtweaks.managers.FakePlayerManager
import org.bukkit.entity.Player
@CommandAlias("player")
@Conditions("tweakEnabled:tweak=FakePlayers")
class CommandPlayer(
private val fakePlayerManager: FakePlayerManager
): 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)
}
@Subcommand("attack")
@CommandCompletion("@players")
fun onAttack(sender: Player, @Single playerName: String) {
fakePlayerManager.attack(playerName)
}
@Subcommand("use")
@CommandCompletion("@players")
fun onUse(sender: Player, @Single playerName: String) {
fakePlayerManager.use(playerName)
}
}

View File

@ -0,0 +1,101 @@
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 net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.NamedTextColor
import nl.kallestruik.dtweaks.managers.NoSpawnZoneManager
import org.bukkit.entity.Player
@CommandAlias("spawnzones")
@Conditions("tweakEnabled:tweak=NoSpawnZones")
class CommandSpawnzones(
private val noSpawnZoneManager: NoSpawnZoneManager
): BaseCommand() {
@Subcommand("claim")
inner class CommandClaim: BaseCommand() {
@Subcommand("one")
fun onClaimOne(sender: Player) {
noSpawnZoneManager.claimChunk(sender.location.chunk.x, sender.location.chunk.z, sender.world.key)
noSpawnZoneManager.saveToFile()
}
@Subcommand("square")
fun onClaimSquare(sender: Player, radius: Int) {
val centerX = sender.location.chunk.x
val centerZ = sender.location.chunk.z
for (x in centerX-radius..centerX+radius)
for (z in centerZ-radius..centerZ+radius)
noSpawnZoneManager.claimChunk(x, z, sender.world.key)
noSpawnZoneManager.saveToFile()
}
}
@Subcommand("unclaim")
inner class CommandUnclaim: BaseCommand() {
@Subcommand("one")
fun onClaimOne(sender: Player) {
noSpawnZoneManager.unclaimChunk(sender.location.chunk.x, sender.location.chunk.z, sender.world.key)
noSpawnZoneManager.saveToFile()
}
@Subcommand("square")
fun onClaimSquare(sender: Player, radius: Int) {
val centerX = sender.location.chunk.x
val centerZ = sender.location.chunk.z
for (x in centerX-radius..centerX+radius)
for (z in centerZ-radius..centerZ+radius)
noSpawnZoneManager.unclaimChunk(x, z, sender.world.key)
noSpawnZoneManager.saveToFile()
}
}
@Subcommand("map")
fun onMap(sender: Player) {
// Print the header of the faction map.
sender.sendMessage(Component
.empty()
.color(NamedTextColor.GRAY)
.append(Component.text("---------------["))
.append(Component
.text(" No Spawn Zones ")
.color(NamedTextColor.AQUA))
.append(Component.text("]---------------"))
)
val centerX = sender.location.chunk.x
val centerZ = sender.location.chunk.z
// Create and send the claim map.
for (x in -5..5) {
var row = Component.empty()
for (z in -6..6) {
row = if (x == 0 && z == 0) {
row.append(Component.text("").color(NamedTextColor.YELLOW))
} else if (noSpawnZoneManager.isChunkClaimed(centerX + x, centerZ + z, sender.world.key)) {
row.append(Component.text("").color(NamedTextColor.AQUA))
} else {
row.append(Component.text("").color(NamedTextColor.GRAY))
}
}
sender.sendMessage(row)
}
// Print the footer of the faction map.
sender.sendMessage(Component
.text("---------------------------------------")
.color(NamedTextColor.GRAY)
)
}
}

View File

@ -1,27 +1,19 @@
//package nl.kallestruik.dtweaks.fakeplayer
//
//import io.netty.util.concurrent.Future
//import io.netty.util.concurrent.GenericFutureListener
//
//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<out Future<in Void?>?>?) {}
//
// // disableAutoRead()
// override fun stopReading() {}
//
// override fun handleDisconnection() {}
//}
package nl.kallestruik.dtweaks.fakeplayer
import io.netty.channel.embedded.EmbeddedChannel
import net.minecraft.network.Connection
import net.minecraft.network.protocol.PacketFlow
class FakeConnection(
packetFlow: PacketFlow
): Connection(packetFlow) {
init {
this.channel = EmbeddedChannel()
}
override fun setReadOnly() {}
override fun handleDisconnection() {}
}

View File

@ -1,79 +1,68 @@
//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
//
package nl.kallestruik.dtweaks.fakeplayer
import com.mojang.authlib.GameProfile
import net.minecraft.network.chat.TextComponent
import net.minecraft.network.protocol.PacketFlow
import net.minecraft.network.protocol.game.ClientboundRotateHeadPacket
import net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket
import net.minecraft.server.MinecraftServer
import net.minecraft.server.TickTask
import net.minecraft.server.level.ServerLevel
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.damagesource.DamageSource
import net.minecraft.world.entity.Entity
import net.minecraft.world.food.FoodData
import net.minecraft.world.level.block.entity.SkullBlockEntity
import org.bukkit.Location
import org.bukkit.craftbukkit.v1_18_R1.CraftWorld
import java.util.concurrent.atomic.AtomicReference
class FakePlayer(
server: MinecraftServer,
val serverLevel: ServerLevel,
gameProfile: GameProfile
): ServerPlayer(server, serverLevel, gameProfile) {
val locale = "en_US"
companion object {
fun atLocation(location: Location, name: String): FakePlayer? {
// Get the minecraft server instance.
val minecraftServer = MinecraftServer.getServer()
// Get the world
val serverLevel = (location.world as CraftWorld).handle
// Get the game profile from the cache (or retrieve it if it is not cached)
var gameProfile = minecraftServer.profileCache.get(name).takeIf { it.isPresent }?.get() ?: return null
if (gameProfile.properties.containsKey("textures")) {
val result = AtomicReference<GameProfile>()
SkullBlockEntity.updateGameprofile(gameProfile, result::set)
gameProfile = result.get()
}
// Create an instance of our fake player.
val instance = FakePlayer(minecraftServer, serverLevel, gameProfile)
// Create a fake connection for our fake player.
val connection = FakeConnection(PacketFlow.SERVERBOUND)
FakePlayerList.placeNewPlayer(minecraftServer.playerList, minecraftServer, connection, instance)
instance.respawn()
instance.teleportTo(serverLevel, location.x, location.y, location.z, location.yaw, location.pitch)
instance.dead = false
instance.heal(instance.maxHealth)
instance.unsetRemoved()
instance.maxUpStep = 0.6F
minecraftServer.playerList.broadcastAll(
ClientboundRotateHeadPacket(
instance,
(instance.yHeadRot * 256 / 360).toInt().toByte()
), serverLevel.dimension())
minecraftServer.playerList.broadcastAll(ClientboundTeleportEntityPacket(instance), serverLevel.dimension())
instance.entityData.set(DATA_PLAYER_MODE_CUSTOMISATION, 0x7f)
return instance
// (DTweaks.reflectionUtil.getValueFromField(
// serverConnection!!,
// "connectedChannels"
@ -126,43 +115,36 @@
// instance.worldServer.getChunkProvider().movePlayer(instance)
// // bp == PLAYER_MODEL_PARTS
// instance.datawatcher.set<Byte>(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 kill() {
server.tell(TickTask(server.tickCount) {connection.onDisconnect(TextComponent("Killed"))})
}
override fun die(source: DamageSource) {
super.die(source)
heal(maxHealth)
foodData = FoodData(this)
kill()
}
override fun tick() {
super.tick()
if (server.tickCount % 10 == 0) {
connection.resetPosition()
serverLevel.chunkSource.move(this)
hasChangedDimension()
}
}
// override fun die(cause: DamageSource?) {
// super.die(cause)
// health = 20f
// foodData = FoodMetaData(this)
// killEntity()
// }
//}
override fun getIpAddress() = "127.0.0.1"
}

View File

@ -1,36 +1,31 @@
//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
// }
//}
package nl.kallestruik.dtweaks.fakeplayer
import io.netty.util.concurrent.Future
import io.netty.util.concurrent.GenericFutureListener
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.TranslatableComponent
import net.minecraft.network.protocol.Packet
import net.minecraft.server.MinecraftServer
import net.minecraft.server.network.ServerGamePacketListenerImpl
import javax.annotation.Nullable
class FakePlayerConnection(
minecraftServer: MinecraftServer,
connection: FakeConnection,
player: FakePlayer
): ServerGamePacketListenerImpl(minecraftServer, connection, player) {
override fun send(packetIn: Packet<*>?) {}
override fun send(packet: Packet<*>, listener: GenericFutureListener<out Future<in Void>>?) {}
override fun disconnect(message: Component) {
if (message is TranslatableComponent && (message.key.equals("multiplayer.disconnect.idling") || message.equals("multiplayer.disconnect.duplicate_login")))
{
player.kill()
}
}
override fun tick() {
println("Tick for ${player.name}")
super.tick()
}
}

View File

@ -1,258 +1,165 @@
//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<World> = if (playerData != null) {
// val dataResult: DataResult<ResourceKey<World>> = DimensionManager.a(
// Dynamic(
// DynamicOpsNBT.a,
// playerData["Dimension"]
// )
// )
// dataResult.resultOrPartial { o -> LOGGER.error(o) }
// .orElse(World.OVERWORLD) as ResourceKey<World>
package nl.kallestruik.dtweaks.fakeplayer
import com.destroystokyo.paper.PaperConfig
import com.destroystokyo.paper.event.player.PlayerInitialSpawnEvent
import com.mojang.authlib.GameProfile
import com.mojang.datafixers.util.Either
import com.mojang.serialization.Dynamic
import io.netty.buffer.Unpooled
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.NbtOps
import net.minecraft.network.Connection
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.network.protocol.game.*
import net.minecraft.resources.ResourceKey
import net.minecraft.server.MinecraftServer
import net.minecraft.server.level.*
import net.minecraft.server.level.ChunkHolder.ChunkLoadingFailure
import net.minecraft.server.network.ServerGamePacketListenerImpl
import net.minecraft.server.players.GameProfileCache
import net.minecraft.server.players.PlayerList
import net.minecraft.world.level.ChunkPos
import net.minecraft.world.level.GameRules
import net.minecraft.world.level.Level
import net.minecraft.world.level.biome.BiomeManager
import net.minecraft.world.level.chunk.ChunkAccess
import net.minecraft.world.level.dimension.DimensionType
import nl.kallestruik.dlib.ReflectionUtil
import nl.kallestruik.dtweaks.DTweaks
import org.apache.logging.log4j.Logger
import org.bukkit.Bukkit
import org.bukkit.craftbukkit.v1_18_R1.CraftWorld
import org.bukkit.entity.Player
import org.spigotmc.event.player.PlayerSpawnLocationEvent
import java.util.*
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
object FakePlayerList {
fun placeNewPlayer(playerList: PlayerList, server: MinecraftServer, connection: FakeConnection, player: FakePlayer) {
player.isRealPlayer = true // Paper - Chunk priority
player.networkManager = connection // Paper
player.loginTime = System.currentTimeMillis() // Paper
val gameProfile: GameProfile = player.getGameProfile()
val userCache: GameProfileCache = server.getProfileCache()
val optional = userCache[gameProfile.id]
var s: String? = optional.map { obj: GameProfile -> obj.name }.orElse(gameProfile.name)
userCache.add(gameProfile)
val nbtTagCompound: CompoundTag? = playerList.load(player)
// CraftBukkit start - Better rename detection
// CraftBukkit start - Better rename detection
if (nbtTagCompound != null && nbtTagCompound.contains("bukkit")) {
val bukkit = nbtTagCompound.getCompound("bukkit")
s = if (bukkit.contains("lastKnownName", 8)) bukkit.getString("lastKnownName") else s
}
val lastKnownName = s // Paper
// CraftBukkit end
// Paper start - move logic in Entity to here, to use bukkit supplied world UUID.
// CraftBukkit end
// Paper start - move logic in Entity to here, to use bukkit supplied world UUID.
val resourceKey: ResourceKey<Level> = if (nbtTagCompound != null && nbtTagCompound.contains("WorldUUIDMost") && nbtTagCompound.contains("WorldUUIDLeast")) {
val uid = UUID(nbtTagCompound.getLong("WorldUUIDMost"), nbtTagCompound.getLong("WorldUUIDLeast"))
val bWorld = Bukkit.getServer().getWorld(uid)
if (bWorld != null) {
(bWorld as CraftWorld).handle.dimension()
} else {
Level.OVERWORLD
}
} else if (nbtTagCompound != null) {
// Vanilla migration support
// Paper end
val dataResult = DimensionType.parseLegacy(
Dynamic(
NbtOps.INSTANCE,
nbtTagCompound["Dimension"]
)
)
dataResult.result().orElse(Level.OVERWORLD)
} else {
Level.OVERWORLD
}
var worldServer: ServerLevel = server.getLevel(resourceKey) ?: server.overworld()
if (nbtTagCompound == null) player.fudgeSpawnLocation(worldServer) // Paper - only move to spawn on first login, otherwise, stay where you are....
player.setLevel(worldServer)
// Spigot start - spawn location event
val spawnPlayer: Player = player.bukkitEntity
val ev: PlayerSpawnLocationEvent =
PlayerInitialSpawnEvent(spawnPlayer, spawnPlayer.location) // Paper use our duplicate event
Bukkit.getServer().pluginManager.callEvent(ev)
val loc = ev.spawnLocation
worldServer = (loc.world as CraftWorld).handle
player.spawnIn(worldServer)
player.gameMode.setLevel(player.level as ServerLevel)
// Paper start - set raw so we aren't fully joined to the world (not added to chunk or world)
// Paper start - set raw so we aren't fully joined to the world (not added to chunk or world)
player.setPosRaw(loc.x, loc.y, loc.z)
player.setRot(loc.yaw, loc.pitch)
// Paper end
// Spigot end
// CraftBukkit - Moved message to after join
// PlayerList.LOGGER.info("{}[{}] logged in with entity id {} at ({}, {}, {})", entityplayer.getName().getString(), s1, entityplayer.getId(), entityplayer.getX(), entityplayer.getY(), entityplayer.getZ());
// Paper end
// Spigot end
// CraftBukkit - Moved message to after join
// PlayerList.LOGGER.info("{}[{}] logged in with entity id {} at ({}, {}, {})", entityplayer.getName().getString(), s1, entityplayer.getId(), entityplayer.getX(), entityplayer.getY(), entityplayer.getZ());
val worlddata = worldServer.getLevelData()
player.loadGameTypes(nbtTagCompound)
val playerconnection = FakePlayerConnection(server, connection, player)
val gamerules = worldServer.gameRules
player.bukkitEntity.sendSupportedChannels() // CraftBukkit
player.stats.markAllDirty()
player.recipeBook.sendInitialRecipeBook(player)
playerList.updateEntireScoreboard(worldServer.scoreboard, player)
server.invalidateStatus()
// Paper start - async load spawn in chunk
// Paper start - async load spawn in chunk
val finalWorldserver = worldServer
val chunkX = loc.blockX shr 4
val chunkZ = loc.blockZ shr 4
val pos = ChunkPos(chunkX, chunkZ)
val playerChunkMap = worldServer.getChunkSource().chunkMap
val distanceManager: DistanceManager = playerChunkMap.distanceManager
distanceManager.addTicketAtLevel(TicketType.LOGIN, pos, 31, pos.toLong())
worldServer.getChunkSource().markAreaHighPriority(pos, 28, 3) // Paper - Chunk priority
val postChunkLoadJoin = playerList::class.java.superclass.getDeclaredMethod("postChunkLoadJoin", ServerPlayer::class.java, ServerLevel::class.java, Connection::class.java, ServerGamePacketListenerImpl::class.java, CompoundTag::class.java, String::class.java, String::class.java)
postChunkLoadJoin.isAccessible = true
postChunkLoadJoin.invoke(playerList, player, finalWorldserver, connection, playerconnection,
nbtTagCompound, "127.0.0.1", lastKnownName)
// worldServer.getChunkSource().getChunkAtAsynchronously(chunkX, chunkZ, true, false)
// .thenApply { chunk: Either<ChunkAccess, ChunkLoadingFailure> -> // Paper - Chunk priority
// val updatingChunk = playerChunkMap.getUpdatingChunkIfPresent(pos.toLong())
// if (updatingChunk != null) {
// return@thenApply updatingChunk.entityTickingChunkFuture
// } else {
// World.OVERWORLD
// return@thenApply CompletableFuture.completedFuture(
// chunk
// )
// }
// var worldServer = server.getWorldServer(worldKey) ?: server.E()
// }.thenAccept {
//
// 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<String, EntityPlayer>
// playersByName[entityPlayer.name.toLowerCase(Locale.ROOT)] = entityPlayer
//
// //playerList.j.put(entityplayer.getUniqueID(), entityplayer);
// val playersByUUID = DTweaks.reflectionUtil.getValueFromField(playerList, "j") as HashMap<UUID, EntityPlayer>
// playersByUUID[entityPlayer.uniqueID] = entityPlayer
//
// if (entityPlayer.playerConnection.networkManager.isConnected) {
// var i: Int
// if (joinMessage != null && joinMessage.isNotEmpty()) {
// var var27: Array<IChatBaseComponent?>
// 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()
// )
// }
// }
// }
}
}

View File

@ -1,30 +1,222 @@
//package nl.kallestruik.dtweaks.managers
package nl.kallestruik.dtweaks.managers
import net.minecraft.core.Direction
import net.minecraft.world.InteractionHand
import net.minecraft.world.entity.Entity
import net.minecraft.world.level.ClipContext
import net.minecraft.world.phys.*
import nl.kallestruik.dlib.ReflectionUtil
import nl.kallestruik.dtweaks.DTweaks
import nl.kallestruik.dtweaks.fakeplayer.FakePlayer
import org.bukkit.Bukkit
import org.bukkit.Location
import org.bukkit.craftbukkit.v1_18_R1.entity.CraftEntity
import org.bukkit.craftbukkit.v1_18_R1.entity.CraftPlayer
import org.bukkit.scheduler.BukkitRunnable
import org.bukkit.scheduler.BukkitTask
import java.util.function.Predicate
/*
A lot of code in this file is inspired by code from carpet mod.
Link: https://github.com/gnembon/fabric-carpet
*/
class FakePlayerManager(
val plugin: DTweaks,
) {
init {
// Tick all fake players.
FakePlayerTickTask().runTaskTimer(plugin, 10, 10)
}
fun getPlayerByName(name: String): FakePlayer? {
val player = Bukkit.getPlayer(name)
if (player == null || !player.isOnline) return null
return (player as CraftPlayer).handle as? FakePlayer
}
fun toFakePlayer(entity: org.bukkit.entity.Entity): FakePlayer? {
return (entity as CraftEntity).handle as? FakePlayer
}
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) {
getPlayerByName(name)?.kill()
}
fun killAllFakePlayers() {
for (player in Bukkit.getOnlinePlayers()) {
if (!player.isOnline) continue
val fakePlayer = (player as CraftPlayer).handle as? FakePlayer ?: continue
fakePlayer.kill()
}
}
private fun tickAll() {
for (player in Bukkit.getOnlinePlayers()) {
if (!player.isOnline) continue
val fakePlayer = (player as CraftPlayer).handle as? FakePlayer ?: continue
fakePlayer.doTick()
}
}
// fun attackInterval(name: String, interval: Int) {
//
//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 {
// fakePlayer.swing()
// }
fun attack(name: String) {
val player = getPlayerByName(name) ?: return
val target = getTarget(player) ?: return
when (target.type) {
HitResult.Type.BLOCK -> {/*TODO: Not implemented*/}
HitResult.Type.ENTITY -> {
if (target !is EntityHitResult) return
player.attack(target.entity);
player.swing(InteractionHand.MAIN_HAND);
player.resetLastActionTime();
}
else -> return
}
}
fun use(name: String) {
val player = getPlayerByName(name) ?: return
val target = getTarget(player) ?: return
for (hand in InteractionHand.values()) {
when (target.type) {
HitResult.Type.BLOCK -> {
if (target !is BlockHitResult) return
player.resetLastActionTime()
val world = player.getLevel()
val pos = target.blockPos
val side = target.direction
if (pos.y < player.getLevel().maxBuildHeight - (if (side === Direction.UP) 1 else 0)
&& world.mayInteract(player, pos)) {
val result = player.gameMode.useItemOn(player, world, player.getItemInHand(hand), hand, target)
if (result.consumesAction()) {
if (result.shouldSwing()) player.swing(hand)
return
}
}
}
HitResult.Type.ENTITY -> {
if (target !is EntityHitResult) return
player.resetLastActionTime()
val entity = target.entity
val relativeHitPos: Vec3 = target.getLocation().subtract(entity.x, entity.y, entity.z)
entity.interactAt(player, relativeHitPos, hand).consumesAction()
// fix for SS itemframe always returns CONSUME even if no action is performed
player.interactOn(entity, hand).consumesAction()
}
}
player.gameMode.useItem(player, player.getLevel(), player.getItemInHand(hand), hand)
}
}
//
// fun spawnFakePlayer(loc: Location, name: String) {
// val player = Bukkit.getPlayer(name)
// if (player != null && player.isOnline) return
// FakePlayer.atLocation(loc, name)
// }
// Ray tracing code borrowed from carpet mod: https://github.com/gnembon/fabric-carpet/blob/master/src/main/java/carpet/helpers/Tracer.java
//
// 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()
// }
// }
//}
fun getTarget(player: FakePlayer): HitResult? {
val reach: Double = if (player.gameMode.isCreative) 5.0 else 4.5f.toDouble()
return rayTrace(player, 1F, reach, false)
}
fun rayTrace(source: FakePlayer, partialTicks: Float, reach: Double, fluids: Boolean): HitResult? {
val blockHit = rayTraceBlocks(source, partialTicks, reach, fluids)
var maxSqDist = reach * reach
if (blockHit != null) {
maxSqDist = blockHit.location.distanceToSqr(source.getEyePosition(partialTicks))
}
val entityHit = rayTraceEntities(source, partialTicks, reach, maxSqDist)
return entityHit ?: blockHit
}
fun rayTraceBlocks(source: FakePlayer, partialTicks: Float, reach: Double, fluids: Boolean): BlockHitResult? {
val pos: Vec3 = source.getEyePosition(partialTicks)
val rotation: Vec3 = source.getViewVector(partialTicks)
val reachEnd = pos.add(rotation.x * reach, rotation.y * reach, rotation.z * reach)
return source.level.clip(
ClipContext(
pos,
reachEnd,
ClipContext.Block.OUTLINE,
if (fluids) ClipContext.Fluid.ANY else ClipContext.Fluid.NONE,
source
)
)
}
fun rayTraceEntities(source: FakePlayer, partialTicks: Float, reach: Double, maxSqDist: Double): EntityHitResult? {
val pos = source.getEyePosition(partialTicks)
val reachVec = source.getViewVector(partialTicks).scale(reach)
val box = source.boundingBox.expandTowards(reachVec).inflate(1.0)
return rayTraceEntities(source, pos, pos.add(reachVec), box, maxSqDist) {
e -> !e.isSpectator && e.isPickable
}
}
fun rayTraceEntities(
source: FakePlayer,
start: Vec3,
end: Vec3,
box: AABB,
maxSqDistance: Double,
predicate: Predicate<Entity>,
): EntityHitResult? {
val world = source.level
var targetDistance = maxSqDistance
var target: Entity? = null
var targetHitPos: Vec3? = null
for (current in world.getEntities(source, box, predicate)) {
val currentBox = current.boundingBox.inflate(current.pickRadius.toDouble())
val currentHit = currentBox.clip(start, end)
if (currentBox.contains(start)) {
if (targetDistance >= 0) {
target = current
targetHitPos = currentHit.orElse(start)
targetDistance = 0.0
}
} else if (currentHit.isPresent) {
val currentHitPos: Vec3 = currentHit.get()
val currentDistance = start.distanceToSqr(currentHitPos)
if (currentDistance < targetDistance || targetDistance == 0.0) {
if (current.rootVehicle === source.rootVehicle) {
if (targetDistance == 0.0) {
target = current
targetHitPos = currentHitPos
}
} else {
target = current
targetHitPos = currentHitPos
targetDistance = currentDistance
}
}
}
}
return if (target == null) null else EntityHitResult(target, targetHitPos!!)
}
inner class FakePlayerTickTask: BukkitRunnable() {
override fun run() {
tickAll()
}
}
}

View File

@ -0,0 +1,70 @@
package nl.kallestruik.dtweaks.managers
import org.bukkit.NamespacedKey
import org.bukkit.configuration.InvalidConfigurationException
import org.bukkit.configuration.file.YamlConfiguration
import java.io.File
import java.io.IOException
class NoSpawnZoneManager {
lateinit var file: File
var noSpawnZones: MutableMap<NamespacedKey, MutableSet<Pair<Int, Int>>> = mutableMapOf()
@Throws(IOException::class, InvalidConfigurationException::class)
fun loadFromFile(file: File) {
this.file = file
file.parentFile.mkdirs()
file.createNewFile()
val config = YamlConfiguration()
config.load(file)
noSpawnZones.clear()
for (world in config.getKeys(false)) {
val worldKey = NamespacedKey.fromString(world)!!
noSpawnZones[worldKey] = mutableSetOf()
for (item in config.getStringList(world)) {
val parts = item.split(":")
if (parts.size != 2)
continue
noSpawnZones[worldKey]?.add(Pair(parts[0].toInt(), parts[1].toInt()))
}
}
}
@Throws(IOException::class, InvalidConfigurationException::class)
fun saveToFile() {
file.parentFile.mkdirs()
file.createNewFile()
val config = YamlConfiguration()
config.load(file)
for (entry in noSpawnZones) {
val world = entry.key.toString()
val list = mutableListOf<String>()
for (chunk in entry.value) {
list.add("${chunk.first}:${chunk.second}")
}
config[world] = list
}
config.save(file)
}
fun isChunkClaimed(x: Int, z: Int, world: NamespacedKey): Boolean {
return noSpawnZones[world]?.contains(Pair(x, z)) ?: false
}
fun claimChunk(x: Int, z: Int, world: NamespacedKey) {
val chunks = noSpawnZones.getOrDefault(world, mutableSetOf())
chunks.add(Pair(x, z))
noSpawnZones[world] = chunks
}
fun unclaimChunk(x: Int, z: Int, world: NamespacedKey) {
val chunks = noSpawnZones.getOrDefault(world, mutableSetOf())
chunks.remove(Pair(x, z))
noSpawnZones[world] = chunks
}
}

View File

@ -0,0 +1,51 @@
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 CraftableChainmail(
private val plugin: JavaPlugin
): ITweak {
private var helmetRecipeKey = NamespacedKey(plugin, "chainmail_helmet")
private var chestplateRecipeKey = NamespacedKey(plugin, "chainmail_chestplate")
private var leggingsRecipeKey = NamespacedKey(plugin, "chainmail_leggings")
private var bootsRecipeKey = NamespacedKey(plugin, "chainmail_boots")
override fun getIdentifier(): String = "CraftableChainmail"
override fun getCategories(): List<String> = listOf("crafting", "survival")
override fun onEnable() {
val helmetRecipe = ShapedRecipe(helmetRecipeKey, ItemStack(Material.CHAINMAIL_HELMET))
helmetRecipe.shape("CCC", "C C")
helmetRecipe.setIngredient('C', Material.CHAIN)
val chestplateRecipe = ShapedRecipe(chestplateRecipeKey, ItemStack(Material.CHAINMAIL_CHESTPLATE))
chestplateRecipe.shape("C C", "CCC", "CCC")
chestplateRecipe.setIngredient('C', Material.CHAIN)
val leggingsRecipe = ShapedRecipe(leggingsRecipeKey, ItemStack(Material.CHAINMAIL_LEGGINGS))
leggingsRecipe.shape("CCC", "C C", "C C")
leggingsRecipe.setIngredient('C', Material.CHAIN)
val bootsRecipe = ShapedRecipe(bootsRecipeKey, ItemStack(Material.CHAINMAIL_BOOTS))
bootsRecipe.shape("C C", "C C")
bootsRecipe.setIngredient('C', Material.CHAIN)
plugin.server.addRecipe(helmetRecipe)
plugin.server.addRecipe(chestplateRecipe)
plugin.server.addRecipe(leggingsRecipe)
plugin.server.addRecipe(bootsRecipe)
}
override fun onDisable() {
plugin.server.removeRecipe(helmetRecipeKey)
plugin.server.removeRecipe(chestplateRecipeKey)
plugin.server.removeRecipe(leggingsRecipeKey)
plugin.server.removeRecipe(bootsRecipeKey)
}
}

View File

@ -0,0 +1,41 @@
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.inventory.ShapelessRecipe
import org.bukkit.plugin.java.JavaPlugin
class MoreSandstone(
private val plugin: JavaPlugin
): ITweak {
private var normalRecipeKey = NamespacedKey(plugin, "sandstone")
private var redRecipeKey = NamespacedKey(plugin, "red_sandstone")
override fun getIdentifier(): String = "MoreSandstone"
override fun getCategories(): List<String> = listOf("crafting", "survival")
override fun onEnable() {
val normalRecipe = ShapedRecipe(normalRecipeKey, ItemStack(Material.SANDSTONE, 4))
normalRecipe.shape("ss", "ss")
normalRecipe.setIngredient('s', Material.SAND)
val redRecipe = ShapedRecipe(redRecipeKey, ItemStack(Material.RED_SANDSTONE, 4))
redRecipe.shape("ss", "ss")
redRecipe.setIngredient('s', Material.RED_SAND)
plugin.server.addRecipe(normalRecipe)
plugin.server.addRecipe(redRecipe)
}
override fun onDisable() {
plugin.server.removeRecipe(normalRecipeKey)
plugin.server.removeRecipe(redRecipeKey)
}
}

View File

@ -77,7 +77,7 @@ class RedyeTerracotta(
}
private fun addRecipe(key: NamespacedKey, output: Material, dye: Material) {
val recipe = ShapedRecipe(key, ItemStack(output))
val recipe = ShapedRecipe(key, ItemStack(output, 8))
recipe.shape("TTT", "TDT", "TTT")
recipe.setIngredient('T', allTerracotta)
recipe.setIngredient('D', dye)

View File

@ -0,0 +1,345 @@
package nl.kallestruik.dtweaks.tweaks.miscellaneoustweaks
import com.google.common.base.Preconditions
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.NamedTextColor
import net.kyori.adventure.text.format.TextDecoration
import nl.kallestruik.dtweaks.BundleColor
import nl.kallestruik.dtweaks.BundleVariant
import nl.kallestruik.dtweaks.DTweaks
import nl.kallestruik.dtweaks.tweaks.ITweak
import org.bukkit.Bukkit
import org.bukkit.Keyed
import org.bukkit.Material
import org.bukkit.NamespacedKey
import org.bukkit.event.EventHandler
import org.bukkit.event.HandlerList
import org.bukkit.event.Listener
import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.event.inventory.PrepareItemCraftEvent
import org.bukkit.inventory.*
import org.bukkit.inventory.meta.BundleMeta
import org.bukkit.persistence.PersistentDataType
class BetterBundles(
private val plugin: DTweaks
): ITweak, Listener {
override fun getIdentifier(): String = "BetterBundles"
override fun getCategories(): List<String> = listOf("miscellaneous", "survival")
private val recipeKeys = mutableListOf<NamespacedKey>()
override fun onEnable() {
plugin.server.pluginManager.registerEvents(this, plugin)
for (color in BundleColor.values()) {
val result = ItemStack(Material.BUNDLE)
val resultMeta = result.itemMeta
resultMeta.setCustomModelData(BundleVariant.NORMAL.modelDataStart + color.modelData)
resultMeta.persistentDataContainer.set(COLOR_KEY, PersistentDataType.STRING, color.toString())
result.itemMeta = resultMeta
val key = NamespacedKey(plugin, "${color.name.lowercase()}_color_bundle")
recipeKeys.add(key)
addDyeRecipe(key, color.dye, result)
}
for (variant in BundleVariant.values()) {
if (variant.material == null)
continue
val result = ItemStack(Material.BUNDLE)
val resultMeta = result.itemMeta
resultMeta.setCustomModelData(variant.modelDataStart + BundleColor.BROWN.modelData)
resultMeta.persistentDataContainer.set(VARIANT_KEY, PersistentDataType.STRING, variant.toString())
result.itemMeta = resultMeta
val key = NamespacedKey(plugin, "${variant.name.lowercase()}_variant_bundle")
recipeKeys.add(key)
addUpgradeRecipe(key, variant.material, result)
}
}
override fun onDisable() {
HandlerList.unregisterAll(this)
for (key in recipeKeys) {
plugin.server.removeRecipe(key)
}
recipeKeys.clear()
}
val VARIANT_KEY = NamespacedKey(plugin, "variant")
val COLOR_KEY = NamespacedKey(plugin, "color")
private fun addDyeRecipe(key: NamespacedKey, dye: Material, result: ItemStack) {
val recipe = ShapelessRecipe(key, result)
recipe.addIngredient(dye)
recipe.addIngredient(Material.BUNDLE)
plugin.server.addRecipe(recipe)
}
private fun addUpgradeRecipe(key: NamespacedKey, material: Material, result: ItemStack) {
val recipe = ShapedRecipe(key, result)
recipe.shape("MMM", "MBM", "MMM")
recipe.setIngredient('M', material)
recipe.setIngredient('B', Material.BUNDLE)
plugin.server.addRecipe(recipe)
}
private fun isBundleRecipe(recipe: Recipe): Boolean {
return isDyeRecipe(recipe) || isVariantRecipe(recipe)
}
private fun isDyeRecipe(recipe: Recipe): Boolean {
if (recipe !is Keyed)
return false
val key = recipe.key
if (key.namespace != plugin.getNamespace())
return false
if (!key.key.endsWith("_color_bundle"))
return false
return true
}
private fun isVariantRecipe(recipe: Recipe): Boolean {
if (recipe !is Keyed)
return false
val key = recipe.key
if (key.namespace != plugin.getNamespace())
return false
if (!key.key.endsWith("_variant_bundle"))
return false
return true
}
@EventHandler
fun onCraft(event: PrepareItemCraftEvent) {
val recipe = event.recipe ?: return
if (!isBundleRecipe(recipe))
return
val inventory = event.inventory
Bukkit.getScheduler().runTask(
plugin,
Runnable {
inventory.result =
if (isDyeRecipe(recipe)) getDyeResult(recipe, inventory)
else if (isVariantRecipe(recipe)) getVariantResult(recipe, inventory)
else null
})
}
private fun getDyeResult(recipe: Recipe, inventory: CraftingInventory): ItemStack? {
val bundle = inventory.matrix?.filter { it?.type == Material.BUNDLE }?.get(0) ?: return null
val recipeResult = recipe.result
val result = bundle.clone()
val resultMeta = result.itemMeta
resultMeta.persistentDataContainer.set(
COLOR_KEY,
PersistentDataType.STRING,
recipeResult.itemMeta.persistentDataContainer.get(
COLOR_KEY,
PersistentDataType.STRING
) ?: BundleColor.BROWN.name
)
result.itemMeta = resultMeta
updateBundle(result)
return result
}
private fun getVariantResult(recipe: Recipe, inventory: CraftingInventory): ItemStack? {
val bundle = inventory.matrix?.filter { it?.type == Material.BUNDLE }?.get(0) ?: return null
val recipeResult = recipe.result
val result = bundle.clone()
val resultMeta = result.itemMeta
resultMeta.persistentDataContainer.set(
VARIANT_KEY,
PersistentDataType.STRING,
recipeResult.itemMeta.persistentDataContainer.get(
VARIANT_KEY,
PersistentDataType.STRING
) ?: BundleVariant.NORMAL.name
)
result.itemMeta = resultMeta
updateBundle(result)
return result
}
@EventHandler
fun onInventoryClick(event: InventoryClickEvent) {
if (!event.isRightClick)
return
val cursor = event.cursor ?: return
val clicked = event.currentItem ?: return
val bundle: ItemStack
val item: ItemStack
var itemIsCursor = true
if (cursor.type == Material.BUNDLE) {
bundle = cursor
item = clicked
itemIsCursor = false
} else if (clicked.type == Material.BUNDLE) {
bundle = clicked
item = cursor
} else
return
if (item.type.isAir || item.amount == 0) {
val itemStack = removeItemFromBundle(bundle) ?: return
if (itemIsCursor)
event.cursor = itemStack
else
event.currentItem = itemStack
} else
addItemToBundle(bundle, item)
event.isCancelled = true
}
private fun removeItemFromBundle(bundle: ItemStack): ItemStack? {
val meta = bundle.itemMeta as BundleMeta
if (!meta.hasItems())
return null
val itemsInBundle = mutableListOf<ItemStack>()
itemsInBundle.addAll(meta.items)
val toReturn = itemsInBundle.removeFirst()
meta.setItems(itemsInBundle)
bundle.itemMeta = meta
updateBundle(bundle)
return toReturn
}
private fun addItemToBundle(bundle: ItemStack, item: ItemStack) {
var meta = bundle.itemMeta as BundleMeta
val persistentContainer = bundle.itemMeta.persistentDataContainer
val variant = BundleVariant.valueOf(persistentContainer.get(VARIANT_KEY, PersistentDataType.STRING) ?: "NORMAL")
val currentWeight = computeWeight(meta.items)
val availableWeight = variant.maxWeight - currentWeight
val weightPerItem = getWeightPerItem(item)
val totalStackWeight = weightPerItem * item.amount
if (totalStackWeight > availableWeight) {
val toStoreAmount = availableWeight / weightPerItem
if (toStoreAmount <= 0)
return
meta = addItemToBundleProper(meta, item.asQuantity(toStoreAmount))
item.amount -= toStoreAmount
} else {
meta = addItemToBundleProper(meta, item.clone())
item.amount = 0
}
bundle.itemMeta = meta
updateBundle(bundle)
}
fun updateBundle(bundle: ItemStack) {
val meta = bundle.itemMeta as BundleMeta
val persistentContainer = meta.persistentDataContainer
val variant = BundleVariant.valueOf(persistentContainer.get(VARIANT_KEY, PersistentDataType.STRING) ?: "NORMAL")
val color = BundleColor.valueOf(persistentContainer.get(COLOR_KEY, PersistentDataType.STRING) ?: "BROWN")
val customModelData = variant.modelDataStart + color.modelData
meta.setCustomModelData(customModelData)
meta.addItemFlags(ItemFlag.HIDE_POTION_EFFECTS)
meta.lore(listOf(Component
.text("${computeWeight(meta.items)}/${variant.maxWeight}")
.color(NamedTextColor.GRAY)
.decoration(TextDecoration.ITALIC, false)))
bundle.itemMeta = meta
}
private fun computeWeight(items: List<ItemStack>): Int {
return items.sumOf { getWeight(it) }
}
private fun getWeight(item: ItemStack): Int {
return item.amount * getWeightPerItem(item)
}
private fun getWeightPerItem(item: ItemStack): Int {
// Special case for bundle nesting.
if (item.type == Material.BUNDLE) {
val meta = item.itemMeta as BundleMeta
return if (meta.hasItems())
64
else
4
}
// Calculate the right weight.
return (64 / item.maxStackSize)
}
private fun addItemToBundleProper(itemMeta: BundleMeta, itemStack: ItemStack): BundleMeta {
Preconditions.checkArgument(!itemStack.type.isAir, "item is null or air")
var leftOver = true
val items = mutableListOf<ItemStack>()
items.addAll(itemMeta.items)
for (item in itemMeta.items) {
if (item.isSimilar(itemStack)) {
if (item.amount >= item.maxStackSize)
continue
items.remove(item)
val spaceLeft = item.maxStackSize - item.amount
if (spaceLeft >= itemStack.amount) {
item.amount += itemStack.amount
itemStack.amount = 0
items.add(0, item)
leftOver = false
break
} else {
item.amount = item.maxStackSize
itemStack.amount -= spaceLeft
items.add(0, item)
}
}
}
if (leftOver)
items.add(0, itemStack)
itemMeta.setItems(items)
return itemMeta
}
}

View File

@ -0,0 +1,109 @@
package nl.kallestruik.dtweaks.tweaks.miscellaneoustweaks
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.format.NamedTextColor
import net.kyori.adventure.text.format.TextDecoration
import nl.kallestruik.dtweaks.DTweaks
import nl.kallestruik.dtweaks.tweaks.ITweak
import org.bukkit.Keyed
import org.bukkit.Material
import org.bukkit.NamespacedKey
import org.bukkit.attribute.Attribute
import org.bukkit.attribute.AttributeModifier
import org.bukkit.event.EventHandler
import org.bukkit.event.HandlerList
import org.bukkit.event.Listener
import org.bukkit.event.inventory.PrepareSmithingEvent
import org.bukkit.inventory.*
import org.bukkit.plugin.java.JavaPlugin
import java.util.*
class DyeableNetherite(
private val plugin: DTweaks
): ITweak, Listener {
override fun getIdentifier(): String = "DyeableNetherite"
override fun getCategories(): List<String> = listOf("miscellaneous", "survival")
private val helmetRecipeKey = NamespacedKey(plugin, "dyed_netherite_helmet")
private val chestplateRecipeKey = NamespacedKey(plugin, "dyed_netherite_chestplate")
private val leggingsRecipeKey = NamespacedKey(plugin, "dyed_netherite_leggings")
private val bootsRecipeKey = NamespacedKey(plugin, "dyed_netherite_boots")
private val helmetStats = ArmorStats(2.0, 3.0, 0.1, EquipmentSlot.HEAD)
private val chestplateStats = ArmorStats(5.0, 3.0, 0.1, EquipmentSlot.CHEST)
private val leggingsStats = ArmorStats(4.0, 3.0, 0.1, EquipmentSlot.LEGS)
private val bootsStats = ArmorStats(2.0, 3.0, 0.1, EquipmentSlot.FEET)
override fun onEnable() {
plugin.server.pluginManager.registerEvents(this, plugin)
addRecipe(helmetRecipeKey, Material.NETHERITE_HELMET, "Netherite Helmet", Material.LEATHER_HELMET)
addRecipe(chestplateRecipeKey, Material.NETHERITE_CHESTPLATE, "Netherite Chestplate", Material.LEATHER_CHESTPLATE)
addRecipe(leggingsRecipeKey, Material.NETHERITE_LEGGINGS, "Netherite Leggings", Material.LEATHER_LEGGINGS)
addRecipe(bootsRecipeKey, Material.NETHERITE_BOOTS, "Netherite Boots", Material.LEATHER_BOOTS)
}
private fun addRecipe(key: NamespacedKey, sourceMaterial: Material, name: String, resultMaterial: Material) {
val resultStack = ItemStack(resultMaterial)
resultStack.editMeta { it.displayName(Component.text(name).color(NamedTextColor.WHITE).decoration(TextDecoration.ITALIC, false))}
val recipe = SmithingRecipe(key, resultStack, RecipeChoice.MaterialChoice(sourceMaterial), RecipeChoice.MaterialChoice(Material.LEATHER))
plugin.server.addRecipe(recipe)
}
override fun onDisable() {
HandlerList.unregisterAll(this)
plugin.server.removeRecipe(helmetRecipeKey)
plugin.server.removeRecipe(chestplateRecipeKey)
plugin.server.removeRecipe(leggingsRecipeKey)
plugin.server.removeRecipe(bootsRecipeKey)
}
@EventHandler
fun onSmith(event: PrepareSmithingEvent) {
val recipe = event.inventory.recipe ?: return
if (!isDyeableNetheriteRecipe(recipe))
return
val result = event.inventory.result ?: return
when (result.type) {
Material.LEATHER_HELMET -> helmetStats.addToItem(result)
Material.LEATHER_CHESTPLATE -> chestplateStats.addToItem(result)
Material.LEATHER_LEGGINGS -> leggingsStats.addToItem(result)
Material.LEATHER_BOOTS -> bootsStats.addToItem(result)
else -> return
}
event.inventory.result = result
}
private fun isDyeableNetheriteRecipe(recipe: Recipe): Boolean {
if (recipe !is Keyed)
return false
if (recipe.key.namespace != plugin.getNamespace())
return false
if (!recipe.key.key.startsWith("dyed_netherite_"))
return false
return true
}
data class ArmorStats(
val armor: Double,
val toughness: Double,
val knockbackResistance: Double,
val slot: EquipmentSlot,
) {
fun addToItem(item: ItemStack) {
item.type = Material.DIAMOND
item.editMeta {
it.addAttributeModifier(Attribute.GENERIC_ARMOR, AttributeModifier(UUID.randomUUID(), "Armor", armor, AttributeModifier.Operation.ADD_NUMBER, slot))
it.addAttributeModifier(Attribute.GENERIC_ARMOR_TOUGHNESS, AttributeModifier(UUID.randomUUID(), "Armor Toughness", toughness, AttributeModifier.Operation.ADD_NUMBER, slot))
it.addAttributeModifier(Attribute.GENERIC_KNOCKBACK_RESISTANCE, AttributeModifier(UUID.randomUUID(), "Knockback Resistance", knockbackResistance, AttributeModifier.Operation.ADD_NUMBER, slot))
}
}
}
}

View File

@ -1,15 +1,48 @@
//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<String> = listOf("players")
//
// override fun onDisable() {
// fakePlayerManager.killAllFakePlayers()
// }
//}
package nl.kallestruik.dtweaks.tweaks.miscellaneoustweaks
import nl.kallestruik.dtweaks.DTweaks
import nl.kallestruik.dtweaks.fakeplayer.FakePlayer
import nl.kallestruik.dtweaks.managers.FakePlayerManager
import nl.kallestruik.dtweaks.tweaks.ITweak
import org.bukkit.Material
import org.bukkit.craftbukkit.v1_18_R1.entity.CraftPlayer
import org.bukkit.craftbukkit.v1_18_R1.inventory.CraftItemStack
import org.bukkit.entity.Player
import org.bukkit.event.EventHandler
import org.bukkit.event.HandlerList
import org.bukkit.event.Listener
import org.bukkit.event.player.PlayerInteractEntityEvent
import org.bukkit.inventory.EquipmentSlot
class FakePlayers(
private val fakePlayerManager: FakePlayerManager,
private val plugin: DTweaks
): ITweak, Listener {
override fun getIdentifier(): String = "FakePlayers"
override fun getCategories(): List<String> = listOf("players")
override fun onEnable() {
plugin.server.pluginManager.registerEvents(this, plugin)
}
override fun onDisable() {
HandlerList.unregisterAll(this)
fakePlayerManager.killAllFakePlayers()
}
@EventHandler
fun onRightClickEntity(event: PlayerInteractEntityEvent) {
if (event.hand != EquipmentSlot.HAND) return
val fakePlayer = event.rightClicked as? Player ?: return
if ((fakePlayer as CraftPlayer).handle !is FakePlayer) return
val playerItem = event.player.inventory.itemInMainHand.clone()
val fakePlayerItem = fakePlayer.inventory.itemInMainHand.clone()
event.player.inventory.setItemInMainHand(fakePlayerItem)
fakePlayer.inventory.setItemInMainHand(playerItem)
fakePlayer.handle.tick()
}
}

View File

@ -0,0 +1,134 @@
package nl.kallestruik.dtweaks.tweaks.miscellaneoustweaks
import nl.kallestruik.dtweaks.DTweaks
import nl.kallestruik.dtweaks.tweaks.ITweak
import org.bukkit.Material
import org.bukkit.NamespacedKey
import org.bukkit.block.Block
import org.bukkit.block.BlockFace
import org.bukkit.event.EventHandler
import org.bukkit.event.HandlerList
import org.bukkit.event.Listener
import org.bukkit.event.block.BlockBreakEvent
import org.bukkit.inventory.*
import org.bukkit.inventory.meta.Damageable
import org.bukkit.persistence.PersistentDataType
class Hammers(
private val plugin: DTweaks
): ITweak, Listener {
override fun getIdentifier(): String = "Hammers"
override fun getCategories(): List<String> = listOf("miscellaneous", "survival")
val HAMMER_KEY = NamespacedKey(plugin, "hammer")
val diamondHammerRecipeKey = NamespacedKey(plugin, "diamond_hammer")
override fun onEnable() {
plugin.server.pluginManager.registerEvents(this, plugin)
val diamondHammer = ItemStack(Material.DIAMOND_PICKAXE)
val diamondHammerMeta = diamondHammer.itemMeta
diamondHammerMeta.persistentDataContainer.set(HAMMER_KEY, PersistentDataType.SHORT, 1)
diamondHammer.itemMeta = diamondHammerMeta
updateHammer(diamondHammer)
val recipe = ShapedRecipe(diamondHammerRecipeKey, diamondHammer)
recipe.shape(
" BD",
" SB",
"S ")
recipe.setIngredient('B', Material.DIAMOND_BLOCK)
recipe.setIngredient('D', Material.DIAMOND)
recipe.setIngredient('S', Material.STICK)
plugin.server.addRecipe(recipe)
}
override fun onDisable() {
HandlerList.unregisterAll(this)
plugin.server.removeRecipe(diamondHammerRecipeKey)
}
fun updateHammer(hammer: ItemStack) {
if (!isHammer(hammer))
return
val meta = hammer.itemMeta
meta.setCustomModelData(1)
hammer.itemMeta = meta
}
private fun isHammer(itemStack: ItemStack): Boolean {
val meta = itemStack.itemMeta ?: return false
val persistentContainer = meta.persistentDataContainer
return persistentContainer.get(HAMMER_KEY, PersistentDataType.SHORT) == 1.toShort()
}
@EventHandler
fun onBlockBreak(event: BlockBreakEvent) {
val hammer = event.player.inventory.itemInMainHand
if (!isHammer(hammer))
return
val player = event.player
val lastTwoTargetBlocks: List<Block> = player.getLastTwoTargetBlocks(null, 100)
if (lastTwoTargetBlocks.size != 2 || !lastTwoTargetBlocks[1].type.isOccluding) return
val targetBlock: Block = lastTwoTargetBlocks[1]
val adjacentBlock: Block = lastTwoTargetBlocks[0]
val targetFace = targetBlock.getFace(adjacentBlock)
val toBreak: MutableList<Block> = mutableListOf()
when (targetFace) {
BlockFace.NORTH, BlockFace.SOUTH -> {
toBreak.add(targetBlock.getRelative(1, 0, 0))
toBreak.add(targetBlock.getRelative(-1, 0, 0))
toBreak.add(targetBlock.getRelative(0, 1, 0))
toBreak.add(targetBlock.getRelative(0, -1, 0))
toBreak.add(targetBlock.getRelative(1, 1, 0))
toBreak.add(targetBlock.getRelative(-1, 1, 0))
toBreak.add(targetBlock.getRelative(1, -1, 0))
toBreak.add(targetBlock.getRelative(-1, -1, 0))
}
BlockFace.EAST, BlockFace.WEST -> {
toBreak.add(targetBlock.getRelative(0, 1, 0))
toBreak.add(targetBlock.getRelative(0, -1, 0))
toBreak.add(targetBlock.getRelative(0, 0, 1))
toBreak.add(targetBlock.getRelative(0, 0, -1))
toBreak.add(targetBlock.getRelative(0, 1, 1))
toBreak.add(targetBlock.getRelative(0, -1, 1))
toBreak.add(targetBlock.getRelative(0, 1, -1))
toBreak.add(targetBlock.getRelative(0, -1, -1))
}
BlockFace.UP, BlockFace.DOWN -> {
toBreak.add(targetBlock.getRelative(1, 0, 0))
toBreak.add(targetBlock.getRelative(-1, 0, 0))
toBreak.add(targetBlock.getRelative(0, 0, 1))
toBreak.add(targetBlock.getRelative(0, 0, -1))
toBreak.add(targetBlock.getRelative(1, 0, 1))
toBreak.add(targetBlock.getRelative(-1, 0, 1))
toBreak.add(targetBlock.getRelative(1, 0, -1))
toBreak.add(targetBlock.getRelative(-1, 0, -1))
}
else -> return
}
val maxHardness = targetBlock.type.hardness
for (block in toBreak) {
if (block.type.hardness > maxHardness)
continue
block.breakNaturally(hammer)
}
}
}

View File

@ -0,0 +1,36 @@
package nl.kallestruik.dtweaks.tweaks.miscellaneoustweaks
import nl.kallestruik.dtweaks.tweaks.ITweak
import org.bukkit.Material
import org.bukkit.entity.ArmorStand
import org.bukkit.event.EventHandler
import org.bukkit.event.HandlerList
import org.bukkit.event.Listener
import org.bukkit.event.block.BlockFadeEvent
import org.bukkit.event.block.BlockFromToEvent
import org.bukkit.event.player.PlayerInteractAtEntityEvent
import org.bukkit.inventory.EquipmentSlot
import org.bukkit.plugin.java.JavaPlugin
class NoMelting(
private val plugin: JavaPlugin
): ITweak, Listener {
override fun getIdentifier(): String = "NoMelting"
override fun getCategories(): List<String> = listOf("miscellaneous", "survival")
override fun onEnable() {
plugin.server.pluginManager.registerEvents(this, plugin)
}
override fun onDisable() {
HandlerList.unregisterAll(this)
}
@EventHandler
fun onClickEntity(event: BlockFadeEvent) {
if (event.block.type != Material.SNOW && event.block.type != Material.ICE)
return
event.isCancelled = true
}
}

View File

@ -0,0 +1,44 @@
package nl.kallestruik.dtweaks.tweaks.mobtweaks
import net.minecraft.core.GlobalPos
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.schedule.Activity
import nl.kallestruik.dtweaks.managers.NoSpawnZoneManager
import nl.kallestruik.dtweaks.tweaks.ITweak
import org.bukkit.craftbukkit.v1_18_R1.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.entity.CreatureSpawnEvent
import org.bukkit.event.entity.EntitySpawnEvent
import org.bukkit.event.player.PlayerInteractEntityEvent
import org.bukkit.plugin.java.JavaPlugin
import java.util.concurrent.atomic.AtomicReference
class NoSpawnZones(
private val plugin: JavaPlugin,
private val noSpawnZoneManager: NoSpawnZoneManager
): ITweak, Listener {
override fun getIdentifier(): String = "NoSpawnZones"
override fun getCategories(): List<String> = listOf("mobs")
override fun onEnable() {
plugin.server.pluginManager.registerEvents(this, plugin)
}
override fun onDisable() {
HandlerList.unregisterAll(this)
}
@EventHandler
fun onMobSpawn(event: CreatureSpawnEvent) {
if (event.spawnReason != CreatureSpawnEvent.SpawnReason.NATURAL)
return
if (!noSpawnZoneManager.isChunkClaimed(event.location.chunk.x, event.location.chunk.z, event.location.world.key))
return
event.isCancelled = true
}
}

View File

@ -11,7 +11,12 @@ commands:
player:
description: "Interact with fake players."
permission: op
bundle:
description: "Admin command for managing custom bundles."
permission: op
mobcaps:
description: "Display mobcap information from your current dimension."
pocketdim:
description: "Interact with pocket dimensions. Testing command."
spawnzones:
description: "Manage no spawn zones"

View File

@ -17,8 +17,15 @@ HoesHarvestArea: true
FakePlayers: true
CarpetBlockPlacingProtocol: true
NoDoorBreaking: true
NoCreeperGrief: true
NoCreeperGrief: false
NoEndermanGrief: true
SugarcaneBonemealing: true
SpaceTimePockets: true
VillagerInfo: true
BetterBundles: true
RedyeTerracotta: true
DyeableNetherite: true
CraftableChainmail: true
NoMelting: true
Hammers: true
NoSpawnZones: true

106
textures/make_bundles.py Normal file
View File

@ -0,0 +1,106 @@
from PIL import Image
import os
import json
variants = [
{"name": "normal", "data": 0},
{"name": "iron", "data": 100},
{"name": "gold", "data": 200},
{"name": "diamond", "data": 300},
{"name": "netherite", "data": 400}
]
colors = [
{"name": "black", "data": 0},
{"name": "blue", "data": 1},
{"name": "brown", "data": 2},
{"name": "cyan", "data": 3},
{"name": "gray", "data": 4},
{"name": "green", "data": 5},
{"name": "light_blue", "data": 6},
{"name": "light_gray", "data": 7},
{"name": "lime", "data": 8},
{"name": "magenta", "data": 9},
{"name": "orange", "data": 10},
{"name": "pink", "data": 11},
{"name": "purple", "data": 12},
{"name": "red", "data": 13},
{"name": "white", "data": 14},
{"name": "yellow", "data": 15}
]
COLOR_SRC_DIR = "src/bundles/colors"
VARIANT_SRC_DIR = "src/bundles/variants"
MODEL_OUTPUT_DIR = "out/bundles/models"
TEXTURE_OUTPUT_DIR = "out/bundles/textures"
bundle_model = {
"parent": "item/generated",
"textures": {
"layer0": "item/bundle"
},
"overrides": [
{
"predicate": {
"filled": 0.0000001
},
"model": "item/bundle_filled"
},
]
}
for variant in variants:
variant_name = variant["name"]
variant_data = variant["data"]
for color in colors:
color_name = color["name"]
color_data = color["data"]
color_image = Image.open(f"{COLOR_SRC_DIR}/{color_name}_bundle.png")
color_image_filled = Image.open(f"{COLOR_SRC_DIR}/{color_name}_bundle_filled.png")
variant_image = Image.open(f"{VARIANT_SRC_DIR}/cord_{variant_name}.png")
variant_image_filled = Image.open(f"{VARIANT_SRC_DIR}/cord_{variant_name}_filled.png")
color_image.paste(variant_image, None, variant_image)
color_image_filled.paste(variant_image_filled, None, variant_image_filled)
color_image.save(f"{TEXTURE_OUTPUT_DIR}/{color_name}_{variant_name}_bundle.png")
color_image_filled.save(f"{TEXTURE_OUTPUT_DIR}/{color_name}_{variant_name}_bundle_filled.png")
bundle_model["overrides"].append({
"predicate": {
"custom_model_data": (variant_data + color_data)
},
"model": f"item/{color_name}_{variant_name}_bundle"
})
bundle_model["overrides"].append({
"predicate": {
"filled": 0.0000001,
"custom_model_data": variant_data + color_data
},
"model": f"item/{color_name}_{variant_name}_bundle_filled"
})
with open(f"{MODEL_OUTPUT_DIR}/{color_name}_{variant_name}_bundle.json", "w") as output_file:
json.dump({
"parent": "item/bundle",
"textures": {
"layer0": f"item/{color_name}_{variant_name}_bundle"
}
}, output_file, indent=4)
with open(f"{MODEL_OUTPUT_DIR}/{color_name}_{variant_name}_bundle_filled.json", "w") as output_file:
json.dump({
"parent": "item/bundle_filled",
"textures": {
"layer0": f"item/{color_name}_{variant_name}_bundle_filled"
}
}, output_file, indent=4)
with open(f"{MODEL_OUTPUT_DIR}/bundle.json", "w") as output_file:
json.dump(bundle_model, output_file, indent=4)

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 367 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 424 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 370 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 435 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 424 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 360 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 447 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 439 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 431 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 437 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 370 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 431 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 369 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 373 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 435 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 363 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 435 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 434 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 370 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 433 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 455 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 476 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 347 B

View File

@ -0,0 +1,6 @@
{
"parent": "item/handheld",
"textures": {
"layer0": "item/diamond_hammer"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 716 B

View File

@ -0,0 +1,14 @@
{
"parent": "item/handheld",
"textures": {
"layer0": "item/diamond_pickaxe"
},
"overrides": [
{
"predicate": {
"custom_model_data": 1
},
"model": "item/diamond_hammer"
},
]
}