place building blocks preview test

This commit is contained in:
ruby
2025-01-02 01:27:05 +13:00
parent c061cff342
commit 3e8731eb1c
6 changed files with 169 additions and 127 deletions

View File

@ -18,12 +18,18 @@ repositories {
maven("https://repo.dmulloy2.net/repository/public/") {
name = "dmulloy2-repo"
}
maven("https://maven.enginehub.org/repo/") {
name = "enginehub"
}
}
dependencies {
compileOnly("io.papermc.paper:paper-api:1.21.3-R0.1-SNAPSHOT")
compileOnly("com.comphenix.protocol:ProtocolLib:5.3.0")
compileOnly("com.fastasyncworldedit:FastAsyncWorldEdit-Bukkit")
// implementation(files("libs/ProtocolLib.jar"))
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation(platform("com.intellectualsites.bom:bom-newest:1.51")) // Ref: https://github.com/IntellectualSites/bom
// implementation("de.nilsdruyen.gradle-ftp-upload-plugin:de.nilsdruyen.gradle-ftp-upload-plugin.gradle.plugin:0.5.0")
}

View File

@ -5,12 +5,20 @@ import com.pobnellion.aoe.ui.PlaceHint
import com.pobnellion.aoe.ui.PlaceItem
import org.bukkit.Bukkit
import org.bukkit.plugin.java.JavaPlugin
import java.io.File
class Aoe : JavaPlugin() {
companion object {
private var instance: Aoe? = null
fun getSchematicsFolder(): File = File(instance!!.dataFolder, "schematics")
}
override fun onEnable() {
instance = this
// Events
Bukkit.getPluginManager().registerEvents(PlaceHint(), this)
Bukkit.getPluginManager().registerEvents(PlaceHint, this)
Bukkit.getPluginManager().registerEvents(PlaceItem(), this)
// Commands

View File

@ -8,7 +8,7 @@ import org.bukkit.entity.Player
class Blacksmith: Building {
override fun showPlaceHint(player: Player) {
PlaceHint.add(player, 4.0f, 3.0f, 4.0f, ::place, PlaceHintValidators::allReplaceable)
PlaceHint.add(player, "house2", ::place, PlaceHintValidators::allReplaceable)
}
fun place(location: Location, sizeX: Float, sizeY: Float, sizeZ: Float) {

View File

@ -8,7 +8,7 @@ import org.bukkit.entity.Player
class House: Building {
override fun showPlaceHint(player: Player) {
PlaceHint.add(player, 3.0f, 3.0f, 3.0f, ::place, PlaceHintValidators::allAir)
PlaceHint.add(player, "house1", ::place, PlaceHintValidators::allReplaceable)
}
fun place(location: Location, sizeX: Float, sizeY: Float, sizeZ: Float) {

View File

@ -5,7 +5,10 @@ import com.comphenix.protocol.ProtocolLibrary
import com.comphenix.protocol.events.PacketContainer
import com.comphenix.protocol.wrappers.*
import com.comphenix.protocol.wrappers.WrappedDataWatcher.Registry
import it.unimi.dsi.fastutil.ints.IntList
import com.pobnellion.aoe.Aoe
import com.sk89q.worldedit.bukkit.BukkitAdapter
import com.sk89q.worldedit.extent.clipboard.Clipboard
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats
import org.bukkit.Bukkit
import org.bukkit.Location
import org.bukkit.Material
@ -16,18 +19,17 @@ import org.bukkit.event.Listener
import org.bukkit.event.block.Action
import org.bukkit.event.player.PlayerInteractEvent
import org.bukkit.event.player.PlayerMoveEvent
import org.bukkit.potion.PotionEffectType
import org.joml.Math
import org.joml.Vector3f
import java.io.File
import java.io.FileInputStream
import java.util.*
import kotlin.math.floor
import kotlin.math.min
class PlaceHint : Listener {
companion object {
class PlaceHint {
companion object : Listener {
private const val MAX_TARGET_DISTANCE = 64
private val playerHints = HashMap<UUID, HintMarker>();
private val playerHints = HashMap<UUID, PlaceHint>();
fun add(
player: Player,
@ -36,12 +38,25 @@ class PlaceHint : Listener {
sizeZ: Float,
onConfirm: (Location, Float, Float, Float) -> Unit,
validate: (Location, Float, Float, Float) -> Boolean,
materialValid: Material = Material.LIME_STAINED_GLASS,
materialInvalid: Material = Material.RED_STAINED_GLASS,
) {
remove(player)
createOutlineTeam(player)
playerHints[player.uniqueId] = HintMarker(player, sizeX, sizeY, sizeZ, onConfirm, validate, materialValid, materialInvalid)
playerHints[player.uniqueId] = PlaceHint(player, sizeX, sizeY, sizeZ, onConfirm, validate)
val block = player.getTargetBlockExact(MAX_TARGET_DISTANCE);
playerHints[player.uniqueId]!!.moveTo(block?.location)
}
fun add(
player: Player,
schematicName: String,
onConfirm: (Location, Float, Float, Float) -> Unit,
validate: (Location, Float, Float, Float) -> Boolean,
) {
remove(player)
createOutlineTeam(player)
playerHints[player.uniqueId] = PlaceHint(player, schematicName, onConfirm, validate)
val block = player.getTargetBlockExact(MAX_TARGET_DISTANCE);
playerHints[player.uniqueId]!!.moveTo(block?.location)
}
@ -63,11 +78,10 @@ class PlaceHint : Listener {
val packet = PacketContainer(PacketType.Play.Server.SCOREBOARD_TEAM)
packet.strings.write(0, "placeValid")
packet.integers.write(0, 0)
packet.optionalTeamParameters.write(0, Optional.of(Utils.teamParams))
packet.optionalTeamParameters.write(0, Optional.of(teamParamsTemplate))
ProtocolLibrary.getProtocolManager().sendServerPacket(player, packet)
}
}
@EventHandler
fun onPlayerLook(event: PlayerMoveEvent) {
@ -90,80 +104,103 @@ class PlaceHint : Listener {
marker.onConfirm(marker.currentLocation!!, marker.sizeX, marker.sizeY, marker.sizeZ)
remove(event.player)
}
private val teamParamsTemplate: WrappedTeamParameters = WrappedTeamParameters.newBuilder()
.displayName(WrappedChatComponent.fromText(""))
.collisionRule("never")
.nametagVisibility("never")
.options(0)
.color(EnumWrappers.ChatFormatting.RED)
.prefix(WrappedChatComponent.fromText(""))
.suffix(WrappedChatComponent.fromText(""))
.build()
}
private class HintMarker(
val player: Player,
val sizeX: Float,
val sizeY: Float,
val sizeZ: Float,
val onConfirm: (Location, Float, Float, Float) -> Unit,
val validate: (Location, Float, Float, Float) -> Boolean,
val materialValid: Material,
val materialInvalid: Material,
) {
private val markerUuid = UUID.randomUUID()
private val outlineUuid = UUID.randomUUID()
private val markerId = (Math.random() * Int.MAX_VALUE).toInt()
private val outlineId = markerId + 1
private val player: Player
private val sizeX: Float
private val sizeY: Float
private val sizeZ: Float
val onConfirm: (Location, Float, Float, Float) -> Unit
val validate: (Location, Float, Float, Float) -> Boolean
private val schematic: Clipboard?
private var marker: Pair<Int, UUID> = Pair((Math.random() * Int.MAX_VALUE).toInt(), UUID.randomUUID())
private var base: Pair<Int, UUID> = Pair((Math.random() * Int.MAX_VALUE).toInt(), UUID.randomUUID())
private val displayIds: MutableList<Pair<Int, UUID>> = mutableListOf()
var currentLocation: Location? = null
var isValid = false
constructor(player: Player, sizeX: Float, sizeY: Float, sizeZ: Float,
onConfirm: (Location, Float, Float, Float) -> Unit,
validate: (Location, Float, Float, Float) -> Boolean) {
this.player = player
this.sizeX = sizeX
this.sizeY = sizeY
this.sizeZ = sizeZ
this.onConfirm = onConfirm
this.validate = validate
this.schematic = null
}
constructor(player: Player, schematicName: String,
onConfirm: (Location, Float, Float, Float) -> Unit,
validate: (Location, Float, Float, Float) -> Boolean) {
this.player = player
this.onConfirm = onConfirm
this.validate = validate
val file = File(Aoe.getSchematicsFolder(),
if (schematicName.endsWith(".schem")) schematicName else "$schematicName.schem")
val format = ClipboardFormats.findByFile(file)
val reader = format?.getReader(FileInputStream(file))
val clipboard = reader?.read()
this.schematic = clipboard
this.sizeX = clipboard!!.dimensions.x().toFloat()
this.sizeY = clipboard.dimensions.y().toFloat()
this.sizeZ = clipboard.dimensions.z().toFloat()
}
private fun show(initialLocation: Location) {
initialLocation.add(-floor(sizeX / 2.0), 1.0, -floor(sizeZ / 2.0))
spawnBlockDisplay(markerId, markerUuid, initialLocation, Material.GRAY_STAINED_GLASS, Vector3f(sizeX, 0.2f, sizeZ), 0b01000000)
// spawnBlockDisplay(outlineId, outlineUuid, initialLocation, Material.STONE, Vector3f(sizeX, sizeY, sizeZ), 0b00100000)
val spawnSlimePacket = PacketContainer(PacketType.Play.Server.SPAWN_ENTITY)
spawnSlimePacket.integers.write(0, outlineId)
spawnSlimePacket.uuiDs.write(0, outlineUuid)
spawnSlimePacket.entityTypeModifier.write(0, EntityType.SLIME)
spawnSlimePacket.doubles
val spawnMarkerPacket = PacketContainer(PacketType.Play.Server.SPAWN_ENTITY)
spawnMarkerPacket.integers.write(0, marker.first)
spawnMarkerPacket.uuiDs.write(0, marker.second)
spawnMarkerPacket.entityTypeModifier.write(0, EntityType.BLOCK_DISPLAY)
spawnMarkerPacket.doubles
.write(0, initialLocation.x)
.write(1, initialLocation.y)
.write(2, initialLocation.z)
val dataPacket = PacketContainer(PacketType.Play.Server.ENTITY_METADATA)
dataPacket.integers.write(0, outlineId)
base = spawnBlockDisplay(initialLocation, Material.GRAY_STAINED_GLASS, scale = Vector3f(sizeX + 0.05f, 0.2f, sizeZ + 0.05f))
// TODO: 1 - display riding slime for nicer movement
// TODO: 2 - slime riding display for nicer movement
// TODO: 3 - resource pack for outline with really low opacity
schematic?.iterator()?.forEach {
val material = BukkitAdapter.adapt(schematic.getBlock(it)).material
val watcher = WrappedDataWatcher()
watcher.setObject(0, Registry.get(Byte::class.javaObjectType), 0b01100000.toByte())
// watcher.setObject(5, Registry.get(Boolean::class.javaObjectType), true)
watcher.setObject(16, Registry.get(Int::class.javaObjectType), min(sizeX, min(sizeY, sizeZ)).toInt())
val x = (it.x() - schematic.minimumPoint.x()).toFloat()
val y = (it.y() - schematic.minimumPoint.y()).toFloat()
val z = (it.z() - schematic.minimumPoint.z()).toFloat()
dataPacket.dataValueCollectionModifier.write(0, watcher.watchableObjects
.map { WrappedDataValue(it.watcherObject.index, it.watcherObject.serializer, it.rawValue) })
ProtocolLibrary.getProtocolManager().sendServerPacket(player, spawnSlimePacket)
ProtocolLibrary.getProtocolManager().sendServerPacket(player, dataPacket)
val ridingPacket = PacketContainer(PacketType.Play.Server.MOUNT)
ridingPacket.integers.write(0, outlineId)
ridingPacket.integerArrays.write(0, intArrayOf(markerId))
if (!material.isAir && !material.isEmpty)
displayIds.add(spawnBlockDisplay(initialLocation, material, offset = Vector3f(x, y, z), options = 0b01000000))
}
val outlineTeamPacket = PacketContainer(PacketType.Play.Server.SCOREBOARD_TEAM)
outlineTeamPacket.strings.write(0, "placeValid")
outlineTeamPacket.integers.write(0, 3)
outlineTeamPacket.getSpecificModifier(Collection::class.java).write(0, listOf(outlineUuid.toString()))
outlineTeamPacket.getSpecificModifier(Collection::class.java).write(0, displayIds.map { it.second.toString() })
// val outlineInvisiblePacket = PacketContainer(PacketType.Play.Server.ENTITY_EFFECT)
// outlineInvisiblePacket.integers
// .write(0, outlineId)
// .write(1, 1)
// .write(2, -1)
//
// outlineInvisiblePacket.effectTypes.write(0, PotionEffectType.INVISIBILITY)
// outlineInvisiblePacket.bytes.write(0, 0b0000.toByte())
val ids = displayIds.map { it.first }.toMutableList()
ids.add(base.first)
val passengerPacket = PacketContainer(PacketType.Play.Server.MOUNT)
passengerPacket.integers.write(0, marker.first)
passengerPacket.integerArrays.write(0, ids.toIntArray())
ProtocolLibrary.getProtocolManager().sendServerPacket(player, spawnMarkerPacket)
ProtocolLibrary.getProtocolManager().sendServerPacket(player, outlineTeamPacket)
ProtocolLibrary.getProtocolManager().sendServerPacket(player, ridingPacket)
// ProtocolLibrary.getProtocolManager().sendServerPacket(player, outlineInvisiblePacket)
ProtocolLibrary.getProtocolManager().sendServerPacket(player, passengerPacket)
isValid = validate(initialLocation, sizeX, sizeY, sizeZ)
updateColour()
@ -173,7 +210,10 @@ private class HintMarker(
private fun hide() {
val packet = PacketContainer(PacketType.Play.Server.ENTITY_DESTROY)
packet.intLists.write(0, IntList.of(markerId, outlineId))
val ids = displayIds.map { it.first }.toMutableList()
ids.add(marker.first)
ids.add(base.first)
packet.intLists.write(0, ids)
ProtocolLibrary.getProtocolManager().sendServerPacket(player, packet)
@ -194,28 +234,17 @@ private class HintMarker(
newLocation.add(-floor(sizeX / 2.0), 1.0, -floor(sizeZ / 2.0))
// val markerPacket = PacketContainer(PacketType.Play.Server.ENTITY_TELEPORT)
// markerPacket.integers.write(0, markerId)
//
// markerPacket.doubles
// .write(0, newLocation.x)
// .write(1, newLocation.y)
// .write(2, newLocation.z)
//
// markerPacket.booleans.write(0, true)
//
// ProtocolLibrary.getProtocolManager().sendServerPacket(player, markerPacket)
val basePacket = PacketContainer(PacketType.Play.Server.ENTITY_TELEPORT)
basePacket.integers.write(0, marker.first)
val outlinePacket = PacketContainer(PacketType.Play.Server.ENTITY_TELEPORT)
outlinePacket.integers.write(0, outlineId)
outlinePacket.doubles
basePacket.doubles
.write(0, newLocation.x)
.write(1, newLocation.y)
.write(2, newLocation.z)
outlinePacket.booleans.write(0, true)
ProtocolLibrary.getProtocolManager().sendServerPacket(player, outlinePacket)
basePacket.booleans.write(0, true)
ProtocolLibrary.getProtocolManager().sendServerPacket(player, basePacket)
currentLocation = newLocation
@ -230,7 +259,16 @@ private class HintMarker(
// region helper functions
private fun spawnBlockDisplay(id: Int, uuid: UUID, location: Location, material: Material, scale: Vector3f, options: Byte) {
private fun spawnBlockDisplay(
location: Location,
material: Material,
scale: Vector3f = Vector3f(1f, 1f, 1f),
offset: Vector3f = Vector3f(0f, 0f, 0f),
options: Byte = 0b00000000
): Pair<Int, UUID> {
val id = (Math.random() * Int.MAX_VALUE).toInt()
val uuid = UUID.randomUUID()
val spawnPacket = PacketContainer(PacketType.Play.Server.SPAWN_ENTITY)
spawnPacket.integers.write(0, id)
spawnPacket.uuiDs.write(0, uuid)
@ -250,6 +288,7 @@ private class HintMarker(
val watcher = WrappedDataWatcher()
watcher.setObject(0, Registry.get(Byte::class.javaObjectType), options)
watcher.setObject(11, Registry.get(Vector3f::class.javaObjectType), offset)
watcher.setObject(12, Registry.get(Vector3f::class.javaObjectType), scale)
watcher.setObject(23, Registry.getBlockDataSerializer(false), blockData)
@ -258,20 +297,22 @@ private class HintMarker(
ProtocolLibrary.getProtocolManager().sendServerPacket(player, spawnPacket)
ProtocolLibrary.getProtocolManager().sendServerPacket(player, dataPacket)
return Pair<Int, UUID>(id, uuid)
}
private fun updateColour() {
// marker
// base
val dataPacket = PacketContainer(PacketType.Play.Server.ENTITY_METADATA)
dataPacket.integers.write(0,markerId);
dataPacket.integers.write(0, base.first);
val material = if (isValid) materialValid else materialInvalid
val material = if (isValid) Material.LIME_STAINED_GLASS else Material.RED_STAINED_GLASS
val blockData = BukkitConverters
.getWrappedBlockDataConverter()
.getGeneric(WrappedBlockData.createData(material))
val metadata = listOf(
WrappedDataValue(23, WrappedDataWatcher.Registry.getBlockDataSerializer(false), blockData)
WrappedDataValue(23, Registry.getBlockDataSerializer(false), blockData)
)
dataPacket.dataValueCollectionModifier.write(0, metadata)
@ -281,7 +322,7 @@ private class HintMarker(
val outlineTeamPacket = PacketContainer(PacketType.Play.Server.SCOREBOARD_TEAM)
outlineTeamPacket.strings.write(0, "placeValid")
outlineTeamPacket.integers.write(0, 2)
val params = WrappedTeamParameters.newBuilder(Utils.teamParams)
val params = WrappedTeamParameters.newBuilder(teamParamsTemplate)
.color(if (isValid) EnumWrappers.ChatFormatting.GREEN else EnumWrappers.ChatFormatting.RED)
.build()
@ -291,17 +332,3 @@ private class HintMarker(
// endregion
}
private class Utils {
companion object {
val teamParams: WrappedTeamParameters = WrappedTeamParameters.newBuilder()
.displayName(WrappedChatComponent.fromText(""))
.collisionRule("never")
.nametagVisibility("never")
.options(0)
.color(EnumWrappers.ChatFormatting.RED)
.prefix(WrappedChatComponent.fromText(""))
.suffix(WrappedChatComponent.fromText(""))
.build()
}
}

View File

@ -4,6 +4,7 @@ main: com.pobnellion.aoe.Aoe
api-version: '1.21'
depend:
- ProtocolLib
- WorldEdit
commands:
debug:
description: Debug command