Skip to content

Commit

Permalink
Merge branch 'master' into ver/1.20.2
Browse files Browse the repository at this point in the history
# Conflicts:
#	build.gradle.kts
#	src/main/java/com/github/mori01231/lifecore/LifeCore.kt
#	src/main/java/com/github/mori01231/lifecore/block/CustomBlock.kt
#	src/main/java/com/github/mori01231/lifecore/listener/CustomBlockListener.kt
  • Loading branch information
acrylic-style committed Sep 1, 2024
2 parents fd40643 + 49cc3ed commit d28a76b
Show file tree
Hide file tree
Showing 9 changed files with 140 additions and 40 deletions.
3 changes: 1 addition & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import io.papermc.paperweight.userdev.ReobfArtifactConfiguration
import org.apache.tools.ant.filters.ReplaceTokens

plugins {
Expand All @@ -11,7 +10,7 @@ plugins {
}

group = "net.azisaba"
version = "1.20.2+6.16.9"
version = "1.20.2+6.17.0"

java {
toolchain.languageVersion.set(JavaLanguageVersion.of(17))
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/com/github/mori01231/lifecore/LifeCore.kt
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,14 @@ class LifeCore : JavaPlugin() {
}
}, 200, 200)

Bukkit.getScheduler().runTaskTimer(this, Runnable {
customBlockManager.getLoadedStates().forEach { (location, state) ->
state.getBlock().tick(customBlockManager, location, state)?.let {
customBlockManager.setState(location.toBukkitLocation(), it)
}
}
}, 1, 1)

logger.info("LifeCore has been enabled.")
}

Expand Down Expand Up @@ -235,6 +243,7 @@ class LifeCore : JavaPlugin() {
runCatching { executorService.shutdownNow() }
runCatching { httpServer?.stop(1) }
runCatching { gcListener.unregister() }
runCatching { customBlockManager.saveAll() }

runCatching {
// unregister all channel handlers
Expand All @@ -256,8 +265,10 @@ class LifeCore : JavaPlugin() {
preloadClass("com.github.mori01231.lifecore.LifeCore\$onDisable\$$i", false)
}
preloadClass("com.github.mori01231.lifecore.lib.com.charleskorn.kaml.Yaml\$encodeToString\$writer\$1")
preloadClass("com.github.mori01231.lifecore.lib.com.charleskorn.kaml.YamlOutput")
preloadClass("com.github.mori01231.lifecore.lib.org.yaml.snakeyaml.Yaml")
preloadClass("com.github.mori01231.lifecore.lib.org.yaml.snakeyaml.nodes.CollectionNode")
preloadClass("com.github.mori01231.lifecore.lib.org.yaml.snakeyaml.nodes.SequenceNode")
}

private fun preloadClass(name: String, required: Boolean = true) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.github.mori01231.lifecore.block

import com.github.mori01231.lifecore.region.WorldLocation
import org.bukkit.Material
import org.bukkit.entity.Player
import org.bukkit.event.player.PlayerInteractEvent
import org.bukkit.inventory.EquipmentSlot
import org.bukkit.potion.PotionEffectType

class BeaconCustomBlock(
override val name: String,
override val lockFacing: Boolean,
override val axisShift: Int,
override val backgroundBlock: Material,
material: Material,
displayName: String? = null,
lore: List<String>? = null,
private val effect: PotionEffectType,
private val amplifier: Int,
private val destroyWithoutWrench: Boolean,
) : CustomBlock(material, displayName, lore) {
var ticks = 0

override fun tick(manager: CustomBlockManager, pos: WorldLocation, state: CustomBlockState): CustomBlockState? {
if (ticks++ % 80 == 0) {
pos.toBukkitLocation().getNearbyEntitiesByType(Player::class.java, 250.0).forEach { player ->
player.addPotionEffect(effect.createEffect(20 * 60, amplifier))
}
}
return super.tick(manager, pos, state)
}

override fun canDestroy(state: CustomBlockState, wrench: Boolean): Boolean = wrench || destroyWithoutWrench
}
16 changes: 9 additions & 7 deletions src/main/java/com/github/mori01231/lifecore/block/CustomBlock.kt
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package com.github.mori01231.lifecore.block

import com.github.mori01231.lifecore.region.WorldLocation
import com.github.mori01231.lifecore.util.AxisX
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement
import net.kyori.adventure.text.Component
import net.minecraft.nbt.CompoundTag
import org.bukkit.Material
import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftItemStack
import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack
import org.bukkit.event.Listener
import org.bukkit.event.block.BlockPlaceEvent
import org.bukkit.event.player.PlayerInteractEvent
import org.bukkit.inventory.ItemStack
import kotlin.text.contains

abstract class CustomBlock(
val material: Material,
Expand All @@ -22,7 +22,7 @@ abstract class CustomBlock(
open val lockFacing: Boolean = false
open val axisShift: Int = 0
open val backgroundBlock: Material = Material.BARRIER
var customModelData = 0
var customModelData: Int? = null

internal fun handleInteract(e: PlayerInteractEvent, state: CustomBlockState) {
if (state.blockName != name) error("Block name mismatch: state ${state.blockName} != block $name")
Expand All @@ -45,8 +45,8 @@ abstract class CustomBlock(

open fun onPlace(e: BlockPlaceEvent): CustomBlockState {
val nms = CraftItemStack.asNMSCopy(e.itemInHand)
val tagString = if (nms.hasTag() && nms.tag!!.contains("BlockState")) {
nms.tag!!.getCompound("BlockState").getString("tag")
val tagString = if (nms.hasTag() && nms.tag!!.contains("CustomBlockState")) {
nms.tag!!.getCompound("CustomBlockState").getString("tag")
} else {
""
}
Expand All @@ -70,12 +70,14 @@ abstract class CustomBlock(
lore = this@CustomBlock.lore
}
val nms = CraftItemStack.asNMSCopy(item)
nms.orCreateTag.put("BlockState", CompoundTag().apply {
nms.orCreateTag.put("CustomBlockState", CompoundTag().apply {
putString("blockName", this@CustomBlock.name)
if (state != null) {
putString("tag", Json.encodeToString(state.tag))
}
})
return CraftItemStack.asCraftMirror(nms)
}

open fun tick(manager: CustomBlockManager, pos: WorldLocation, state: CustomBlockState): CustomBlockState? = null
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.github.mori01231.lifecore.block

import com.github.mori01231.lifecore.LifeCore
import com.github.mori01231.lifecore.listener.CustomBlockListener
import com.github.mori01231.lifecore.region.WorldLocation
import com.github.mori01231.lifecore.util.AxisX
import com.github.mori01231.lifecore.util.LRUCache
import kotlinx.serialization.json.Json
Expand All @@ -17,6 +18,7 @@ import org.bukkit.entity.Entity
import org.bukkit.event.HandlerList
import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.ShapedRecipe
import org.bukkit.potion.PotionEffectType
import java.io.File
import java.util.*

Expand Down Expand Up @@ -50,6 +52,15 @@ class CustomBlockManager(val plugin: LifeCore) {

private fun getRegionPos(worldPos: Int) = worldPos shr 9

fun getLoadedStates(): Map<WorldLocation, CustomBlockState> =
region.flatMap { (world, wld) ->
wld.flatMap { (_, region) ->
region.getAllStates().map { (pos, state) ->
WorldLocation(world, pos.first, pos.second, pos.third) to state
}
}
}.toMap()

fun getState(location: Location): CustomBlockState? {
val region = loadRegion(location.world, getRegionPos(location.blockX), getRegionPos(location.blockZ))
return region.getState(location.blockX, location.blockY, location.blockZ)
Expand All @@ -66,24 +77,23 @@ class CustomBlockManager(val plugin: LifeCore) {
region.setState(location.blockX, location.blockY, location.blockZ, state)
}

private fun loadRegion(world: World, x: Int, z: Int): CustomBlockRegion {
val wld = region.getOrPut(world.name) { LRUCache(100) }
val loaded = wld.getOrPut(x to z) {
val file = File(regionDir, "${world.name}/$x.$z.json")
if (file.exists()) {
plugin.logger.info("Loading region $x, $z")
Json.decodeFromString(CustomBlockRegion.serializer(), file.readText())
} else {
plugin.logger.info("Creating region $x, $z")
CustomBlockRegion(world.name, x, z)
private fun loadRegion(world: World, x: Int, z: Int): CustomBlockRegion =
region.getOrPut(world.name) { LRUCache(100) }
.getOrPut(x to z) {
val file = File(regionDir, "${world.name}/$x.$z.json")
val loaded = if (file.exists()) {
plugin.logger.info("Loading region $x, $z")
Json.decodeFromString(CustomBlockRegion.serializer(), file.readText())
} else {
plugin.logger.info("Creating region $x, $z")
CustomBlockRegion(world.name, x, z)
}
if (loaded.dirty) {
plugin.logger.warning("Region $x, $z was not saved properly")
loaded.save()
}
loaded
}
}
if (loaded.dirty) {
plugin.logger.warning("Region $x, $z was not saved properly")
loaded.save()
}
return loaded
}

fun getWrenchItem() = ItemStack(Material.STICK).apply {
itemMeta = itemMeta?.apply {
Expand Down Expand Up @@ -117,18 +127,28 @@ class CustomBlockManager(val plugin: LifeCore) {

// add blocks
plugin.config.getMapList("custom-blocks").forEach { map ->
val type = map["type"]?.toString() ?: "command"
val blockName = map["name"]?.toString() ?: return plugin.logger.warning("name is required")
val axisShift = map["axisShift"]?.toString()?.toIntOrNull() ?: 0
val lockFacing = map["lockFacing"]?.toString()?.toBoolean() ?: false
val lockFacing = map["lockFacing"]?.toString()?.toBoolean() == true
val backgroundBlock = Material.valueOf(map["backgroundBlock"]?.toString()?.uppercase() ?: "BARRIER")
val material = Material.valueOf(map["material"]?.toString()?.uppercase() ?: return plugin.logger.warning("material is required"))
val material = Material.valueOf(map["material"]?.toString()?.uppercase() ?: return plugin.logger.warning("material is required @ $blockName"))
val displayName = map["displayName"]?.toString()?.let { ChatColor.translateAlternateColorCodes('&', it) }
val lore = map["lore"]?.toString()?.let { ChatColor.translateAlternateColorCodes('&', it) }?.split("\n")
val customModelData = map["customModelData"]?.toString()?.toIntOrNull() ?: 0
val commands = map["commands"] as List<String>?
val consoleCommands = map["consoleCommands"] as List<String>?
val destroyWithoutWrench = map["destroyWithoutWrench"]?.toString()?.toBoolean() ?: false
val block = CommandCustomBlock(blockName, lockFacing, axisShift, backgroundBlock, material, displayName, lore, commands ?: emptyList(), consoleCommands ?: emptyList(), destroyWithoutWrench)
val destroyWithoutWrench = map["destroyWithoutWrench"]?.toString()?.toBoolean() == true
val customModelData = map["customModelData"]?.toString()?.toIntOrNull()
val block = if (type == "command") {
val commands = map["commands"] as List<String>?
val consoleCommands = map["consoleCommands"] as List<String>?
CommandCustomBlock(blockName, lockFacing, axisShift, backgroundBlock, material, displayName, lore, commands ?: emptyList(), consoleCommands ?: emptyList(), destroyWithoutWrench)
} else if (type == "beacon") {
val effect = PotionEffectType.getByName(map["effect"]?.toString() ?: return plugin.logger.warning("effect is required @ $blockName"))
?: return plugin.logger.warning("Unknown effect: ${map["effect"]} @ $blockName")
val amplifier = map["amplifier"]?.toString()?.toIntOrNull() ?: 0
BeaconCustomBlock(blockName, lockFacing, axisShift, backgroundBlock, material, displayName, lore, effect, amplifier, destroyWithoutWrench)
} else {
error("Unknown block type: $type")
}
block.customModelData = customModelData
registerCustomBlock(block)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,17 @@ data class CustomBlockRegion(val world: String, val x: Int, val z: Int) {

fun unpackZ(packed: Long) = (packed and 0x1FFFFF).toInt()

fun getAllStates(): Map<Triple<Int, Int, Int>, CustomBlockState> {
return states.mapKeys {
val regionX = x * 512
val regionZ = z * 512
val x = unpackX(it.key) + regionX
val y = unpackY(it.key)
val z = unpackZ(it.key) + regionZ
Triple(x, y, z)
}
}

fun getState(x: Int, y: Int, z: Int): CustomBlockState? {
val packed = packXYZ(x and 511, y, z and 511)
return states[packed]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command
players.add(player.getUniqueId());
sender.sendMessage(ChatColor.GREEN + "投票しました。");
if (sender.hasPermission("lifecore.extend-time-immediately") || players.size() == 5) {
ScheduleRestartCommand.schedule(30);
ScheduleRestartCommand.schedule(30 * 60);
}
return true;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
package com.github.mori01231.lifecore.listener

import com.github.mori01231.lifecore.LifeCore
import net.kyori.adventure.text.Component
import org.bukkit.GameMode
import org.bukkit.Location
import org.bukkit.Material
import org.bukkit.Sound
import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftItemStack
import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack
import org.bukkit.entity.Arrow
import org.bukkit.entity.Player
import org.bukkit.event.EventHandler
import org.bukkit.event.EventPriority
import org.bukkit.event.Listener
import org.bukkit.event.block.Action
import org.bukkit.event.block.BlockBreakEvent
import org.bukkit.event.block.BlockPistonExtendEvent
import org.bukkit.event.block.BlockPistonRetractEvent
import org.bukkit.event.block.BlockPlaceEvent
import org.bukkit.event.entity.ProjectileHitEvent
import org.bukkit.event.player.PlayerInteractAtEntityEvent
import org.bukkit.event.player.PlayerInteractEntityEvent
import org.bukkit.event.player.PlayerInteractEvent
import kotlin.text.contains

class CustomBlockListener(val plugin: LifeCore) : Listener {
private var callingEvent = false

@EventHandler
fun onPlayerInteract(e: PlayerInteractEvent) {
if (e.clickedBlock == null) return
Expand All @@ -44,10 +48,10 @@ class CustomBlockListener(val plugin: LifeCore) : Listener {
if (!nms.hasTag()) {
return
}
if (!nms.tag!!.contains("BlockState")) {
if (!nms.tag!!.contains("CustomBlockState")) {
return
}
val blockState = nms.tag!!.getCompound("BlockState")
val blockState = nms.tag!!.getCompound("CustomBlockState")
val blockName = blockState.getString("blockName")
val block = plugin.customBlockManager.findBlockByName(blockName) ?: return
if (!e.player.hasPermission("lifecore.customblock.place.$blockName")) {
Expand All @@ -61,6 +65,7 @@ class CustomBlockListener(val plugin: LifeCore) : Listener {

@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
fun onBlockBreak(e: BlockBreakEvent) {
if (callingEvent) return
val state = plugin.customBlockManager.getState(e.block.location) ?: return
e.isCancelled = true
if (!e.player.hasPermission("lifecore.customblock.destroy.${state.blockName}")) {
Expand Down Expand Up @@ -113,6 +118,22 @@ class CustomBlockListener(val plugin: LifeCore) : Listener {
}
}

@EventHandler
fun onPistonExtend(e: BlockPistonExtendEvent) {
e.blocks.forEach { block ->
plugin.customBlockManager.getState(block.location) ?: return
e.isCancelled = true
}
}

@EventHandler
fun onPistonRetract(e: BlockPistonRetractEvent) {
e.blocks.forEach { block ->
plugin.customBlockManager.getState(block.location) ?: return
e.isCancelled = true
}
}

private fun destroyAt(player: Player, location: Location, callEvent: Boolean = true) {
val state = plugin.customBlockManager.getState(location) ?: return
if (callEvent) {
Expand All @@ -121,10 +142,12 @@ class CustomBlockListener(val plugin: LifeCore) : Listener {
location.block.type = Material.BARRIER
}
try {
callingEvent = true
if (!BlockBreakEvent(location.block, player).callEvent()) {
return
}
} finally {
callingEvent = false
if (wasAir) {
location.block.type = Material.AIR
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ public synchronized void triggerNow() {
}
} else if (command.startsWith("@schedulerestart ")) {
try {
int delayMinutes = Integer.parseInt(command.substring("@schedulerestart ".length()));
ScheduleRestartCommand.schedule(delayMinutes);
int delaySeconds = Integer.parseInt(command.substring("@schedulerestart ".length()));
ScheduleRestartCommand.schedule(delaySeconds);
TextComponent component = new TextComponent("再起動までの時間を延長するには、このメッセージをクリックしてください。");
component.setColor(ChatColor.AQUA);
component.setUnderlined(true);
Expand Down

0 comments on commit d28a76b

Please sign in to comment.