sync code

This commit is contained in:
TONY_All 2022-10-27 21:09:27 +08:00
parent 94295d7f44
commit b19752154e
21 changed files with 636 additions and 28 deletions

2
.gitignore vendored
View File

@ -1,4 +1,4 @@
.gradle
.idea
./build
build/

View File

@ -2,7 +2,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
kotlin("jvm") version "1.7.20"
id("io.izzel.taboolib") version "1.42"
id("io.izzel.taboolib") version "1.50"
}
group = "cc.maxmc.blastingcrisis"
@ -20,9 +20,9 @@ repositories {
dependencies {
implementation(kotlin("stdlib"))
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
implementation("ink.ptms.core:v10800:10800")
implementation("ink.ptms.core:v11200:11200")
implementation("ink.ptms:Adyeshach:1.5.12")
}
taboolib {
@ -34,10 +34,11 @@ taboolib {
}
install("common", "common-5")
install("platform-bukkit")
install("module-nms", "module-nms-util", "module-chat")
install("module-nms", "module-nms-util")
install("module-chat", "module-lang", "module-configuration")
options("skip-kotlin-relocate")
version = "6.0.9-117"
version = "6.0.10-12"
}
tasks.withType<KotlinCompile> {

View File

@ -1,11 +1,15 @@
package cc.maxmc.blastingcrisis
import cc.maxmc.blastingcrisis.command.DebugCommand
import taboolib.common.env.RuntimeDependency
import taboolib.common.platform.Plugin
@RuntimeDependency(
"org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4",
)
object BlastingCrisis: Plugin() {
override fun onEnable() {
DebugCommand.debug("debugcmd")
}
}

View File

@ -1,18 +1,149 @@
package cc.maxmc.blastingcrisis.command
import cc.maxmc.blastingcrisis.game.Game
import cc.maxmc.blastingcrisis.game.GameOreGenerator
import cc.maxmc.blastingcrisis.game.GameState
import cc.maxmc.blastingcrisis.map.GameMap
import cc.maxmc.blastingcrisis.map.MapTeam
import cc.maxmc.blastingcrisis.misc.Area
import cc.maxmc.blastingcrisis.packet.BEntityVillager
import kotlinx.coroutines.cancel
import org.bukkit.Bukkit
import org.bukkit.Location
import org.bukkit.entity.Blaze
import org.bukkit.entity.EntityType
import org.bukkit.ChatColor
import org.bukkit.entity.Player
import taboolib.common.platform.ProxyPlayer
import taboolib.common.platform.command.command
import taboolib.common.platform.function.adaptCommandSender
import taboolib.common.platform.function.getDataFolder
import taboolib.module.configuration.Configuration
import taboolib.platform.BukkitPlugin
import taboolib.platform.util.sendActionBar
import java.io.File
object DebugCommand {
lateinit var game: Game
lateinit var villager: BEntityVillager
fun debug(cmd: String) = command(cmd) {
literal("game") {
execute<Player> { sender, _, _ ->
Bukkit.broadcastMessage(sender.name)
val homeArea = Area(sender.location, sender.location.add(10.0, 10.0, 10.0))
val area1 = Area(sender.location, sender.location.add(5.0, 5.0, 5.0))
val area2 = Area(sender.location, sender.location.add(5.0, 5.0, 5.0))
val area3 = Area(sender.location, sender.location.add(5.0, 5.0, 5.0))
val team = MapTeam(
"testTeam1",
ChatColor.AQUA,
sender.location,
homeArea,
area1,
area2,
listOf(area3),
area2,
sender.eyeLocation
)
val map = GameMap(
"test", listOf(
team
), 4
)
game = Game(map)
game.join(sender)
}
literal("scoreboard") {
literal("state") {
dynamic {
execute<Player> { sender, ctx, argument ->
if (!GameState.values().map { it.name }
.contains(argument.uppercase())) return@execute sender.sendMessage("NO STATE")
val state = GameState.valueOf(argument.uppercase())
game.state = state
sender.sendMessage("changed to $argument")
}
suggestion<Player> { _, _ ->
GameState.values().map { it.name }
}
}
}
literal("timer") {
execute<Player> { sender, ctx, argument ->
game.timer.beginCountdown()
}
}
}
literal("scoreboard") {
execute { sender, context, argument ->
game.scoreboard.sendScoreboardPlayer(sender)
}
}
literal("title") {
execute<Player> { sender, _, _ ->
val adapt = adaptCommandSender(sender) as ProxyPlayer
adapt.sendTitle("Title", "sub", 20, 20, 20)
}
}
literal("actionBar") {
execute<Player> { sender, _, _ ->
sender.sendActionBar("This is an action bar msg")
}
}
literal("spawnEntity") {
execute<Player> { sender, _, _ ->
val loc = sender.location
villager = BEntityVillager(loc)
villager.addViewer(sender)
}
}
// literal("rename") {
// dynamic {
// execute<Player> { _, _, arg ->
// villager.name = arg
// }
// }
// }
//
// literal("destroy") {
// execute { sender, _, _ ->
// villager.removeViewer(sender)
// }
// }
//
// literal("hurt") {
// execute<Player> { _, _, _ ->
// villager.hurtAnimate()
// }
// }
//
// literal("die") {
// execute<Player> { _, _, _ ->
// villager.dieAnimate()
// }
// }
//
// literal("update") {
// execute<Player> { _, _, _ ->
// villager.update()
// }
// }
literal("genOre") {
execute<Player> { sender, _, _ ->
val area = Area(sender.location.block.location, sender.location.block.location.add(20.0, 20.0, 20.0))
BukkitPlugin.getInstance().saveResource("ore-generators/default.yml", false)
println("saved")
val config = Configuration.loadFromFile(File(getDataFolder(), "ore-generators/default.yml"))
val gen = GameOreGenerator(config)
val job = gen.enable(area)
Bukkit.getScheduler().runTaskLater(BukkitPlugin.getInstance(), {
job.cancel("Stop by hand")
} ,60 * 20)
}
}
}
}

View File

@ -0,0 +1,26 @@
package cc.maxmc.blastingcrisis.debug
import net.minecraft.server.v1_8_R3.PacketPlayOutSpawnEntityLiving
import org.bukkit.Bukkit
import taboolib.common.platform.event.SubscribeEvent
import taboolib.library.reflex.Reflex.Companion.getProperty
import taboolib.module.nms.PacketSendEvent
import taboolib.platform.BukkitPlugin
import taboolib.platform.util.broadcast
object DListener {
@SubscribeEvent
fun onPacket(it: PacketSendEvent) {
Bukkit.getScheduler().runTask(BukkitPlugin.getInstance()) {
if (it.player.name != "TONY_All") return@runTask
// val source = it.packet.source
if (!it.packet.name.lowercase().contains("entity")) return@runTask
// println(it.packet.name)
if( it.packet.source !is PacketPlayOutSpawnEntityLiving ) {
return@runTask
}
// it.packet.source.getProperty<Int>("a")!!.broadcast()
// it.packet.source.getProperty<Int>("b")!!.broadcast()
}
}
}

View File

@ -1,14 +1,65 @@
package cc.maxmc.blastingcrisis.game
import cc.maxmc.blastingcrisis.map.GameMap
import cc.maxmc.blastingcrisis.misc.debug
import org.bukkit.entity.Player
import taboolib.platform.util.sendLang
class Game(val map: GameMap, val teams: List<GameTeam>, val state: GameState, scoreboard: GameScoreboard) {
val players: List<Player>
get() {
val all = ArrayList<Player>()
teams.map { it.players }.forEach { all.addAll(it) }
return all
class Game(
val map: GameMap,
) {
val teams = map.teams.map { GameTeam(this, it) }
val scoreboard: GameScoreboard = GameScoreboard(this)
val timer: GameTimer = GameTimer(this)
val players = ArrayList<Player>()
var state: GameState = GameState.WAITING
private fun autoJoinTeam() {
players.filter { it.team == null }
.shuffled()
.forEach {
teams.minBy { team -> team.players.size }.join(it)
}
}
fun join(player: Player) {
players += player
broadcast {
it.sendLang("game_join", player.name)
}
scoreboard.sendScoreboard()
}
fun leave(player: Player) {
if (state == GameState.WAITING || state == GameState.COUNTING_DOWN) {
players.remove(player)
checkStart()
}
val team =
player.team ?: throw IllegalStateException("Player ${player.name} has no team, which shouldn't happen.")
team.checkTeamAlive()
team.players.remove(player)
checkEnd()
}
fun start() {
debug("game ${map.name} started.")
}
private fun checkStart() {
if (players.size > map.maxPlayer * 0.75) {
state = GameState.COUNTING_DOWN
timer.beginCountdown()
}
}
private fun checkEnd() {
}
fun broadcast(action: (Player) -> Unit) {
players.forEach(action)
}
}

View File

@ -0,0 +1,51 @@
package cc.maxmc.blastingcrisis.game
import cc.maxmc.blastingcrisis.misc.Area
import cc.maxmc.blastingcrisis.misc.WeightRandom
import cc.maxmc.blastingcrisis.misc.debug
import cc.maxmc.blastingcrisis.misc.pluginScore
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.bukkit.Bukkit
import org.bukkit.Material
import taboolib.common.util.random
import taboolib.library.xseries.XMaterial
import taboolib.module.configuration.Configuration
import taboolib.platform.BukkitPlugin
class GameOreGenerator(config: Configuration) {
private val rate: Int = config.getInt("rate")
private val ores: MutableMap<XMaterial, Int> = HashMap<XMaterial, Int>().also {
config.getConfigurationSection("ores")!!.getKeys(false).forEach { key ->
it += XMaterial.valueOf(key) to (config["ores.$key"]!! as Int)
}
}
private val random = WeightRandom(ores.map { it.key to it.value })
fun enable(area: Area): Job {
return pluginScore.launch {
while (true) {
generate(area)
delay(rate * 50L)
}
}
}
private tailrec fun generate(area: Area) {
val x = random(area.locMin.blockX, area.locTop.blockX)
val y = random(area.locMin.blockY, area.locTop.blockY)
val z = random(area.locMin.blockZ, area.locTop.blockZ)
val block = area.locMin.world.getBlockAt(x, y, z)
debug("trying generate at ($x, $y, $z) - ${block.type}")
if (block.type == Material.AIR) {
Bukkit.getScheduler().runTask(BukkitPlugin.getInstance()) {
block.type = random.random().parseMaterial()
debug("generating ${block.type} at ($x, $y, $z)")
}
} else {
return generate(area)
}
}
}

View File

@ -1,2 +1,9 @@
package cc.maxmc.blastingcrisis.game
import cc.maxmc.blastingcrisis.misc.GameManager
import org.bukkit.entity.Player
val Player.team: GameTeam?
get() = GameManager.currentGame.teams.findLast {
it.players.contains(this)
}

View File

@ -1,28 +1,27 @@
package cc.maxmc.blastingcrisis.game
import cc.maxmc.blastingcrisis.game.GameState.*
import cc.maxmc.blastingcrisis.misc.debug
import org.bukkit.entity.Player
import taboolib.module.nms.sendScoreboard
import taboolib.platform.util.asLangText
class GameScoreboard(val game: Game) {
fun sendScoreboard() {
game.teams.forEach { g ->
g.players.forEach {
game.broadcast {
sendScoreboardPlayer(it)
}
}
}
fun sendScoreboardPlayer(player: Player) {
debug("sending Scoreboard to ${player.name}")
val scoreboardText = when (game.state) {
WAITING -> {
player.asLangText("scoreboard_waiting", game.players.size, game.map.maxPlayer)
}
COUNTING_DOWN -> {
player.asLangText("scoreboard_counting_down", game.players.size, game.map.maxPlayer)
player.asLangText("scoreboard_counting_down", game.players.size, game.map.maxPlayer, game.timer.current)
}
START -> TODO()
WALL_FALL -> TODO()

View File

@ -3,6 +3,16 @@ package cc.maxmc.blastingcrisis.game
import cc.maxmc.blastingcrisis.map.MapTeam
import org.bukkit.entity.Player
class GameTeam(val team: MapTeam, val players: List<Player>) {
class GameTeam(val game: Game, val team: MapTeam) {
val players = ArrayList<Player>()
fun join(player: Player) {
players += player
}
fun checkTeamAlive() {
}
}

View File

@ -1,10 +1,49 @@
package cc.maxmc.blastingcrisis.game
class GameTimer(val game: Game) {
val time2Start = 2
import cc.maxmc.blastingcrisis.misc.pluginScore
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import taboolib.common.platform.function.adaptCommandSender
import taboolib.module.lang.getLocaleFile
import taboolib.platform.util.asLangTextOrNull
import taboolib.platform.util.sendLang
class GameTimer(private val game: Game) {
val timeToStart = 15
lateinit var countdownJob: Job
var current = timeToStart
fun beginCountdown() {
countdownJob = pluginScore.launch {
while (current > 0) {
if (!isActive) {
return@launch
}
countdown()
current--
delay(1000)
}
game.start()
}
}
private fun countdown() {
game.scoreboard.sendScoreboard()
game.broadcast {
val locale = adaptCommandSender(it).getLocaleFile()!!
if (locale.nodes.containsKey("game_countdown_${current}")) {
it.sendLang("game_countdown_${current}")
}
}
}
fun resetCountdown() {
game.broadcast {
it.sendLang("time_reset")
}
current = timeToStart
}
fun startTimer() {

View File

@ -0,0 +1,9 @@
package cc.maxmc.blastingcrisis.misc
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
val pluginScore = CoroutineScope(SupervisorJob() + CoroutineExceptionHandler { job, exception ->
IllegalStateException("在执行 Job $job 时出线异常", exception).printStackTrace()
})

View File

@ -0,0 +1,7 @@
package cc.maxmc.blastingcrisis.misc
import taboolib.common.platform.function.console
fun debug(message: String) {
console().sendMessage("§1DEBUG| §7$message")
}

View File

@ -1,5 +1,7 @@
package cc.maxmc.blastingcrisis.misc
object GameManager {
import cc.maxmc.blastingcrisis.game.Game
object GameManager {
lateinit var currentGame : Game
}

View File

@ -0,0 +1,24 @@
package cc.maxmc.blastingcrisis.misc
import com.google.common.base.Preconditions
import java.util.*
class WeightRandom<K, V : Number>(list: List<Pair<K, V>>) {
private val weightMap = TreeMap<Double, K>()
init {
Preconditions.checkNotNull(list, "list can NOT be null!")
for (pair in list) {
Preconditions.checkArgument(pair.second.toDouble() > 0, "非法权重值pair=$pair")
val lastWeight: Double = if (weightMap.size == 0) 0.0 else weightMap.lastKey().toDouble() //统一转为double
weightMap[pair.second.toDouble() + lastWeight] = pair.first //权重累加
}
}
fun random(): K {
val randomWeight = weightMap.lastKey() * Math.random()
val tailMap: SortedMap<Double, K> = weightMap.tailMap(randomWeight, false)
return weightMap[tailMap.firstKey()]!!
}
}

View File

@ -0,0 +1,101 @@
package cc.maxmc.blastingcrisis.packet
import gnu.trove.TDecorators
import gnu.trove.map.hash.TIntObjectHashMap
import net.minecraft.server.v1_8_R3.*
import org.bukkit.Location
import org.bukkit.entity.Player
import taboolib.common.util.random
import taboolib.library.reflex.Reflex.Companion.getProperty
import taboolib.library.reflex.Reflex.Companion.setProperty
import taboolib.library.reflex.Reflex.Companion.unsafeInstance
import taboolib.module.nms.sendPacket
import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.locks.ReentrantReadWriteLock
abstract class BEntity(var loc: Location, val entityType: Int) {
private val dataWatcher = (DataWatcher::class.java.unsafeInstance() as DataWatcher).apply {
setProperty("b", true)
setProperty("dataValues", TIntObjectHashMap<Any?>(10, 0.5f, -1))
setProperty("d", TDecorators.wrap(getProperty<TIntObjectHashMap<Any>>("dataValues")!!))
setProperty("f", ReentrantReadWriteLock())
}.apply {
/*
0x01 On Fire
0x02 Crouched
0x08 Sprinting
0x10 Eating/Drinking/Blocking
0x20 Invisible
*/
a(0, 0.toByte())
a(1, 300.toShort()) // Air
a(2, "") // Name Tag
a(3, 0.toByte()) // Always Show Name Tag
a(4, 0.toByte()) // Silent
initEntity()
}
val entityID: Int = getEID()
val viewers = ArrayList<Player>()
abstract fun DataWatcher.initEntity()
open fun spawnForPlayer(player: Player) {
val packet = PacketPlayOutSpawnEntityLiving()
packet.apply {
setProperty("a", entityID)
setProperty("b", entityType)
setProperty("c", MathHelper.floor(loc.x * 32.0))
setProperty("d", MathHelper.floor(loc.y * 32.0))
setProperty("e", MathHelper.floor(loc.z * 32.0))
setProperty("f", 0.toShort())
setProperty("g", 0.toShort())
setProperty("h", 0.toShort())
setProperty("i", (loc.yaw * 256.0f / 360.0f).toInt().toByte())
setProperty("j", (loc.pitch * 256.0f / 360.0f).toInt().toByte())
setProperty("k", (loc.yaw * 256.0f / 360.0f).toInt().toByte())
}
packet.setProperty("l", dataWatcher)
viewers.forEach { it.sendPacket(packet) }
}
open fun addViewer(viewer: Player) {
viewers.add(viewer)
spawnForPlayer(viewer)
}
open fun removeViewer(viewer: Player) {
viewer.sendPacket(PacketPlayOutEntityDestroy(entityID))
viewers.remove(viewer)
}
fun update() {
val packet = PacketPlayOutEntityMetadata(entityID, dataWatcher, true)
viewers.forEach { it.sendPacket(packet) }
}
fun updateData(toDo: DataWatcher.() -> Unit) {
toDo(dataWatcher)
update()
}
fun hurtAnimate() {
PacketPlayOutEntityStatus().apply {
setProperty("a", entityID)
setProperty("b", 2.toByte())
}.let { viewers.forEach { p -> p.sendPacket(it) } }
}
fun dieAnimate() {
PacketPlayOutEntityStatus().apply {
setProperty("a", entityID)
setProperty("b", 3.toByte())
}.let { viewers.forEach { p -> p.sendPacket(it) } }
}
companion object {
private val incrementEntityID = AtomicInteger(197827 + random(0, 549))
fun getEID() = incrementEntityID.addAndGet(1)
}
}

View File

@ -0,0 +1,46 @@
package cc.maxmc.blastingcrisis.packet
import net.minecraft.server.v1_8_R3.DataWatcher
import net.minecraft.server.v1_8_R3.MathHelper
import net.minecraft.server.v1_8_R3.PacketPlayOutSpawnEntity
import org.bukkit.Location
import org.bukkit.entity.Player
import taboolib.library.reflex.Reflex.Companion.setProperty
import taboolib.module.nms.sendPacket
import kotlin.experimental.or
class BEntityNameTag(loc: Location) : BEntity(loc, 78) {
var name: String = ""
set(name) {
updateData {
getWatchableObject(2).a(name)
}
}
override fun DataWatcher.initEntity() {
getWatchableObject(0).a(getByte(0) or 0x20)
getWatchableObject(2).a("")
getWatchableObject(3).a(1.toByte())
}
override fun spawnForPlayer(player: Player) {
val packet = PacketPlayOutSpawnEntity()
packet.apply {
setProperty("a", entityID)
setProperty("b", MathHelper.floor(loc.x * 32.0))
setProperty("c", MathHelper.floor(loc.y * 32.0))
setProperty("d", MathHelper.floor(loc.z * 32.0))
setProperty("e", 0)
setProperty("f", 0)
setProperty("g", 0)
setProperty("h", 0)
setProperty("i", 0)
setProperty("j", entityType)
setProperty("k", 0)
}
player.sendPacket(packet)
update()
}
}

View File

@ -0,0 +1,49 @@
package cc.maxmc.blastingcrisis.packet
import net.minecraft.server.v1_8_R3.DataWatcher
import net.minecraft.server.v1_8_R3.PacketPlayOutEntityStatus
import org.bukkit.Location
import org.bukkit.entity.Player
import taboolib.library.reflex.Reflex.Companion.setProperty
class BEntityVillager(loc: Location) : BEntity(loc, 120) {
var name: String
get() = nameTag.name
set(name) {
nameTag.name = name
}
private val nameTag = BEntityNameTag(loc)
override fun addViewer(viewer: Player) {
super.addViewer(viewer)
nameTag.addViewer(viewer)
}
override fun spawnForPlayer(player: Player) {
super.spawnForPlayer(player)
nameTag.spawnForPlayer(player)
}
override fun removeViewer(viewer: Player) {
super.removeViewer(viewer)
nameTag.removeViewer(viewer)
}
override fun DataWatcher.initEntity() {
a(6, 1.0f) // Health
a(7, 0) // Potion Effect Color
a(8, 0.toByte()) // Is Potion Effect Ambient
a(9, 0.toByte()) // Number of Arrows in Entity
a(12, 1.toByte()) // Age (0 - child, 1 - adult)
a(15, 0.toByte()) // AI
/*
0 Farmer
1 Librarian
2 Priest
3 Blacksmith
4 Butcher
*/
a(16, 0) // Villager Type
}
}

View File

@ -0,0 +1,8 @@
package cc.maxmc.blastingcrisis.packet
import net.minecraft.server.v1_8_R3.DataWatcher
import taboolib.library.reflex.Reflex.Companion.invokeMethod
import java.lang.IllegalStateException
fun DataWatcher.getWatchableObject(id: Int): DataWatcher.WatchableObject = this.invokeMethod<DataWatcher.WatchableObject>("j", id) ?: throw IllegalStateException("Object not exists.")

View File

@ -5,4 +5,39 @@ scoreboard_waiting: |-
§a| §7等待中...
§aplay.maxmc.cc
§6Play.MaxMC.cc
scoreboard_counting_down: |-
§6Blasting§7Crisis
§a| §7当前玩家: §a{0}/{1}
§a| §7游戏还有 §a{2} §7秒开始.
§6Play.MaxMC.cc
game_countdown_15:
- "§e| §7游戏还有 §e15 §7秒开始."
game_countdown_10:
- type: title
title: "§c10"
- "§e| §7游戏还有 §e10 §7秒开始."
game_countdown_5:
- type: title
title: "§c5"
- "§e| §7游戏还有 §e5 §7秒开始."
game_countdown_3:
- type: title
title: "§c3"
- "§e| §7游戏还有 §e3 §7秒开始."
game_countdown_2:
- type: title
title: "§e2"
- "§e| §7游戏还有 §e2 §7秒开始."
game_countdown_1:
- type: title
title: "§a1"
- "§e| §7游戏还有 §e1 §7秒开始."
game_countdown_reset:
- type: title
text: "§c人数不足"
- "§c| §7由于人数不足, 无法开始游戏."
game_join: "§a| §7玩家 §a{0} §7加入了游戏"

View File

@ -0,0 +1,8 @@
rate: 20 # tick per generate
ores:
# ORE: weight
COAL_ORE: 50
IRON_ORE: 25
REDSTONE_ORE: 15
DIAMOND_ORE: 10
EMERALD_ORE: 5