sync code

This commit is contained in:
TONY_All 2022-10-28 16:30:46 +08:00
parent b19752154e
commit 3145fc1586
24 changed files with 278 additions and 80 deletions

View File

@ -22,7 +22,7 @@ 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.core:v11200:11200")
}
taboolib {

View File

@ -1,6 +1,8 @@
package cc.maxmc.blastingcrisis
import cc.maxmc.blastingcrisis.command.DebugCommand
import cc.maxmc.blastingcrisis.misc.pluginScope
import kotlinx.coroutines.cancel
import taboolib.common.env.RuntimeDependency
import taboolib.common.platform.Plugin
@ -12,4 +14,7 @@ object BlastingCrisis: Plugin() {
DebugCommand.debug("debugcmd")
}
override fun onDisable() {
pluginScope.cancel()
}
}

View File

@ -1,5 +1,6 @@
package cc.maxmc.blastingcrisis.command
import cc.maxmc.blastingcrisis.factory.BEntityFactory
import cc.maxmc.blastingcrisis.game.Game
import cc.maxmc.blastingcrisis.game.GameOreGenerator
import cc.maxmc.blastingcrisis.game.GameState
@ -35,25 +36,33 @@ object DebugCommand {
"testTeam1",
ChatColor.AQUA,
sender.location,
sender.location,
sender.eyeLocation,
homeArea,
area1,
area2,
listOf(area3),
area2,
sender.eyeLocation
listOf(area3),
)
val map = GameMap(
"test", listOf(
"test",
homeArea,
listOf(
team
), 4
)
game = Game(map)
}
literal("join") {
execute<Player> { sender, _, _ ->
game.join(sender)
}
}
literal("state") {
dynamic {
execute<Player> { sender, ctx, argument ->
execute<Player> { sender, _, argument ->
if (!GameState.values().map { it.name }
.contains(argument.uppercase())) return@execute sender.sendMessage("NO STATE")
val state = GameState.valueOf(argument.uppercase())
@ -67,14 +76,14 @@ object DebugCommand {
}
literal("timer") {
execute<Player> { sender, ctx, argument ->
execute<Player> { _, _, _ ->
game.timer.beginCountdown()
}
}
}
literal("scoreboard") {
execute { sender, context, argument ->
execute { sender, _, _ ->
game.scoreboard.sendScoreboardPlayer(sender)
}
}
@ -95,42 +104,42 @@ object DebugCommand {
literal("spawnEntity") {
execute<Player> { sender, _, _ ->
val loc = sender.location
villager = BEntityVillager(loc)
villager = BEntityFactory.createVillager(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("rename") {
dynamic {
execute<Player> { _, _, arg ->
villager.name = { it.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, _, _ ->
@ -142,7 +151,7 @@ object DebugCommand {
val job = gen.enable(area)
Bukkit.getScheduler().runTaskLater(BukkitPlugin.getInstance(), {
job.cancel("Stop by hand")
} ,60 * 20)
}, 60 * 20)
}
}
}

View File

@ -0,0 +1,13 @@
package cc.maxmc.blastingcrisis.configuration
import taboolib.module.configuration.Config
import taboolib.module.configuration.Configuration
object GlobalSettings {
@Config("settings.yml")
lateinit var settings: Configuration
val timeToStart
get() = settings.getInt("time-to-start")
}

View File

@ -3,10 +3,8 @@ 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
@ -16,7 +14,7 @@ object DListener {
// val source = it.packet.source
if (!it.packet.name.lowercase().contains("entity")) return@runTask
// println(it.packet.name)
if( it.packet.source !is PacketPlayOutSpawnEntityLiving ) {
if (it.packet.source !is PacketPlayOutSpawnEntityLiving) {
return@runTask
}
// it.packet.source.getProperty<Int>("a")!!.broadcast()

View File

@ -0,0 +1,19 @@
package cc.maxmc.blastingcrisis.factory
import cc.maxmc.blastingcrisis.misc.PacketEntityManager
import cc.maxmc.blastingcrisis.misc.debug
import cc.maxmc.blastingcrisis.packet.BEntityVillager
import org.bukkit.Location
object BEntityFactory {
fun createVillager(loc: Location): BEntityVillager {
val entity = BEntityVillager(loc)
PacketEntityManager.register(entity.entityID, entity)
entity.handleInteract {
debug("Interacting with entity: ${entity.entityID}")
debug("type: ${it.type}")
it.vector?.let { vec -> debug("vector: $vec") }
}
return entity
}
}

View File

@ -33,21 +33,29 @@ class Game(
fun leave(player: Player) {
if (state == GameState.WAITING || state == GameState.COUNTING_DOWN) {
players.remove(player)
player.team?.removePlayer(player)
broadcast { it.sendLang("game_leave", player.name) }
checkStart()
}
val team =
player.team ?: throw IllegalStateException("Player ${player.name} has no team, which shouldn't happen.")
team.checkTeamAlive()
team.players.remove(player)
team.removePlayer(player)
checkEnd()
}
fun start() {
debug("game ${map.name} started.")
timer.startTimer()
}
private fun checkStart() {
if (players.size < map.maxPlayer * 0.5 && state == GameState.COUNTING_DOWN) {
state = GameState.WAITING
timer.resetCountdown()
}
if (players.size > map.maxPlayer * 0.75) {
state = GameState.COUNTING_DOWN
timer.beginCountdown()

View File

@ -3,7 +3,7 @@ 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 cc.maxmc.blastingcrisis.misc.pluginScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@ -16,6 +16,7 @@ import taboolib.platform.BukkitPlugin
class GameOreGenerator(config: Configuration) {
private val rate: Int = config.getInt("rate")
private val maxTry: Int = config.getInt("try")
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)
@ -24,7 +25,7 @@ class GameOreGenerator(config: Configuration) {
private val random = WeightRandom(ores.map { it.key to it.value })
fun enable(area: Area): Job {
return pluginScore.launch {
return pluginScope.launch {
while (true) {
generate(area)
delay(rate * 50L)
@ -32,7 +33,8 @@ class GameOreGenerator(config: Configuration) {
}
}
private tailrec fun generate(area: Area) {
private tailrec fun generate(area: Area, times: Int = 0) {
if (times > maxTry) return
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)
@ -45,7 +47,7 @@ class GameOreGenerator(config: Configuration) {
debug("generating ${block.type} at ($x, $y, $z)")
}
} else {
return generate(area)
return generate(area, times + 1)
}
}
}

View File

@ -6,4 +6,6 @@ enum class GameState {
START,
WALL_FALL,
FINISH;
fun isStarted(): Boolean = this == START || this == WALL_FALL
}

View File

@ -2,17 +2,35 @@ package cc.maxmc.blastingcrisis.game
import cc.maxmc.blastingcrisis.map.MapTeam
import org.bukkit.entity.Player
import taboolib.platform.util.sendLang
class GameTeam(val game: Game, val team: MapTeam) {
class GameTeam(val game: Game, val teamInfo: MapTeam) {
val players = ArrayList<Player>()
val villager = TeamVillager(this)
var teamSurvive = true
private set
fun join(player: Player) {
players += player
if (game.state == GameState.COUNTING_DOWN || game.state == GameState.WAITING) {
player.sendLang("team_join", teamInfo.name)
}
}
fun checkTeamAlive() {
fun removePlayer(player: Player) {
players -= player
if (game.state.isStarted()) {
checkTeamAlive()
}
}
private fun checkTeamAlive() {
if (players.size != 0) return
game.broadcast {
it.sendLang("team_death", teamInfo.name)
}
teamSurvive = false
}
}

View File

@ -1,22 +1,21 @@
package cc.maxmc.blastingcrisis.game
import cc.maxmc.blastingcrisis.misc.pluginScore
import cc.maxmc.blastingcrisis.configuration.GlobalSettings
import cc.maxmc.blastingcrisis.misc.pluginScope
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
var current = GlobalSettings.timeToStart
fun beginCountdown() {
countdownJob = pluginScore.launch {
countdownJob = pluginScope.launch {
while (current > 0) {
if (!isActive) {
return@launch
@ -43,7 +42,7 @@ class GameTimer(private val game: Game) {
game.broadcast {
it.sendLang("time_reset")
}
current = timeToStart
current = GlobalSettings.timeToStart
}
fun startTimer() {

View File

@ -0,0 +1,38 @@
package cc.maxmc.blastingcrisis.game
import cc.maxmc.blastingcrisis.factory.BEntityFactory
import org.bukkit.Bukkit
import taboolib.platform.BukkitPlugin
import taboolib.platform.util.asLangText
class TeamVillager(private val team: GameTeam) {
private val packetVillager = BEntityFactory.createVillager(team.teamInfo.villager)
private var health: Int = 150
init {
packetVillager.name = {
if (it.team == team) {
it.asLangText("team_villager_name_internal")
} else {
it.asLangText("team_villager_name_outer")
}
}
}
fun spawn() {
team.game.broadcast(packetVillager::addViewer)
}
fun damage() {
health -= 10
if (health <= 0) {
packetVillager.dieAnimate()
Bukkit.getScheduler().runTaskLater(BukkitPlugin.getInstance(), {
packetVillager.destroy()
}, 20 * 3)
}
packetVillager.hurtAnimate()
}
}

View File

@ -0,0 +1,30 @@
package cc.maxmc.blastingcrisis.listener
import cc.maxmc.blastingcrisis.misc.PacketEntityManager
import net.minecraft.server.v1_8_R3.PacketPlayInUseEntity
import org.bukkit.util.Vector
import taboolib.common.platform.event.SubscribeEvent
import taboolib.library.reflex.Reflex.Companion.getProperty
import taboolib.module.nms.PacketReceiveEvent
object PacketEntityListener {
@SubscribeEvent
fun onClick(e: PacketReceiveEvent) {
if (e.packet.name != "PacketPlayInUseEntity") return
val packet = e.packet.source as PacketPlayInUseEntity
val id = packet.getProperty<Int>("a")!!
val bEntity = PacketEntityManager.getEntity(id) ?: return
val direction = packet.b()
bEntity.onInteract(BEntityInteract(map(packet.a()), direction?.let { Vector(it.a, it.b, it.c) }))
e.isCancelled = true
}
private fun map(action: PacketPlayInUseEntity.EnumEntityUseAction) = EntityUseAction.values()[action.ordinal]
enum class EntityUseAction {
INTERACT, ATTACK, INTERACT_AT;
}
}
data class BEntityInteract(val type: PacketEntityListener.EntityUseAction, val vector: Vector?)

View File

@ -1,6 +1,8 @@
package cc.maxmc.blastingcrisis.map
class GameMap(val name: String, val teams: List<MapTeam>, val maxPlayersPerTeam: Int) {
import cc.maxmc.blastingcrisis.misc.Area
class GameMap(val name: String, val area: Area, val teams: List<MapTeam>, val maxPlayersPerTeam: Int) {
val maxPlayer: Int
get() = maxPlayersPerTeam * teams.size
}

View File

@ -8,10 +8,11 @@ data class MapTeam(
val name: String,
val color: ChatColor,
val spawn: Location,
val villager: Location,
val upgrade: Location,
val home: Area,
val wall: Area,
val mine: Area,
val sides: List<Area>,
val teleport: Area,
val upgrade: Location,
val sides: List<Area>,
)

View File

@ -53,9 +53,9 @@ class Area(loc1: Location, loc2: Location) : ConfigurationSerializable {
}
/**
* get all locations of the non air blocks in this area
* get all locations of the non-air blocks in this area
*
* @return all locations of the non air blocks in this area
* @return all locations of the non-air blocks in this area
*/
fun forBlocksWithoutAir(): List<Location> {
return forBlocksInArea().filter { it.block.type != Material.AIR }

View File

@ -4,6 +4,6 @@ import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
val pluginScore = CoroutineScope(SupervisorJob() + CoroutineExceptionHandler { job, exception ->
val pluginScope = CoroutineScope(SupervisorJob() + CoroutineExceptionHandler { job, exception ->
IllegalStateException("在执行 Job $job 时出线异常", exception).printStackTrace()
})

View File

@ -0,0 +1,14 @@
package cc.maxmc.blastingcrisis.misc
import cc.maxmc.blastingcrisis.packet.BEntity
object PacketEntityManager {
private val entities = HashMap<Int, BEntity>()
fun register(id: Int, entity: BEntity) {
entities[id] = entity
}
fun getEntity(id: Int): BEntity? = entities[id]
}

View File

@ -1,5 +1,6 @@
package cc.maxmc.blastingcrisis.packet
import cc.maxmc.blastingcrisis.listener.BEntityInteract
import gnu.trove.TDecorators
import gnu.trove.map.hash.TIntObjectHashMap
import net.minecraft.server.v1_8_R3.*
@ -36,6 +37,7 @@ abstract class BEntity(var loc: Location, val entityType: Int) {
}
val entityID: Int = getEID()
val viewers = ArrayList<Player>()
val interactHandlers = ArrayList<(BEntityInteract) -> Unit>()
abstract fun DataWatcher.initEntity()
@ -69,14 +71,26 @@ abstract class BEntity(var loc: Location, val entityType: Int) {
viewers.remove(viewer)
}
fun update() {
val packet = PacketPlayOutEntityMetadata(entityID, dataWatcher, true)
viewers.forEach { it.sendPacket(packet) }
fun destroy() {
ArrayList(viewers).forEach { removeViewer(it) }
}
fun updateData(toDo: DataWatcher.() -> Unit) {
toDo(dataWatcher)
update()
fun update() {
viewers.forEach {
updateFor(it)
}
}
fun updateFor(player: Player) {
val packet = PacketPlayOutEntityMetadata(entityID, dataWatcher, true)
player.sendPacket(packet)
}
fun updateData(toDo: DataWatcher.(Player) -> Unit) {
viewers.forEach {
toDo(dataWatcher, it)
updateFor(it)
}
}
fun hurtAnimate() {
@ -93,6 +107,14 @@ abstract class BEntity(var loc: Location, val entityType: Int) {
}.let { viewers.forEach { p -> p.sendPacket(it) } }
}
fun handleInteract(handler: (BEntityInteract) -> Unit) {
interactHandlers.add(handler)
}
fun onInteract(interact: BEntityInteract) {
interactHandlers.forEach { it(interact) }
}
companion object {
private val incrementEntityID = AtomicInteger(197827 + random(0, 549))

View File

@ -10,10 +10,11 @@ import taboolib.module.nms.sendPacket
import kotlin.experimental.or
class BEntityNameTag(loc: Location) : BEntity(loc, 78) {
var name: String = ""
var name: (Player) -> String = { "" }
set(name) {
field = name
updateData {
getWatchableObject(2).a(name)
getWatchableObject(2).a(name(it))
}
}
@ -40,7 +41,7 @@ class BEntityNameTag(loc: Location) : BEntity(loc, 78) {
}
player.sendPacket(packet)
update()
updateFor(player)
}
}

View File

@ -7,7 +7,7 @@ import org.bukkit.entity.Player
import taboolib.library.reflex.Reflex.Companion.setProperty
class BEntityVillager(loc: Location) : BEntity(loc, 120) {
var name: String
var name: (Player) -> String
get() = nameTag.name
set(name) {
nameTag.name = name

View File

@ -5,4 +5,3 @@ 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

@ -1,3 +1,4 @@
## part scoreboard
scoreboard_waiting: |-
§6Blasting§7Crisis
@ -14,6 +15,10 @@ scoreboard_counting_down: |-
§a| §7游戏还有 §a{2} §7秒开始.
§6Play.MaxMC.cc
## end part scoreboard
## part game
game_countdown_15:
- "§e| §7游戏还有 §e15 §7秒开始."
game_countdown_10:
@ -40,4 +45,13 @@ game_countdown_reset:
- type: title
text: "§c人数不足"
- "§c| §7由于人数不足, 无法开始游戏."
game_join: "§a| §7玩家 §a{0} §7加入了游戏"
game_join: "§a| §7玩家 §a{0} §7加入了游戏."
game_leave: "§e| §7玩家 §e{0} §7离开了游戏."
## end part game
## part team
team_join: "§a| §7你加入了 §a{0}"
team_death: "§e| §7队伍 §e{0} §7已淘汰."
team_villager_name_internal: "本队村民 {0}"
team_villager_name_outer: "{0}队村民 {1}"
## end part team

View File

@ -0,0 +1,4 @@
# Time before game start
# Unit: second
time-to-start: 15